summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/assert.h5
-rw-r--r--src/common/bit_set.h244
-rw-r--r--src/common/math_util.h16
-rw-r--r--src/common/string_util.cpp9
-rw-r--r--src/common/string_util.h8
-rw-r--r--src/common/thread.cpp35
-rw-r--r--src/common/thread.h20
-rw-r--r--src/common/x64/xbyak_abi.h222
-rw-r--r--src/common/x64/xbyak_util.h47
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/core.cpp136
-rw-r--r--src/core/core.h5
-rw-r--r--src/core/cpu_core_manager.cpp142
-rw-r--r--src/core/cpu_core_manager.h59
-rw-r--r--src/core/file_sys/card_image.h13
-rw-r--r--src/core/file_sys/patch_manager.cpp49
-rw-r--r--src/core/frontend/applets/software_keyboard.cpp29
-rw-r--r--src/core/frontend/applets/software_keyboard.h54
-rw-r--r--src/core/frontend/input.h7
-rw-r--r--src/core/gdbstub/gdbstub.cpp123
-rw-r--r--src/core/hle/kernel/handle_table.cpp11
-rw-r--r--src/core/hle/kernel/handle_table.h15
-rw-r--r--src/core/hle/kernel/kernel.cpp79
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/process.cpp7
-rw-r--r--src/core/hle/kernel/process.h4
-rw-r--r--src/core/hle/kernel/resource_limit.cpp75
-rw-r--r--src/core/hle/kernel/resource_limit.h98
-rw-r--r--src/core/hle/kernel/shared_memory.cpp22
-rw-r--r--src/core/hle/kernel/shared_memory.h48
-rw-r--r--src/core/hle/kernel/svc.cpp56
-rw-r--r--src/core/hle/kernel/vm_manager.cpp6
-rw-r--r--src/core/hle/kernel/vm_manager.h3
-rw-r--r--src/core/hle/service/am/am.cpp370
-rw-r--r--src/core/hle/service/am/am.h30
-rw-r--r--src/core/hle/service/am/applets/applets.cpp113
-rw-r--r--src/core/hle/service/am/applets/applets.h112
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp161
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h74
-rw-r--r--src/core/hle/service/am/applets/stub_applet.cpp70
-rw-r--r--src/core/hle/service/am/applets/stub_applet.h24
-rw-r--r--src/core/hle/service/audio/audout_u.cpp28
-rw-r--r--src/core/hle/service/audio/audout_u.h3
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp5
-rw-r--r--src/core/hle/service/filesystem/filesystem.h1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h41
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp20
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h7
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp25
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h9
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp464
-rw-r--r--src/core/hle/service/hid/controllers/npad.h45
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h12
-rw-r--r--src/core/hle/service/hid/hid.cpp4
-rw-r--r--src/core/hle/service/ldr/ldr.cpp380
-rw-r--r--src/core/hle/service/lm/lm.cpp13
-rw-r--r--src/core/hle/service/ns/pl_u.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h6
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp18
-rw-r--r--src/core/hle/service/nvdrv/interface.h2
-rw-r--r--src/core/hle/service/sm/sm.cpp54
-rw-r--r--src/core/hle/service/sm/sm.h3
-rw-r--r--src/core/hle/service/time/interface.cpp3
-rw-r--r--src/core/hle/service/time/time.cpp15
-rw-r--r--src/core/hle/service/time/time.h1
-rw-r--r--src/core/hle/service/vi/vi.cpp54
-rw-r--r--src/core/loader/nro.cpp7
-rw-r--r--src/core/settings.cpp50
-rw-r--r--src/core/settings.h319
-rw-r--r--src/video_core/CMakeLists.txt3
-rw-r--r--src/video_core/engines/maxwell_3d.cpp16
-rw-r--r--src/video_core/engines/maxwell_3d.h192
-rw-r--r--src/video_core/engines/shader_bytecode.h27
-rw-r--r--src/video_core/engines/shader_header.h11
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/macro_interpreter.cpp29
-rw-r--r--src/video_core/macro_interpreter.h4
-rw-r--r--src/video_core/memory_manager.cpp7
-rw-r--r--src/video_core/memory_manager.h3
-rw-r--r--src/video_core/morton.cpp353
-rw-r--r--src/video_core/morton.h21
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp216
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h38
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp243
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp1206
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h10
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp234
-rw-r--r--src/video_core/renderer_opengl/gl_state.h51
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h11
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp10
-rw-r--r--src/video_core/textures/decoders.cpp85
-rw-r--r--src/video_core/textures/decoders.h7
-rw-r--r--src/video_core/textures/texture.h15
-rw-r--r--src/video_core/utils.h164
-rw-r--r--src/yuzu/CMakeLists.txt11
-rw-r--r--src/yuzu/applets/software_keyboard.cpp152
-rw-r--r--src/yuzu/applets/software_keyboard.h79
-rw-r--r--src/yuzu/bootmanager.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp387
-rw-r--r--src/yuzu/configuration/config.h14
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui10
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp14
-rw-r--r--src/yuzu/configuration/configure_gamelist.h2
-rw-r--r--src/yuzu/configuration/configure_general.cpp39
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_general.ui9
-rw-r--r--src/yuzu/configuration/configure_graphics.ui94
-rw-r--r--src/yuzu/configuration/configure_input.cpp436
-rw-r--r--src/yuzu/configuration/configure_input.h52
-rw-r--r--src/yuzu/configuration/configure_input.ui1064
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp508
-rw-r--r--src/yuzu/configuration/configure_input_player.h103
-rw-r--r--src/yuzu/configuration/configure_input_player.ui1164
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp213
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h68
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui261
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.cpp42
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.h32
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.ui199
-rw-r--r--src/yuzu/main.cpp57
-rw-r--r--src/yuzu/main.h11
-rw-r--r--src/yuzu/ui_settings.h2
-rw-r--r--src/yuzu_cmd/config.cpp289
-rw-r--r--src/yuzu_cmd/default_ini.h2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp16
136 files changed, 8878 insertions, 3734 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index eccd8f64a..a5e71d879 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -44,7 +44,6 @@ add_library(common STATIC
44 detached_tasks.cpp 44 detached_tasks.cpp
45 detached_tasks.h 45 detached_tasks.h
46 bit_field.h 46 bit_field.h
47 bit_set.h
48 cityhash.cpp 47 cityhash.cpp
49 cityhash.h 48 cityhash.h
50 color.h 49 color.h
@@ -95,14 +94,9 @@ if(ARCHITECTURE_x86_64)
95 PRIVATE 94 PRIVATE
96 x64/cpu_detect.cpp 95 x64/cpu_detect.cpp
97 x64/cpu_detect.h 96 x64/cpu_detect.h
98 x64/xbyak_abi.h
99 x64/xbyak_util.h
100 ) 97 )
101endif() 98endif()
102 99
103create_target_directory_groups(common) 100create_target_directory_groups(common)
104 101
105target_link_libraries(common PUBLIC Boost::boost fmt microprofile) 102target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
106if (ARCHITECTURE_x86_64)
107 target_link_libraries(common PRIVATE xbyak)
108endif()
diff --git a/src/common/assert.h b/src/common/assert.h
index 0d4eddc19..6002f7ab1 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -52,5 +52,8 @@ __declspec(noinline, noreturn)
52#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) 52#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
53#endif 53#endif
54 54
55#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!") 55#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!")
56#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__) 56#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
57
58#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!")
59#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__)
diff --git a/src/common/bit_set.h b/src/common/bit_set.h
deleted file mode 100644
index 5cd1352b2..000000000
--- a/src/common/bit_set.h
+++ /dev/null
@@ -1,244 +0,0 @@
1// This file is under the public domain.
2
3#pragma once
4
5#include <cstddef>
6#ifdef _WIN32
7#include <intrin.h>
8#endif
9#include <initializer_list>
10#include <new>
11#include <type_traits>
12#include "common/common_types.h"
13
14// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
15namespace Common {
16
17// Helper functions:
18
19#ifdef _MSC_VER
20template <typename T>
21static inline int CountSetBits(T v) {
22 // from https://graphics.stanford.edu/~seander/bithacks.html
23 // GCC has this built in, but MSVC's intrinsic will only emit the actual
24 // POPCNT instruction, which we're not depending on
25 v = v - ((v >> 1) & (T) ~(T)0 / 3);
26 v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
27 v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
28 return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
29}
30static inline int LeastSignificantSetBit(u8 val) {
31 unsigned long index;
32 _BitScanForward(&index, val);
33 return (int)index;
34}
35static inline int LeastSignificantSetBit(u16 val) {
36 unsigned long index;
37 _BitScanForward(&index, val);
38 return (int)index;
39}
40static inline int LeastSignificantSetBit(u32 val) {
41 unsigned long index;
42 _BitScanForward(&index, val);
43 return (int)index;
44}
45static inline int LeastSignificantSetBit(u64 val) {
46 unsigned long index;
47 _BitScanForward64(&index, val);
48 return (int)index;
49}
50#else
51static inline int CountSetBits(u8 val) {
52 return __builtin_popcount(val);
53}
54static inline int CountSetBits(u16 val) {
55 return __builtin_popcount(val);
56}
57static inline int CountSetBits(u32 val) {
58 return __builtin_popcount(val);
59}
60static inline int CountSetBits(u64 val) {
61 return __builtin_popcountll(val);
62}
63static inline int LeastSignificantSetBit(u8 val) {
64 return __builtin_ctz(val);
65}
66static inline int LeastSignificantSetBit(u16 val) {
67 return __builtin_ctz(val);
68}
69static inline int LeastSignificantSetBit(u32 val) {
70 return __builtin_ctz(val);
71}
72static inline int LeastSignificantSetBit(u64 val) {
73 return __builtin_ctzll(val);
74}
75#endif
76
77// Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
78// using the set bits of an integer to represent a set of integers. Like that
79// class, it acts like an array of bools:
80// BitSet32 bs;
81// bs[1] = true;
82// but also like the underlying integer ([0] = least significant bit):
83// BitSet32 bs2 = ...;
84// bs = (bs ^ bs2) & BitSet32(0xffff);
85// The following additional functionality is provided:
86// - Construction using an initializer list.
87// BitSet bs { 1, 2, 4, 8 };
88// - Efficiently iterating through the set bits:
89// for (int i : bs)
90// [i is the *index* of a set bit]
91// (This uses the appropriate CPU instruction to find the next set bit in one
92// operation.)
93// - Counting set bits using .Count() - see comment on that method.
94
95// TODO: use constexpr when MSVC gets out of the Dark Ages
96
97template <typename IntTy>
98class BitSet {
99 static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
100
101public:
102 // A reference to a particular bit, returned from operator[].
103 class Ref {
104 public:
105 Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
106 Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
107 operator bool() const {
108 return (m_bs->m_val & m_mask) != 0;
109 }
110 bool operator=(bool set) {
111 m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
112 return set;
113 }
114
115 private:
116 BitSet* m_bs;
117 IntTy m_mask;
118 };
119
120 // A STL-like iterator is required to be able to use range-based for loops.
121 class Iterator {
122 public:
123 Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
124 Iterator(IntTy val) : m_val(val), m_bit(0) {}
125 Iterator& operator=(Iterator other) {
126 new (this) Iterator(other);
127 return *this;
128 }
129 int operator*() {
130 return m_bit + ComputeLsb();
131 }
132 Iterator& operator++() {
133 int lsb = ComputeLsb();
134 m_val >>= lsb + 1;
135 m_bit += lsb + 1;
136 m_has_lsb = false;
137 return *this;
138 }
139 Iterator operator++(int _) {
140 Iterator other(*this);
141 ++*this;
142 return other;
143 }
144 bool operator==(Iterator other) const {
145 return m_val == other.m_val;
146 }
147 bool operator!=(Iterator other) const {
148 return m_val != other.m_val;
149 }
150
151 private:
152 int ComputeLsb() {
153 if (!m_has_lsb) {
154 m_lsb = LeastSignificantSetBit(m_val);
155 m_has_lsb = true;
156 }
157 return m_lsb;
158 }
159 IntTy m_val;
160 int m_bit;
161 int m_lsb = -1;
162 bool m_has_lsb = false;
163 };
164
165 BitSet() : m_val(0) {}
166 explicit BitSet(IntTy val) : m_val(val) {}
167 BitSet(std::initializer_list<int> init) {
168 m_val = 0;
169 for (int bit : init)
170 m_val |= (IntTy)1 << bit;
171 }
172
173 static BitSet AllTrue(std::size_t count) {
174 return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
175 }
176
177 Ref operator[](std::size_t bit) {
178 return Ref(this, (IntTy)1 << bit);
179 }
180 const Ref operator[](std::size_t bit) const {
181 return (*const_cast<BitSet*>(this))[bit];
182 }
183 bool operator==(BitSet other) const {
184 return m_val == other.m_val;
185 }
186 bool operator!=(BitSet other) const {
187 return m_val != other.m_val;
188 }
189 bool operator<(BitSet other) const {
190 return m_val < other.m_val;
191 }
192 bool operator>(BitSet other) const {
193 return m_val > other.m_val;
194 }
195 BitSet operator|(BitSet other) const {
196 return BitSet(m_val | other.m_val);
197 }
198 BitSet operator&(BitSet other) const {
199 return BitSet(m_val & other.m_val);
200 }
201 BitSet operator^(BitSet other) const {
202 return BitSet(m_val ^ other.m_val);
203 }
204 BitSet operator~() const {
205 return BitSet(~m_val);
206 }
207 BitSet& operator|=(BitSet other) {
208 return *this = *this | other;
209 }
210 BitSet& operator&=(BitSet other) {
211 return *this = *this & other;
212 }
213 BitSet& operator^=(BitSet other) {
214 return *this = *this ^ other;
215 }
216 operator u32() = delete;
217 operator bool() {
218 return m_val != 0;
219 }
220
221 // Warning: Even though on modern CPUs this is a single fast instruction,
222 // Dolphin's official builds do not currently assume POPCNT support on x86,
223 // so slower explicit bit twiddling is generated. Still should generally
224 // be faster than a loop.
225 unsigned int Count() const {
226 return CountSetBits(m_val);
227 }
228
229 Iterator begin() const {
230 return Iterator(m_val);
231 }
232 Iterator end() const {
233 return Iterator(0);
234 }
235
236 IntTy m_val;
237};
238
239} // namespace Common
240
241typedef Common::BitSet<u8> BitSet8;
242typedef Common::BitSet<u16> BitSet16;
243typedef Common::BitSet<u32> BitSet32;
244typedef Common::BitSet<u64> BitSet64;
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 343cdd902..94b4394c5 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -4,18 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
8#include <cstdlib> 7#include <cstdlib>
9#include <type_traits> 8#include <type_traits>
10 9
11namespace MathUtil { 10namespace MathUtil {
12 11
13static constexpr float PI = 3.14159265f; 12constexpr float PI = 3.14159265f;
14
15inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1,
16 unsigned length1) {
17 return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1));
18}
19 13
20template <class T> 14template <class T>
21struct Rectangle { 15struct Rectangle {
@@ -24,16 +18,16 @@ struct Rectangle {
24 T right{}; 18 T right{};
25 T bottom{}; 19 T bottom{};
26 20
27 Rectangle() = default; 21 constexpr Rectangle() = default;
28 22
29 Rectangle(T left, T top, T right, T bottom) 23 constexpr Rectangle(T left, T top, T right, T bottom)
30 : left(left), top(top), right(right), bottom(bottom) {} 24 : left(left), top(top), right(right), bottom(bottom) {}
31 25
32 T GetWidth() const { 26 T GetWidth() const {
33 return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); 27 return std::abs(static_cast<std::make_signed_t<T>>(right - left));
34 } 28 }
35 T GetHeight() const { 29 T GetHeight() const {
36 return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); 30 return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
37 } 31 }
38 Rectangle<T> TranslateX(const T x) const { 32 Rectangle<T> TranslateX(const T x) const {
39 return Rectangle{left + x, top, right + x, bottom}; 33 return Rectangle{left + x, top, right + x, bottom};
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 14f7037d8..959f278aa 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -214,6 +214,15 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
214 return std::string(buffer, len); 214 return std::string(buffer, len);
215} 215}
216 216
217std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
218 std::size_t max_len) {
219 std::size_t len = 0;
220 while (len < max_len && buffer[len] != '\0')
221 ++len;
222
223 return std::u16string(buffer.begin(), buffer.begin() + len);
224}
225
217const char* TrimSourcePath(const char* path, const char* root) { 226const char* TrimSourcePath(const char* path, const char* root) {
218 const char* p = path; 227 const char* p = path;
219 228
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 08f96533b..583fd05e6 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -67,6 +67,14 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
67std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); 67std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
68 68
69/** 69/**
70 * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
71 * null-terminated, then the string ends at the greatest multiple of two less then or equal to
72 * max_len_bytes.
73 */
74std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
75 std::size_t max_len);
76
77/**
70 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's 78 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
71 * intended to be used to strip a system-specific build directory from the `__FILE__` macro, 79 * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
72 * leaving only the path relative to the sources root. 80 * leaving only the path relative to the sources root.
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 9e207118f..5144c0d9f 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -25,23 +25,6 @@
25 25
26namespace Common { 26namespace Common {
27 27
28int CurrentThreadId() {
29#ifdef _MSC_VER
30 return GetCurrentThreadId();
31#elif defined __APPLE__
32 return mach_thread_self();
33#else
34 return 0;
35#endif
36}
37
38#ifdef _WIN32
39// Supporting functions
40void SleepCurrentThread(int ms) {
41 Sleep(ms);
42}
43#endif
44
45#ifdef _MSC_VER 28#ifdef _MSC_VER
46 29
47void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { 30void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
@@ -62,7 +45,7 @@ void SwitchCurrentThread() {
62 45
63// This is implemented much nicer in upcoming msvc++, see: 46// This is implemented much nicer in upcoming msvc++, see:
64// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx 47// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
65void SetCurrentThreadName(const char* szThreadName) { 48void SetCurrentThreadName(const char* name) {
66 static const DWORD MS_VC_EXCEPTION = 0x406D1388; 49 static const DWORD MS_VC_EXCEPTION = 0x406D1388;
67 50
68#pragma pack(push, 8) 51#pragma pack(push, 8)
@@ -75,7 +58,7 @@ void SetCurrentThreadName(const char* szThreadName) {
75#pragma pack(pop) 58#pragma pack(pop)
76 59
77 info.dwType = 0x1000; 60 info.dwType = 0x1000;
78 info.szName = szThreadName; 61 info.szName = name;
79 info.dwThreadID = -1; // dwThreadID; 62 info.dwThreadID = -1; // dwThreadID;
80 info.dwFlags = 0; 63 info.dwFlags = 0;
81 64
@@ -107,10 +90,6 @@ void SetCurrentThreadAffinity(u32 mask) {
107} 90}
108 91
109#ifndef _WIN32 92#ifndef _WIN32
110void SleepCurrentThread(int ms) {
111 usleep(1000 * ms);
112}
113
114void SwitchCurrentThread() { 93void SwitchCurrentThread() {
115 usleep(1000 * 1); 94 usleep(1000 * 1);
116} 95}
@@ -118,15 +97,15 @@ void SwitchCurrentThread() {
118 97
119// MinGW with the POSIX threading model does not support pthread_setname_np 98// MinGW with the POSIX threading model does not support pthread_setname_np
120#if !defined(_WIN32) || defined(_MSC_VER) 99#if !defined(_WIN32) || defined(_MSC_VER)
121void SetCurrentThreadName(const char* szThreadName) { 100void SetCurrentThreadName(const char* name) {
122#ifdef __APPLE__ 101#ifdef __APPLE__
123 pthread_setname_np(szThreadName); 102 pthread_setname_np(name);
124#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) 103#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
125 pthread_set_name_np(pthread_self(), szThreadName); 104 pthread_set_name_np(pthread_self(), name);
126#elif defined(__NetBSD__) 105#elif defined(__NetBSD__)
127 pthread_setname_np(pthread_self(), "%s", (void*)szThreadName); 106 pthread_setname_np(pthread_self(), "%s", (void*)name);
128#else 107#else
129 pthread_setname_np(pthread_self(), szThreadName); 108 pthread_setname_np(pthread_self(), name);
130#endif 109#endif
131} 110}
132#endif 111#endif
diff --git a/src/common/thread.h b/src/common/thread.h
index 6cbdb96a3..2cf74452d 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -13,15 +13,8 @@
13 13
14namespace Common { 14namespace Common {
15 15
16int CurrentThreadId();
17
18void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
19void SetCurrentThreadAffinity(u32 mask);
20
21class Event { 16class Event {
22public: 17public:
23 Event() : is_set(false) {}
24
25 void Set() { 18 void Set() {
26 std::lock_guard<std::mutex> lk(mutex); 19 std::lock_guard<std::mutex> lk(mutex);
27 if (!is_set) { 20 if (!is_set) {
@@ -53,14 +46,14 @@ public:
53 } 46 }
54 47
55private: 48private:
56 bool is_set; 49 bool is_set = false;
57 std::condition_variable condvar; 50 std::condition_variable condvar;
58 std::mutex mutex; 51 std::mutex mutex;
59}; 52};
60 53
61class Barrier { 54class Barrier {
62public: 55public:
63 explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {} 56 explicit Barrier(std::size_t count_) : count(count_) {}
64 57
65 /// Blocks until all "count" threads have called Sync() 58 /// Blocks until all "count" threads have called Sync()
66 void Sync() { 59 void Sync() {
@@ -80,12 +73,13 @@ public:
80private: 73private:
81 std::condition_variable condvar; 74 std::condition_variable condvar;
82 std::mutex mutex; 75 std::mutex mutex;
83 const std::size_t count; 76 std::size_t count;
84 std::size_t waiting; 77 std::size_t waiting = 0;
85 std::size_t generation; // Incremented once each time the barrier is used 78 std::size_t generation = 0; // Incremented once each time the barrier is used
86}; 79};
87 80
88void SleepCurrentThread(int ms); 81void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
82void SetCurrentThreadAffinity(u32 mask);
89void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms 83void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
90void SetCurrentThreadName(const char* name); 84void SetCurrentThreadName(const char* name);
91 85
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
deleted file mode 100644
index 636a5c0f9..000000000
--- a/src/common/x64/xbyak_abi.h
+++ /dev/null
@@ -1,222 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <initializer_list>
8#include <xbyak.h>
9#include "common/assert.h"
10#include "common/bit_set.h"
11
12namespace Common::X64 {
13
14inline int RegToIndex(const Xbyak::Reg& reg) {
15 using Kind = Xbyak::Reg::Kind;
16 ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
17 "RegSet only support GPRs and XMM registers.");
18 ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
19 return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
20}
21
22inline Xbyak::Reg64 IndexToReg64(int reg_index) {
23 ASSERT(reg_index < 16);
24 return Xbyak::Reg64(reg_index);
25}
26
27inline Xbyak::Xmm IndexToXmm(int reg_index) {
28 ASSERT(reg_index >= 16 && reg_index < 32);
29 return Xbyak::Xmm(reg_index - 16);
30}
31
32inline Xbyak::Reg IndexToReg(int reg_index) {
33 if (reg_index < 16) {
34 return IndexToReg64(reg_index);
35 } else {
36 return IndexToXmm(reg_index);
37 }
38}
39
40inline BitSet32 BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
41 BitSet32 bits;
42 for (const Xbyak::Reg& reg : regs) {
43 bits[RegToIndex(reg)] = true;
44 }
45 return bits;
46}
47
48const BitSet32 ABI_ALL_GPRS(0x0000FFFF);
49const BitSet32 ABI_ALL_XMMS(0xFFFF0000);
50
51#ifdef _WIN32
52
53// Microsoft x64 ABI
54const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
55const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
56const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
57const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
58const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
59
60const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
61 // GPRs
62 Xbyak::util::rcx,
63 Xbyak::util::rdx,
64 Xbyak::util::r8,
65 Xbyak::util::r9,
66 Xbyak::util::r10,
67 Xbyak::util::r11,
68 // XMMs
69 Xbyak::util::xmm0,
70 Xbyak::util::xmm1,
71 Xbyak::util::xmm2,
72 Xbyak::util::xmm3,
73 Xbyak::util::xmm4,
74 Xbyak::util::xmm5,
75});
76
77const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
78 // GPRs
79 Xbyak::util::rbx,
80 Xbyak::util::rsi,
81 Xbyak::util::rdi,
82 Xbyak::util::rbp,
83 Xbyak::util::r12,
84 Xbyak::util::r13,
85 Xbyak::util::r14,
86 Xbyak::util::r15,
87 // XMMs
88 Xbyak::util::xmm6,
89 Xbyak::util::xmm7,
90 Xbyak::util::xmm8,
91 Xbyak::util::xmm9,
92 Xbyak::util::xmm10,
93 Xbyak::util::xmm11,
94 Xbyak::util::xmm12,
95 Xbyak::util::xmm13,
96 Xbyak::util::xmm14,
97 Xbyak::util::xmm15,
98});
99
100constexpr std::size_t ABI_SHADOW_SPACE = 0x20;
101
102#else
103
104// System V x86-64 ABI
105const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
106const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
107const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
108const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
109const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
110
111const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
112 // GPRs
113 Xbyak::util::rcx,
114 Xbyak::util::rdx,
115 Xbyak::util::rdi,
116 Xbyak::util::rsi,
117 Xbyak::util::r8,
118 Xbyak::util::r9,
119 Xbyak::util::r10,
120 Xbyak::util::r11,
121 // XMMs
122 Xbyak::util::xmm0,
123 Xbyak::util::xmm1,
124 Xbyak::util::xmm2,
125 Xbyak::util::xmm3,
126 Xbyak::util::xmm4,
127 Xbyak::util::xmm5,
128 Xbyak::util::xmm6,
129 Xbyak::util::xmm7,
130 Xbyak::util::xmm8,
131 Xbyak::util::xmm9,
132 Xbyak::util::xmm10,
133 Xbyak::util::xmm11,
134 Xbyak::util::xmm12,
135 Xbyak::util::xmm13,
136 Xbyak::util::xmm14,
137 Xbyak::util::xmm15,
138});
139
140const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
141 // GPRs
142 Xbyak::util::rbx,
143 Xbyak::util::rbp,
144 Xbyak::util::r12,
145 Xbyak::util::r13,
146 Xbyak::util::r14,
147 Xbyak::util::r15,
148});
149
150constexpr std::size_t ABI_SHADOW_SPACE = 0;
151
152#endif
153
154inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment,
155 std::size_t needed_frame_size, s32* out_subtraction,
156 s32* out_xmm_offset) {
157 int count = (regs & ABI_ALL_GPRS).Count();
158 rsp_alignment -= count * 8;
159 std::size_t subtraction = 0;
160 int xmm_count = (regs & ABI_ALL_XMMS).Count();
161 if (xmm_count) {
162 // If we have any XMMs to save, we must align the stack here.
163 subtraction = rsp_alignment & 0xF;
164 }
165 subtraction += 0x10 * xmm_count;
166 std::size_t xmm_base_subtraction = subtraction;
167 subtraction += needed_frame_size;
168 subtraction += ABI_SHADOW_SPACE;
169 // Final alignment.
170 rsp_alignment -= subtraction;
171 subtraction += rsp_alignment & 0xF;
172
173 *out_subtraction = (s32)subtraction;
174 *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
175}
176
177inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
178 std::size_t rsp_alignment,
179 std::size_t needed_frame_size = 0) {
180 s32 subtraction, xmm_offset;
181 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
182
183 for (int reg_index : (regs & ABI_ALL_GPRS)) {
184 code.push(IndexToReg64(reg_index));
185 }
186
187 if (subtraction != 0) {
188 code.sub(code.rsp, subtraction);
189 }
190
191 for (int reg_index : (regs & ABI_ALL_XMMS)) {
192 code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(reg_index));
193 xmm_offset += 0x10;
194 }
195
196 return ABI_SHADOW_SPACE;
197}
198
199inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
200 std::size_t rsp_alignment,
201 std::size_t needed_frame_size = 0) {
202 s32 subtraction, xmm_offset;
203 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
204
205 for (int reg_index : (regs & ABI_ALL_XMMS)) {
206 code.movaps(IndexToXmm(reg_index), code.xword[code.rsp + xmm_offset]);
207 xmm_offset += 0x10;
208 }
209
210 if (subtraction != 0) {
211 code.add(code.rsp, subtraction);
212 }
213
214 // GPRs need to be popped in reverse order
215 for (int reg_index = 15; reg_index >= 0; reg_index--) {
216 if (regs[reg_index]) {
217 code.pop(IndexToReg64(reg_index));
218 }
219 }
220}
221
222} // namespace Common::X64
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
deleted file mode 100644
index 5cc8a8c76..000000000
--- a/src/common/x64/xbyak_util.h
+++ /dev/null
@@ -1,47 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <type_traits>
8#include <xbyak.h>
9#include "common/x64/xbyak_abi.h"
10
11namespace Common::X64 {
12
13// Constants for use with cmpps/cmpss
14enum {
15 CMP_EQ = 0,
16 CMP_LT = 1,
17 CMP_LE = 2,
18 CMP_UNORD = 3,
19 CMP_NEQ = 4,
20 CMP_NLT = 5,
21 CMP_NLE = 6,
22 CMP_ORD = 7,
23};
24
25inline bool IsWithin2G(uintptr_t ref, uintptr_t target) {
26 u64 distance = target - (ref + 5);
27 return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
28}
29
30inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
31 return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
32}
33
34template <typename T>
35inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
36 static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
37 std::size_t addr = reinterpret_cast<std::size_t>(f);
38 if (IsWithin2G(code, addr)) {
39 code.call(f);
40 } else {
41 // ABI_RETURN is a safe temp register to use before a call
42 code.mov(ABI_RETURN, addr);
43 code.call(ABI_RETURN);
44 }
45}
46
47} // namespace Common::X64
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 64fdf38cd..e1f21a764 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -12,6 +12,8 @@ add_library(core STATIC
12 core_timing.h 12 core_timing.h
13 core_timing_util.cpp 13 core_timing_util.cpp
14 core_timing_util.h 14 core_timing_util.h
15 cpu_core_manager.cpp
16 cpu_core_manager.h
15 crypto/aes_util.cpp 17 crypto/aes_util.cpp
16 crypto/aes_util.h 18 crypto/aes_util.h
17 crypto/encryption_layer.cpp 19 crypto/encryption_layer.cpp
@@ -77,6 +79,8 @@ add_library(core STATIC
77 file_sys/vfs_vector.h 79 file_sys/vfs_vector.h
78 file_sys/xts_archive.cpp 80 file_sys/xts_archive.cpp
79 file_sys/xts_archive.h 81 file_sys/xts_archive.h
82 frontend/applets/software_keyboard.cpp
83 frontend/applets/software_keyboard.h
80 frontend/emu_window.cpp 84 frontend/emu_window.cpp
81 frontend/emu_window.h 85 frontend/emu_window.h
82 frontend/framebuffer_layout.cpp 86 frontend/framebuffer_layout.cpp
@@ -150,6 +154,12 @@ add_library(core STATIC
150 hle/service/am/applet_ae.h 154 hle/service/am/applet_ae.h
151 hle/service/am/applet_oe.cpp 155 hle/service/am/applet_oe.cpp
152 hle/service/am/applet_oe.h 156 hle/service/am/applet_oe.h
157 hle/service/am/applets/applets.cpp
158 hle/service/am/applets/applets.h
159 hle/service/am/applets/software_keyboard.cpp
160 hle/service/am/applets/software_keyboard.h
161 hle/service/am/applets/stub_applet.cpp
162 hle/service/am/applets/stub_applet.h
153 hle/service/am/idle.cpp 163 hle/service/am/idle.cpp
154 hle/service/am/idle.h 164 hle/service/am/idle.h
155 hle/service/am/omm.cpp 165 hle/service/am/omm.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 6d5b5a2d0..795fabc65 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -14,6 +14,7 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_cpu.h" 15#include "core/core_cpu.h"
16#include "core/core_timing.h" 16#include "core/core_timing.h"
17#include "core/cpu_core_manager.h"
17#include "core/file_sys/mode.h" 18#include "core/file_sys/mode.h"
18#include "core/file_sys/vfs_concat.h" 19#include "core/file_sys/vfs_concat.h"
19#include "core/file_sys/vfs_real.h" 20#include "core/file_sys/vfs_real.h"
@@ -23,12 +24,13 @@
23#include "core/hle/kernel/process.h" 24#include "core/hle/kernel/process.h"
24#include "core/hle/kernel/scheduler.h" 25#include "core/hle/kernel/scheduler.h"
25#include "core/hle/kernel/thread.h" 26#include "core/hle/kernel/thread.h"
27#include "core/hle/service/am/applets/software_keyboard.h"
26#include "core/hle/service/service.h" 28#include "core/hle/service/service.h"
27#include "core/hle/service/sm/sm.h" 29#include "core/hle/service/sm/sm.h"
28#include "core/loader/loader.h" 30#include "core/loader/loader.h"
29#include "core/perf_stats.h" 31#include "core/perf_stats.h"
30#include "core/settings.h"
31#include "core/telemetry_session.h" 32#include "core/telemetry_session.h"
33#include "frontend/applets/software_keyboard.h"
32#include "video_core/debug_utils/debug_utils.h" 34#include "video_core/debug_utils/debug_utils.h"
33#include "video_core/gpu.h" 35#include "video_core/gpu.h"
34#include "video_core/renderer_base.h" 36#include "video_core/renderer_base.h"
@@ -69,64 +71,22 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
69 71
70 return vfs->OpenFile(path, FileSys::Mode::Read); 72 return vfs->OpenFile(path, FileSys::Mode::Read);
71} 73}
72
73/// Runs a CPU core while the system is powered on
74void RunCpuCore(Cpu& cpu_state) {
75 while (Core::System::GetInstance().IsPoweredOn()) {
76 cpu_state.RunLoop(true);
77 }
78}
79} // Anonymous namespace 74} // Anonymous namespace
80 75
81struct System::Impl { 76struct System::Impl {
82 Cpu& CurrentCpuCore() { 77 Cpu& CurrentCpuCore() {
83 if (Settings::values.use_multi_core) { 78 return cpu_core_manager.GetCurrentCore();
84 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
85 ASSERT(search != thread_to_cpu.end());
86 ASSERT(search->second);
87 return *search->second;
88 }
89
90 // Otherwise, use single-threaded mode active_core variable
91 return *cpu_cores[active_core];
92 } 79 }
93 80
94 ResultStatus RunLoop(bool tight_loop) { 81 ResultStatus RunLoop(bool tight_loop) {
95 status = ResultStatus::Success; 82 status = ResultStatus::Success;
96 83
97 // Update thread_to_cpu in case Core 0 is run from a different host thread 84 cpu_core_manager.RunLoop(tight_loop);
98 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
99
100 if (GDBStub::IsServerEnabled()) {
101 GDBStub::HandlePacket();
102
103 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
104 // execute. Otherwise, get out of the loop function.
105 if (GDBStub::GetCpuHaltFlag()) {
106 if (GDBStub::GetCpuStepFlag()) {
107 tight_loop = false;
108 } else {
109 return ResultStatus::Success;
110 }
111 }
112 }
113
114 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
115 cpu_cores[active_core]->RunLoop(tight_loop);
116 if (Settings::values.use_multi_core) {
117 // Cores 1-3 are run on other threads in this mode
118 break;
119 }
120 }
121
122 if (GDBStub::IsServerEnabled()) {
123 GDBStub::SetCpuStepFlag(false);
124 }
125 85
126 return status; 86 return status;
127 } 87 }
128 88
129 ResultStatus Init(Frontend::EmuWindow& emu_window) { 89 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
130 LOG_DEBUG(HW_Memory, "initialized OK"); 90 LOG_DEBUG(HW_Memory, "initialized OK");
131 91
132 CoreTiming::Init(); 92 CoreTiming::Init();
@@ -136,15 +96,13 @@ struct System::Impl {
136 if (virtual_filesystem == nullptr) 96 if (virtual_filesystem == nullptr)
137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 97 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
138 98
99 /// Create default implementations of applets if one is not provided.
100 if (software_keyboard == nullptr)
101 software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
102
139 auto main_process = Kernel::Process::Create(kernel, "main"); 103 auto main_process = Kernel::Process::Create(kernel, "main");
140 kernel.MakeCurrentProcess(main_process.get()); 104 kernel.MakeCurrentProcess(main_process.get());
141 105
142 cpu_barrier = std::make_unique<CpuBarrier>();
143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
145 cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
146 }
147
148 telemetry_session = std::make_unique<Core::TelemetrySession>(); 106 telemetry_session = std::make_unique<Core::TelemetrySession>();
149 service_manager = std::make_shared<Service::SM::ServiceManager>(); 107 service_manager = std::make_shared<Service::SM::ServiceManager>();
150 108
@@ -158,17 +116,8 @@ struct System::Impl {
158 116
159 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); 117 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
160 118
161 // Create threads for CPU cores 1-3, and build thread_to_cpu map 119 cpu_core_manager.Initialize(system);
162 // CPU core 0 is run on the main thread 120 is_powered_on = true;
163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
164 if (Settings::values.use_multi_core) {
165 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
166 cpu_core_threads[index] =
167 std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
168 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
169 }
170 }
171
172 LOG_DEBUG(Core, "Initialized OK"); 121 LOG_DEBUG(Core, "Initialized OK");
173 122
174 // Reset counters and set time origin to current frame 123 // Reset counters and set time origin to current frame
@@ -178,7 +127,8 @@ struct System::Impl {
178 return ResultStatus::Success; 127 return ResultStatus::Success;
179 } 128 }
180 129
181 ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 130 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
131 const std::string& filepath) {
182 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 132 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
183 133
184 if (!app_loader) { 134 if (!app_loader) {
@@ -195,7 +145,7 @@ struct System::Impl {
195 return ResultStatus::ErrorSystemMode; 145 return ResultStatus::ErrorSystemMode;
196 } 146 }
197 147
198 ResultStatus init_result{Init(emu_window)}; 148 ResultStatus init_result{Init(system, emu_window)};
199 if (init_result != ResultStatus::Success) { 149 if (init_result != ResultStatus::Success) {
200 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", 150 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
201 static_cast<int>(init_result)); 151 static_cast<int>(init_result));
@@ -225,6 +175,8 @@ struct System::Impl {
225 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", 175 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
226 perf_results.frametime * 1000.0); 176 perf_results.frametime * 1000.0);
227 177
178 is_powered_on = false;
179
228 // Shutdown emulation session 180 // Shutdown emulation session
229 renderer.reset(); 181 renderer.reset();
230 GDBStub::Shutdown(); 182 GDBStub::Shutdown();
@@ -234,19 +186,7 @@ struct System::Impl {
234 gpu_core.reset(); 186 gpu_core.reset();
235 187
236 // Close all CPU/threading state 188 // Close all CPU/threading state
237 cpu_barrier->NotifyEnd(); 189 cpu_core_manager.Shutdown();
238 if (Settings::values.use_multi_core) {
239 for (auto& thread : cpu_core_threads) {
240 thread->join();
241 thread.reset();
242 }
243 }
244 thread_to_cpu.clear();
245 for (auto& cpu_core : cpu_cores) {
246 cpu_core.reset();
247 }
248 cpu_exclusive_monitor.reset();
249 cpu_barrier.reset();
250 190
251 // Shutdown kernel and core timing 191 // Shutdown kernel and core timing
252 kernel.Shutdown(); 192 kernel.Shutdown();
@@ -283,11 +223,11 @@ struct System::Impl {
283 std::unique_ptr<VideoCore::RendererBase> renderer; 223 std::unique_ptr<VideoCore::RendererBase> renderer;
284 std::unique_ptr<Tegra::GPU> gpu_core; 224 std::unique_ptr<Tegra::GPU> gpu_core;
285 std::shared_ptr<Tegra::DebugContext> debug_context; 225 std::shared_ptr<Tegra::DebugContext> debug_context;
286 std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 226 CpuCoreManager cpu_core_manager;
287 std::unique_ptr<CpuBarrier> cpu_barrier; 227 bool is_powered_on = false;
288 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; 228
289 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; 229 /// Frontend applets
290 std::size_t active_core{}; ///< Active core, only used in single thread mode 230 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
291 231
292 /// Service manager 232 /// Service manager
293 std::shared_ptr<Service::SM::ServiceManager> service_manager; 233 std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -298,9 +238,6 @@ struct System::Impl {
298 ResultStatus status = ResultStatus::Success; 238 ResultStatus status = ResultStatus::Success;
299 std::string status_details = ""; 239 std::string status_details = "";
300 240
301 /// Map of guest threads to CPU cores
302 std::map<std::thread::id, Cpu*> thread_to_cpu;
303
304 Core::PerfStats perf_stats; 241 Core::PerfStats perf_stats;
305 Core::FrameLimiter frame_limiter; 242 Core::FrameLimiter frame_limiter;
306}; 243};
@@ -325,17 +262,15 @@ System::ResultStatus System::SingleStep() {
325} 262}
326 263
327void System::InvalidateCpuInstructionCaches() { 264void System::InvalidateCpuInstructionCaches() {
328 for (auto& cpu : impl->cpu_cores) { 265 impl->cpu_core_manager.InvalidateAllInstructionCaches();
329 cpu->ArmInterface().ClearInstructionCache();
330 }
331} 266}
332 267
333System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 268System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
334 return impl->Load(emu_window, filepath); 269 return impl->Load(*this, emu_window, filepath);
335} 270}
336 271
337bool System::IsPoweredOn() const { 272bool System::IsPoweredOn() const {
338 return impl->cpu_barrier && impl->cpu_barrier->IsAlive(); 273 return impl->is_powered_on;
339} 274}
340 275
341void System::PrepareReschedule() { 276void System::PrepareReschedule() {
@@ -399,21 +334,20 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
399} 334}
400 335
401Cpu& System::CpuCore(std::size_t core_index) { 336Cpu& System::CpuCore(std::size_t core_index) {
402 ASSERT(core_index < NUM_CPU_CORES); 337 return impl->cpu_core_manager.GetCore(core_index);
403 return *impl->cpu_cores[core_index];
404} 338}
405 339
406const Cpu& System::CpuCore(std::size_t core_index) const { 340const Cpu& System::CpuCore(std::size_t core_index) const {
407 ASSERT(core_index < NUM_CPU_CORES); 341 ASSERT(core_index < NUM_CPU_CORES);
408 return *impl->cpu_cores[core_index]; 342 return impl->cpu_core_manager.GetCore(core_index);
409} 343}
410 344
411ExclusiveMonitor& System::Monitor() { 345ExclusiveMonitor& System::Monitor() {
412 return *impl->cpu_exclusive_monitor; 346 return impl->cpu_core_manager.GetExclusiveMonitor();
413} 347}
414 348
415const ExclusiveMonitor& System::Monitor() const { 349const ExclusiveMonitor& System::Monitor() const {
416 return *impl->cpu_exclusive_monitor; 350 return impl->cpu_core_manager.GetExclusiveMonitor();
417} 351}
418 352
419Tegra::GPU& System::GPU() { 353Tegra::GPU& System::GPU() {
@@ -488,8 +422,16 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
488 return impl->virtual_filesystem; 422 return impl->virtual_filesystem;
489} 423}
490 424
425void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
426 impl->software_keyboard = std::move(applet);
427}
428
429const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
430 return *impl->software_keyboard;
431}
432
491System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 433System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
492 return impl->Init(emu_window); 434 return impl->Init(*this, emu_window);
493} 435}
494 436
495void System::Shutdown() { 437void System::Shutdown() {
diff --git a/src/core/core.h b/src/core/core.h
index cfacceb81..be71bd437 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -13,6 +13,7 @@
13 13
14namespace Core::Frontend { 14namespace Core::Frontend {
15class EmuWindow; 15class EmuWindow;
16class SoftwareKeyboardApplet;
16} // namespace Core::Frontend 17} // namespace Core::Frontend
17 18
18namespace FileSys { 19namespace FileSys {
@@ -236,6 +237,10 @@ public:
236 237
237 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 238 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
238 239
240 void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
241
242 const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
243
239private: 244private:
240 System(); 245 System();
241 246
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
new file mode 100644
index 000000000..769a6fefa
--- /dev/null
+++ b/src/core/cpu_core_manager.cpp
@@ -0,0 +1,142 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/arm/exclusive_monitor.h"
7#include "core/core.h"
8#include "core/core_cpu.h"
9#include "core/cpu_core_manager.h"
10#include "core/gdbstub/gdbstub.h"
11#include "core/settings.h"
12
13namespace Core {
14namespace {
15void RunCpuCore(const System& system, Cpu& cpu_state) {
16 while (system.IsPoweredOn()) {
17 cpu_state.RunLoop(true);
18 }
19}
20} // Anonymous namespace
21
22CpuCoreManager::CpuCoreManager() = default;
23CpuCoreManager::~CpuCoreManager() = default;
24
25void CpuCoreManager::Initialize(System& system) {
26 barrier = std::make_unique<CpuBarrier>();
27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
28
29 for (std::size_t index = 0; index < cores.size(); ++index) {
30 cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
31 }
32
33 // Create threads for CPU cores 1-3, and build thread_to_cpu map
34 // CPU core 0 is run on the main thread
35 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
36 if (!Settings::values.use_multi_core) {
37 return;
38 }
39
40 for (std::size_t index = 0; index < core_threads.size(); ++index) {
41 core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
42 std::ref(*cores[index + 1]));
43 thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
44 }
45}
46
47void CpuCoreManager::Shutdown() {
48 barrier->NotifyEnd();
49 if (Settings::values.use_multi_core) {
50 for (auto& thread : core_threads) {
51 thread->join();
52 thread.reset();
53 }
54 }
55
56 thread_to_cpu.clear();
57 for (auto& cpu_core : cores) {
58 cpu_core.reset();
59 }
60
61 exclusive_monitor.reset();
62 barrier.reset();
63}
64
65Cpu& CpuCoreManager::GetCore(std::size_t index) {
66 return *cores.at(index);
67}
68
69const Cpu& CpuCoreManager::GetCore(std::size_t index) const {
70 return *cores.at(index);
71}
72
73ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() {
74 return *exclusive_monitor;
75}
76
77const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const {
78 return *exclusive_monitor;
79}
80
81Cpu& CpuCoreManager::GetCurrentCore() {
82 if (Settings::values.use_multi_core) {
83 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
84 ASSERT(search != thread_to_cpu.end());
85 ASSERT(search->second);
86 return *search->second;
87 }
88
89 // Otherwise, use single-threaded mode active_core variable
90 return *cores[active_core];
91}
92
93const Cpu& CpuCoreManager::GetCurrentCore() const {
94 if (Settings::values.use_multi_core) {
95 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
96 ASSERT(search != thread_to_cpu.end());
97 ASSERT(search->second);
98 return *search->second;
99 }
100
101 // Otherwise, use single-threaded mode active_core variable
102 return *cores[active_core];
103}
104
105void CpuCoreManager::RunLoop(bool tight_loop) {
106 // Update thread_to_cpu in case Core 0 is run from a different host thread
107 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
108
109 if (GDBStub::IsServerEnabled()) {
110 GDBStub::HandlePacket();
111
112 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
113 // execute. Otherwise, get out of the loop function.
114 if (GDBStub::GetCpuHaltFlag()) {
115 if (GDBStub::GetCpuStepFlag()) {
116 tight_loop = false;
117 } else {
118 return;
119 }
120 }
121 }
122
123 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
124 cores[active_core]->RunLoop(tight_loop);
125 if (Settings::values.use_multi_core) {
126 // Cores 1-3 are run on other threads in this mode
127 break;
128 }
129 }
130
131 if (GDBStub::IsServerEnabled()) {
132 GDBStub::SetCpuStepFlag(false);
133 }
134}
135
136void CpuCoreManager::InvalidateAllInstructionCaches() {
137 for (auto& cpu : cores) {
138 cpu->ArmInterface().ClearInstructionCache();
139 }
140}
141
142} // namespace Core
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h
new file mode 100644
index 000000000..a4d70ec56
--- /dev/null
+++ b/src/core/cpu_core_manager.h
@@ -0,0 +1,59 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <map>
9#include <memory>
10#include <thread>
11
12namespace Core {
13
14class Cpu;
15class CpuBarrier;
16class ExclusiveMonitor;
17class System;
18
19class CpuCoreManager {
20public:
21 CpuCoreManager();
22 CpuCoreManager(const CpuCoreManager&) = delete;
23 CpuCoreManager(CpuCoreManager&&) = delete;
24
25 ~CpuCoreManager();
26
27 CpuCoreManager& operator=(const CpuCoreManager&) = delete;
28 CpuCoreManager& operator=(CpuCoreManager&&) = delete;
29
30 void Initialize(System& system);
31 void Shutdown();
32
33 Cpu& GetCore(std::size_t index);
34 const Cpu& GetCore(std::size_t index) const;
35
36 Cpu& GetCurrentCore();
37 const Cpu& GetCurrentCore() const;
38
39 ExclusiveMonitor& GetExclusiveMonitor();
40 const ExclusiveMonitor& GetExclusiveMonitor() const;
41
42 void RunLoop(bool tight_loop);
43
44 void InvalidateAllInstructionCaches();
45
46private:
47 static constexpr std::size_t NUM_CPU_CORES = 4;
48
49 std::unique_ptr<ExclusiveMonitor> exclusive_monitor;
50 std::unique_ptr<CpuBarrier> barrier;
51 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores;
52 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads;
53 std::size_t active_core{}; ///< Active core, only used in single thread mode
54
55 /// Map of guest threads to CPU cores
56 std::map<std::thread::id, Cpu*> thread_to_cpu;
57};
58
59} // namespace Core
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 25f5914b6..a350496f7 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -32,7 +32,18 @@ enum class GamecardSize : u8 {
32}; 32};
33 33
34struct GamecardInfo { 34struct GamecardInfo {
35 std::array<u8, 0x70> data; 35 u64_le firmware_version;
36 u32_le access_control_flags;
37 u32_le read_wait_time1;
38 u32_le read_wait_time2;
39 u32_le write_wait_time1;
40 u32_le write_wait_time2;
41 u32_le firmware_mode;
42 u32_le cup_version;
43 std::array<u8, 4> reserved1;
44 u64_le update_partition_hash;
45 u64_le cup_id;
46 std::array<u8, 0x38> reserved2;
36}; 47};
37static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size."); 48static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
38 49
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 8d062eb3e..e8df08724 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -26,6 +26,11 @@ namespace FileSys {
26constexpr u64 SINGLE_BYTE_MODULUS = 0x100; 26constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
27constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 27constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
28 28
29constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
30 "main", "main.npdm", "rtld", "sdk", "subsdk0", "subsdk1", "subsdk2",
31 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
32};
33
29struct NSOBuildHeader { 34struct NSOBuildHeader {
30 u32_le magic; 35 u32_le magic;
31 INSERT_PADDING_BYTES(0x3C); 36 INSERT_PADDING_BYTES(0x3C);
@@ -57,6 +62,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
57 if (exefs == nullptr) 62 if (exefs == nullptr)
58 return exefs; 63 return exefs;
59 64
65 if (Settings::values.dump_exefs) {
66 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
67 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
68 if (dump_dir != nullptr) {
69 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
70 VfsRawCopyD(exefs, exefs_dir);
71 }
72 }
73
60 const auto installed = Service::FileSystem::GetUnionContents(); 74 const auto installed = Service::FileSystem::GetUnionContents();
61 75
62 // Game Updates 76 // Game Updates
@@ -70,6 +84,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
70 exefs = update->GetExeFS(); 84 exefs = update->GetExeFS();
71 } 85 }
72 86
87 // LayeredExeFS
88 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
89 if (load_dir != nullptr && load_dir->GetSize() > 0) {
90 auto patch_dirs = load_dir->GetSubdirectories();
91 std::sort(
92 patch_dirs.begin(), patch_dirs.end(),
93 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
94
95 std::vector<VirtualDir> layers;
96 layers.reserve(patch_dirs.size() + 1);
97 for (const auto& subdir : patch_dirs) {
98 auto exefs_dir = subdir->GetSubdirectory("exefs");
99 if (exefs_dir != nullptr)
100 layers.push_back(std::move(exefs_dir));
101 }
102 layers.push_back(exefs);
103
104 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
105 if (layered != nullptr) {
106 LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
107 exefs = std::move(layered);
108 }
109 }
110
73 return exefs; 111 return exefs;
74} 112}
75 113
@@ -314,18 +352,25 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
314 if (IsDirValidAndNonEmpty(exefs_dir)) { 352 if (IsDirValidAndNonEmpty(exefs_dir)) {
315 bool ips = false; 353 bool ips = false;
316 bool ipswitch = false; 354 bool ipswitch = false;
355 bool layeredfs = false;
317 356
318 for (const auto& file : exefs_dir->GetFiles()) { 357 for (const auto& file : exefs_dir->GetFiles()) {
319 if (file->GetExtension() == "ips") 358 if (file->GetExtension() == "ips") {
320 ips = true; 359 ips = true;
321 else if (file->GetExtension() == "pchtxt") 360 } else if (file->GetExtension() == "pchtxt") {
322 ipswitch = true; 361 ipswitch = true;
362 } else if (std::find(EXEFS_FILE_NAMES.begin(), EXEFS_FILE_NAMES.end(),
363 file->GetName()) != EXEFS_FILE_NAMES.end()) {
364 layeredfs = true;
365 }
323 } 366 }
324 367
325 if (ips) 368 if (ips)
326 AppendCommaIfNotEmpty(types, "IPS"); 369 AppendCommaIfNotEmpty(types, "IPS");
327 if (ipswitch) 370 if (ipswitch)
328 AppendCommaIfNotEmpty(types, "IPSwitch"); 371 AppendCommaIfNotEmpty(types, "IPSwitch");
372 if (layeredfs)
373 AppendCommaIfNotEmpty(types, "LayeredExeFS");
329 } 374 }
330 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) 375 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
331 AppendCommaIfNotEmpty(types, "LayeredFS"); 376 AppendCommaIfNotEmpty(types, "LayeredFS");
diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp
new file mode 100644
index 000000000..856ed33da
--- /dev/null
+++ b/src/core/frontend/applets/software_keyboard.cpp
@@ -0,0 +1,29 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/backend.h"
6#include "common/string_util.h"
7#include "core/frontend/applets/software_keyboard.h"
8
9namespace Core::Frontend {
10SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
11
12void DefaultSoftwareKeyboardApplet::RequestText(
13 std::function<void(std::optional<std::u16string>)> out,
14 SoftwareKeyboardParameters parameters) const {
15 if (parameters.initial_text.empty())
16 out(u"yuzu");
17
18 out(parameters.initial_text);
19}
20
21void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(
22 std::u16string error_message, std::function<void()> finished_check) const {
23 LOG_WARNING(Service_AM,
24 "(STUBBED) called - Default fallback software keyboard does not support text "
25 "check! (error_message={})",
26 Common::UTF16ToUTF8(error_message));
27 finished_check();
28}
29} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h
new file mode 100644
index 000000000..f9b202664
--- /dev/null
+++ b/src/core/frontend/applets/software_keyboard.h
@@ -0,0 +1,54 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <optional>
9#include <string>
10#include "common/bit_field.h"
11#include "common/common_types.h"
12
13namespace Core::Frontend {
14struct SoftwareKeyboardParameters {
15 std::u16string submit_text;
16 std::u16string header_text;
17 std::u16string sub_text;
18 std::u16string guide_text;
19 std::u16string initial_text;
20 std::size_t max_length;
21 bool password;
22 bool cursor_at_beginning;
23
24 union {
25 u8 value;
26
27 BitField<1, 1, u8> disable_space;
28 BitField<2, 1, u8> disable_address;
29 BitField<3, 1, u8> disable_percent;
30 BitField<4, 1, u8> disable_slash;
31 BitField<6, 1, u8> disable_number;
32 BitField<7, 1, u8> disable_download_code;
33 };
34};
35
36class SoftwareKeyboardApplet {
37public:
38 virtual ~SoftwareKeyboardApplet();
39
40 virtual void RequestText(std::function<void(std::optional<std::u16string>)> out,
41 SoftwareKeyboardParameters parameters) const = 0;
42 virtual void SendTextCheckDialog(std::u16string error_message,
43 std::function<void()> finished_check) const = 0;
44};
45
46class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
47public:
48 void RequestText(std::function<void(std::optional<std::u16string>)> out,
49 SoftwareKeyboardParameters parameters) const override;
50 void SendTextCheckDialog(std::u16string error_message,
51 std::function<void()> finished_check) const override;
52};
53
54} // namespace Core::Frontend
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 39bdf4e21..16fdcd376 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -132,4 +132,11 @@ using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>
132 */ 132 */
133using TouchDevice = InputDevice<std::tuple<float, float, bool>>; 133using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
134 134
135/**
136 * A mouse device is an input device that returns a tuple of two floats and four ints.
137 * The first two floats are X and Y device coordinates of the mouse (from 0-1).
138 * The s32s are the mouse wheel.
139 */
140using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
141
135} // namespace Input 142} // namespace Input
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index bdcc889e0..687dea409 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -71,10 +71,6 @@ constexpr u32 PSTATE_REGISTER = 33;
71constexpr u32 UC_ARM64_REG_Q0 = 34; 71constexpr u32 UC_ARM64_REG_Q0 = 34;
72constexpr u32 FPCR_REGISTER = 66; 72constexpr u32 FPCR_REGISTER = 66;
73 73
74// TODO/WiP - Used while working on support for FPU
75constexpr u32 TODO_DUMMY_REG_997 = 997;
76constexpr u32 TODO_DUMMY_REG_998 = 998;
77
78// For sample XML files see the GDB source /gdb/features 74// For sample XML files see the GDB source /gdb/features
79// GDB also wants the l character at the start 75// GDB also wants the l character at the start
80// This XML defines what the registers are for this specific ARM device 76// This XML defines what the registers are for this specific ARM device
@@ -260,6 +256,36 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
260 } 256 }
261} 257}
262 258
259static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) {
260 if (!thread) {
261 return u128{0};
262 }
263
264 auto& thread_context = thread->GetContext();
265
266 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
267 return thread_context.vector_registers[id - UC_ARM64_REG_Q0];
268 } else if (id == FPCR_REGISTER) {
269 return u128{thread_context.fpcr, 0};
270 } else {
271 return u128{0};
272 }
273}
274
275static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr) {
276 if (!thread) {
277 return;
278 }
279
280 auto& thread_context = thread->GetContext();
281
282 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
283 thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val;
284 } else if (id == FPCR_REGISTER) {
285 thread_context.fpcr = val[0];
286 }
287}
288
263/** 289/**
264 * Turns hex string character into the equivalent byte. 290 * Turns hex string character into the equivalent byte.
265 * 291 *
@@ -409,6 +435,27 @@ static u64 GdbHexToLong(const u8* src) {
409 return output; 435 return output;
410} 436}
411 437
438/**
439 * Convert a gdb-formatted hex string into a u128.
440 *
441 * @param src Pointer to hex string.
442 */
443static u128 GdbHexToU128(const u8* src) {
444 u128 output;
445
446 for (int i = 0; i < 16; i += 2) {
447 output[0] = (output[0] << 4) | HexCharToValue(src[15 - i - 1]);
448 output[0] = (output[0] << 4) | HexCharToValue(src[15 - i]);
449 }
450
451 for (int i = 0; i < 16; i += 2) {
452 output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i - 1]);
453 output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i]);
454 }
455
456 return output;
457}
458
412/// Read a byte from the gdb client. 459/// Read a byte from the gdb client.
413static u8 ReadByte() { 460static u8 ReadByte() {
414 u8 c; 461 u8 c;
@@ -599,8 +646,7 @@ static void HandleQuery() {
599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 646 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
600 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList(); 647 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
601 for (const auto& thread : threads) { 648 for (const auto& thread : threads) {
602 val += fmt::format("{:x}", thread->GetThreadID()); 649 val += fmt::format("{:x},", thread->GetThreadID());
603 val += ",";
604 } 650 }
605 } 651 }
606 val.pop_back(); 652 val.pop_back();
@@ -791,11 +837,15 @@ static void ReadRegister() {
791 } else if (id == PSTATE_REGISTER) { 837 } else if (id == PSTATE_REGISTER) {
792 IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread))); 838 IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread)));
793 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { 839 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
794 LongToGdbHex(reply, RegRead(id, current_thread)); 840 u128 r = FpuRead(id, current_thread);
841 LongToGdbHex(reply, r[0]);
842 LongToGdbHex(reply + 16, r[1]);
795 } else if (id == FPCR_REGISTER) { 843 } else if (id == FPCR_REGISTER) {
796 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread)); 844 u128 r = FpuRead(id, current_thread);
797 } else { 845 IntToGdbHex(reply, static_cast<u32>(r[0]));
798 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread)); 846 } else if (id == FPCR_REGISTER + 1) {
847 u128 r = FpuRead(id, current_thread);
848 IntToGdbHex(reply, static_cast<u32>(r[0] >> 32));
799 } 849 }
800 850
801 SendReply(reinterpret_cast<char*>(reply)); 851 SendReply(reinterpret_cast<char*>(reply));
@@ -822,13 +872,18 @@ static void ReadRegisters() {
822 872
823 bufptr += 8; 873 bufptr += 8;
824 874
825 for (u32 reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) { 875 u128 r;
826 LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread)); 876
877 for (u32 reg = UC_ARM64_REG_Q0; reg < FPCR_REGISTER; reg++) {
878 r = FpuRead(reg, current_thread);
879 LongToGdbHex(bufptr + reg * 32, r[0]);
880 LongToGdbHex(bufptr + reg * 32 + 16, r[1]);
827 } 881 }
828 882
829 bufptr += 32 * 32; 883 bufptr += 32 * 32;
830 884
831 LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread)); 885 r = FpuRead(FPCR_REGISTER, current_thread);
886 IntToGdbHex(bufptr, static_cast<u32>(r[0]));
832 887
833 bufptr += 8; 888 bufptr += 8;
834 889
@@ -853,14 +908,12 @@ static void WriteRegister() {
853 } else if (id == PSTATE_REGISTER) { 908 } else if (id == PSTATE_REGISTER) {
854 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); 909 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
855 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { 910 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
856 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); 911 FpuWrite(id, GdbHexToU128(buffer_ptr), current_thread);
857 } else if (id == FPCR_REGISTER) { 912 } else if (id == FPCR_REGISTER) {
858 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread); 913 } else if (id == FPCR_REGISTER + 1) {
859 } else {
860 RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
861 } 914 }
862 915
863 // Update Unicorn context skipping scheduler, no running threads at this point 916 // Update ARM context, skipping scheduler - no running threads at this point
864 Core::System::GetInstance() 917 Core::System::GetInstance()
865 .ArmInterface(current_core) 918 .ArmInterface(current_core)
866 .LoadContext(current_thread->GetContext()); 919 .LoadContext(current_thread->GetContext());
@@ -885,13 +938,13 @@ static void WriteRegisters() {
885 } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) { 938 } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) {
886 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); 939 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
887 } else if (reg == FPCR_REGISTER) { 940 } else if (reg == FPCR_REGISTER) {
888 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread); 941 RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
889 } else { 942 } else if (reg == FPCR_REGISTER + 1) {
890 UNIMPLEMENTED(); 943 RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
891 } 944 }
892 } 945 }
893 946
894 // Update Unicorn context skipping scheduler, no running threads at this point 947 // Update ARM context, skipping scheduler - no running threads at this point
895 Core::System::GetInstance() 948 Core::System::GetInstance()
896 .ArmInterface(current_core) 949 .ArmInterface(current_core)
897 .LoadContext(current_thread->GetContext()); 950 .LoadContext(current_thread->GetContext());
@@ -917,12 +970,6 @@ static void ReadMemory() {
917 SendReply("E01"); 970 SendReply("E01");
918 } 971 }
919 972
920 const auto& vm_manager = Core::CurrentProcess()->VMManager();
921 if (addr < vm_manager.GetCodeRegionBaseAddress() ||
922 addr >= vm_manager.GetMapRegionEndAddress()) {
923 return SendReply("E00");
924 }
925
926 if (!Memory::IsValidVirtualAddress(addr)) { 973 if (!Memory::IsValidVirtualAddress(addr)) {
927 return SendReply("E00"); 974 return SendReply("E00");
928 } 975 }
@@ -967,7 +1014,7 @@ void Break(bool is_memory_break) {
967static void Step() { 1014static void Step() {
968 if (command_length > 1) { 1015 if (command_length > 1) {
969 RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread); 1016 RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
970 // Update Unicorn context skipping scheduler, no running threads at this point 1017 // Update ARM context, skipping scheduler - no running threads at this point
971 Core::System::GetInstance() 1018 Core::System::GetInstance()
972 .ArmInterface(current_core) 1019 .ArmInterface(current_core)
973 .LoadContext(current_thread->GetContext()); 1020 .LoadContext(current_thread->GetContext());
@@ -1010,7 +1057,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
1010 breakpoint.addr = addr; 1057 breakpoint.addr = addr;
1011 breakpoint.len = len; 1058 breakpoint.len = len;
1012 Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); 1059 Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
1013 static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}}; 1060 static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
1014 Memory::WriteBlock(addr, btrap.data(), btrap.size()); 1061 Memory::WriteBlock(addr, btrap.data(), btrap.size());
1015 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 1062 Core::System::GetInstance().InvalidateCpuInstructionCaches();
1016 p.insert({addr, breakpoint}); 1063 p.insert({addr, breakpoint});
@@ -1321,13 +1368,15 @@ void SetCpuStepFlag(bool is_step) {
1321} 1368}
1322 1369
1323void SendTrap(Kernel::Thread* thread, int trap) { 1370void SendTrap(Kernel::Thread* thread, int trap) {
1324 if (send_trap) { 1371 if (!send_trap) {
1325 if (!halt_loop || current_thread == thread) { 1372 return;
1326 current_thread = thread;
1327 SendSignal(thread, trap);
1328 }
1329 halt_loop = true;
1330 send_trap = false;
1331 } 1373 }
1374
1375 if (!halt_loop || current_thread == thread) {
1376 current_thread = thread;
1377 SendSignal(thread, trap);
1378 }
1379 halt_loop = true;
1380 send_trap = false;
1332} 1381}
1333}; // namespace GDBStub 1382}; // namespace GDBStub
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index 5ee5c05e3..1bf79b692 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -12,12 +12,23 @@
12#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
13 13
14namespace Kernel { 14namespace Kernel {
15namespace {
16constexpr u16 GetSlot(Handle handle) {
17 return handle >> 15;
18}
19
20constexpr u16 GetGeneration(Handle handle) {
21 return handle & 0x7FFF;
22}
23} // Anonymous namespace
15 24
16HandleTable::HandleTable() { 25HandleTable::HandleTable() {
17 next_generation = 1; 26 next_generation = 1;
18 Clear(); 27 Clear();
19} 28}
20 29
30HandleTable::~HandleTable() = default;
31
21ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { 32ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
22 DEBUG_ASSERT(obj != nullptr); 33 DEBUG_ASSERT(obj != nullptr);
23 34
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index 9e2f33e8a..e3f3e3fb8 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -43,6 +43,7 @@ enum KernelHandle : Handle {
43class HandleTable final : NonCopyable { 43class HandleTable final : NonCopyable {
44public: 44public:
45 HandleTable(); 45 HandleTable();
46 ~HandleTable();
46 47
47 /** 48 /**
48 * Allocates a handle for the given object. 49 * Allocates a handle for the given object.
@@ -89,18 +90,8 @@ public:
89 void Clear(); 90 void Clear();
90 91
91private: 92private:
92 /** 93 /// This is the maximum limit of handles allowed per process in Horizon
93 * This is the maximum limit of handles allowed per process in CTR-OS. It can be further 94 static constexpr std::size_t MAX_COUNT = 1024;
94 * reduced by ExHeader values, but this is not emulated here.
95 */
96 static const std::size_t MAX_COUNT = 4096;
97
98 static u16 GetSlot(Handle handle) {
99 return handle >> 15;
100 }
101 static u16 GetGeneration(Handle handle) {
102 return handle & 0x7FFF;
103 }
104 95
105 /// Stores the Object referenced by the handle or null if the slot is empty. 96 /// Stores the Object referenced by the handle or null if the slot is empty.
106 std::array<SharedPtr<Object>, MAX_COUNT> objects; 97 std::array<SharedPtr<Object>, MAX_COUNT> objects;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1fd4ba5d2..e441c5bc6 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -105,7 +105,7 @@ struct KernelCore::Impl {
105 void Initialize(KernelCore& kernel) { 105 void Initialize(KernelCore& kernel) {
106 Shutdown(); 106 Shutdown();
107 107
108 InitializeResourceLimits(kernel); 108 InitializeSystemResourceLimit(kernel);
109 InitializeThreads(); 109 InitializeThreads();
110 InitializeTimers(); 110 InitializeTimers();
111 } 111 }
@@ -118,7 +118,7 @@ struct KernelCore::Impl {
118 process_list.clear(); 118 process_list.clear();
119 current_process = nullptr; 119 current_process = nullptr;
120 120
121 resource_limits.fill(nullptr); 121 system_resource_limit = nullptr;
122 122
123 thread_wakeup_callback_handle_table.Clear(); 123 thread_wakeup_callback_handle_table.Clear();
124 thread_wakeup_event_type = nullptr; 124 thread_wakeup_event_type = nullptr;
@@ -129,63 +129,17 @@ struct KernelCore::Impl {
129 named_ports.clear(); 129 named_ports.clear();
130 } 130 }
131 131
132 void InitializeResourceLimits(KernelCore& kernel) { 132 // Creates the default system resource limit
133 // Create the four resource limits that the system uses 133 void InitializeSystemResourceLimit(KernelCore& kernel) {
134 // Create the APPLICATION resource limit 134 system_resource_limit = ResourceLimit::Create(kernel, "System");
135 SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications"); 135
136 resource_limit->max_priority = 0x18; 136 // If setting the default system values fails, then something seriously wrong has occurred.
137 resource_limit->max_commit = 0x4000000; 137 ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
138 resource_limit->max_threads = 0x20; 138 .IsSuccess());
139 resource_limit->max_events = 0x20; 139 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
140 resource_limit->max_mutexes = 0x20; 140 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
141 resource_limit->max_semaphores = 0x8; 141 ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
142 resource_limit->max_timers = 0x8; 142 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
143 resource_limit->max_shared_mems = 0x10;
144 resource_limit->max_address_arbiters = 0x2;
145 resource_limit->max_cpu_time = 0x1E;
146 resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
147
148 // Create the SYS_APPLET resource limit
149 resource_limit = ResourceLimit::Create(kernel, "System Applets");
150 resource_limit->max_priority = 0x4;
151 resource_limit->max_commit = 0x5E00000;
152 resource_limit->max_threads = 0x1D;
153 resource_limit->max_events = 0xB;
154 resource_limit->max_mutexes = 0x8;
155 resource_limit->max_semaphores = 0x4;
156 resource_limit->max_timers = 0x4;
157 resource_limit->max_shared_mems = 0x8;
158 resource_limit->max_address_arbiters = 0x3;
159 resource_limit->max_cpu_time = 0x2710;
160 resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
161
162 // Create the LIB_APPLET resource limit
163 resource_limit = ResourceLimit::Create(kernel, "Library Applets");
164 resource_limit->max_priority = 0x4;
165 resource_limit->max_commit = 0x600000;
166 resource_limit->max_threads = 0xE;
167 resource_limit->max_events = 0x8;
168 resource_limit->max_mutexes = 0x8;
169 resource_limit->max_semaphores = 0x4;
170 resource_limit->max_timers = 0x4;
171 resource_limit->max_shared_mems = 0x8;
172 resource_limit->max_address_arbiters = 0x1;
173 resource_limit->max_cpu_time = 0x2710;
174 resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
175
176 // Create the OTHER resource limit
177 resource_limit = ResourceLimit::Create(kernel, "Others");
178 resource_limit->max_priority = 0x4;
179 resource_limit->max_commit = 0x2180000;
180 resource_limit->max_threads = 0xE1;
181 resource_limit->max_events = 0x108;
182 resource_limit->max_mutexes = 0x25;
183 resource_limit->max_semaphores = 0x43;
184 resource_limit->max_timers = 0x2C;
185 resource_limit->max_shared_mems = 0x1F;
186 resource_limit->max_address_arbiters = 0x2D;
187 resource_limit->max_cpu_time = 0x3E8;
188 resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
189 } 143 }
190 144
191 void InitializeThreads() { 145 void InitializeThreads() {
@@ -208,7 +162,7 @@ struct KernelCore::Impl {
208 std::vector<SharedPtr<Process>> process_list; 162 std::vector<SharedPtr<Process>> process_list;
209 Process* current_process = nullptr; 163 Process* current_process = nullptr;
210 164
211 std::array<SharedPtr<ResourceLimit>, 4> resource_limits; 165 SharedPtr<ResourceLimit> system_resource_limit;
212 166
213 /// The event type of the generic timer callback event 167 /// The event type of the generic timer callback event
214 CoreTiming::EventType* timer_callback_event_type = nullptr; 168 CoreTiming::EventType* timer_callback_event_type = nullptr;
@@ -239,9 +193,8 @@ void KernelCore::Shutdown() {
239 impl->Shutdown(); 193 impl->Shutdown();
240} 194}
241 195
242SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( 196SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
243 ResourceLimitCategory category) const { 197 return impl->system_resource_limit;
244 return impl->resource_limits.at(static_cast<std::size_t>(category));
245} 198}
246 199
247SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const { 200SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7f822d524..ea00c89f5 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -24,8 +24,6 @@ class ResourceLimit;
24class Thread; 24class Thread;
25class Timer; 25class Timer;
26 26
27enum class ResourceLimitCategory : u8;
28
29/// Represents a single instance of the kernel. 27/// Represents a single instance of the kernel.
30class KernelCore { 28class KernelCore {
31private: 29private:
@@ -47,8 +45,8 @@ public:
47 /// Clears all resources in use by the kernel instance. 45 /// Clears all resources in use by the kernel instance.
48 void Shutdown(); 46 void Shutdown();
49 47
50 /// Retrieves a shared pointer to a ResourceLimit identified by the given category. 48 /// Retrieves a shared pointer to the system resource limit instance.
51 SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; 49 SharedPtr<ResourceLimit> GetSystemResourceLimit() const;
52 50
53 /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. 51 /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
54 SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const; 52 SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index f06b6bb55..7ca538401 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <random>
7#include "common/assert.h" 8#include "common/assert.h"
8#include "common/logging/log.h" 9#include "common/logging/log.h"
9#include "core/core.h" 10#include "core/core.h"
@@ -28,7 +29,7 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
28 process->name = std::move(name); 29 process->name = std::move(name);
29 process->flags.raw = 0; 30 process->flags.raw = 0;
30 process->flags.memory_region.Assign(MemoryRegion::APPLICATION); 31 process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
31 process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION); 32 process->resource_limit = kernel.GetSystemResourceLimit();
32 process->status = ProcessStatus::Created; 33 process->status = ProcessStatus::Created;
33 process->program_id = 0; 34 process->program_id = 0;
34 process->process_id = kernel.CreateNewProcessID(); 35 process->process_id = kernel.CreateNewProcessID();
@@ -252,8 +253,8 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
252 return vm_manager.HeapFree(target, size); 253 return vm_manager.HeapFree(target, size);
253} 254}
254 255
255ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 256ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
256 return vm_manager.MirrorMemory(dst_addr, src_addr, size); 257 return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
257} 258}
258 259
259ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { 260ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index cf48787ce..ada845c7f 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -8,7 +8,6 @@
8#include <bitset> 8#include <bitset>
9#include <cstddef> 9#include <cstddef>
10#include <memory> 10#include <memory>
11#include <random>
12#include <string> 11#include <string>
13#include <vector> 12#include <vector>
14#include <boost/container/static_vector.hpp> 13#include <boost/container/static_vector.hpp>
@@ -259,7 +258,8 @@ public:
259 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 258 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
260 ResultCode HeapFree(VAddr target, u32 size); 259 ResultCode HeapFree(VAddr target, u32 size);
261 260
262 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); 261 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
262 MemoryState state = MemoryState::Mapped);
263 263
264 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); 264 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
265 265
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index b253a680f..2f9695005 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -2,12 +2,16 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include "core/hle/kernel/errors.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/hle/kernel/resource_limit.h" 6#include "core/hle/kernel/resource_limit.h"
7#include "core/hle/result.h"
9 8
10namespace Kernel { 9namespace Kernel {
10namespace {
11constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
12 return static_cast<std::size_t>(type);
13}
14} // Anonymous namespace
11 15
12ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {} 16ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
13ResourceLimit::~ResourceLimit() = default; 17ResourceLimit::~ResourceLimit() = default;
@@ -19,59 +23,22 @@ SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string n
19 return resource_limit; 23 return resource_limit;
20} 24}
21 25
22s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { 26s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
23 switch (resource) { 27 return values.at(ResourceTypeToIndex(resource));
24 case ResourceType::Commit: 28}
25 return current_commit; 29
26 case ResourceType::Thread: 30s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
27 return current_threads; 31 return limits.at(ResourceTypeToIndex(resource));
28 case ResourceType::Event:
29 return current_events;
30 case ResourceType::Mutex:
31 return current_mutexes;
32 case ResourceType::Semaphore:
33 return current_semaphores;
34 case ResourceType::Timer:
35 return current_timers;
36 case ResourceType::SharedMemory:
37 return current_shared_mems;
38 case ResourceType::AddressArbiter:
39 return current_address_arbiters;
40 case ResourceType::CPUTime:
41 return current_cpu_time;
42 default:
43 LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
44 UNIMPLEMENTED();
45 return 0;
46 }
47} 32}
48 33
49u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { 34ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
50 switch (resource) { 35 const auto index = ResourceTypeToIndex(resource);
51 case ResourceType::Priority: 36
52 return max_priority; 37 if (value < values[index]) {
53 case ResourceType::Commit: 38 return ERR_INVALID_STATE;
54 return max_commit;
55 case ResourceType::Thread:
56 return max_threads;
57 case ResourceType::Event:
58 return max_events;
59 case ResourceType::Mutex:
60 return max_mutexes;
61 case ResourceType::Semaphore:
62 return max_semaphores;
63 case ResourceType::Timer:
64 return max_timers;
65 case ResourceType::SharedMemory:
66 return max_shared_mems;
67 case ResourceType::AddressArbiter:
68 return max_address_arbiters;
69 case ResourceType::CPUTime:
70 return max_cpu_time;
71 default:
72 LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
73 UNIMPLEMENTED();
74 return 0;
75 } 39 }
40
41 values[index] = value;
42 return RESULT_SUCCESS;
76} 43}
77} // namespace Kernel 44} // namespace Kernel
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 219e49562..bec065543 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -4,31 +4,25 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/hle/kernel/object.h" 9#include "core/hle/kernel/object.h"
9 10
11union ResultCode;
12
10namespace Kernel { 13namespace Kernel {
11 14
12class KernelCore; 15class KernelCore;
13 16
14enum class ResourceLimitCategory : u8 {
15 APPLICATION = 0,
16 SYS_APPLET = 1,
17 LIB_APPLET = 2,
18 OTHER = 3
19};
20
21enum class ResourceType { 17enum class ResourceType {
22 Priority = 0, 18 PhysicalMemory,
23 Commit = 1, 19 Threads,
24 Thread = 2, 20 Events,
25 Event = 3, 21 TransferMemory,
26 Mutex = 4, 22 Sessions,
27 Semaphore = 5, 23
28 Timer = 6, 24 // Used as a count, not an actual type.
29 SharedMemory = 7, 25 ResourceTypeCount
30 AddressArbiter = 8,
31 CPUTime = 9,
32}; 26};
33 27
34class ResourceLimit final : public Object { 28class ResourceLimit final : public Object {
@@ -55,61 +49,51 @@ public:
55 * @param resource Requested resource type 49 * @param resource Requested resource type
56 * @returns The current value of the resource type 50 * @returns The current value of the resource type
57 */ 51 */
58 s32 GetCurrentResourceValue(ResourceType resource) const; 52 s64 GetCurrentResourceValue(ResourceType resource) const;
59 53
60 /** 54 /**
61 * Gets the max value for the specified resource. 55 * Gets the max value for the specified resource.
62 * @param resource Requested resource type 56 * @param resource Requested resource type
63 * @returns The max value of the resource type 57 * @returns The max value of the resource type
64 */ 58 */
65 u32 GetMaxResourceValue(ResourceType resource) const; 59 s64 GetMaxResourceValue(ResourceType resource) const;
66
67 /// Name of resource limit object.
68 std::string name;
69
70 /// Max thread priority that a process in this category can create
71 s32 max_priority = 0;
72
73 /// Max memory that processes in this category can use
74 s32 max_commit = 0;
75 60
76 ///< Max number of objects that can be collectively created by the processes in this category 61 /**
77 s32 max_threads = 0; 62 * Sets the limit value for a given resource type.
78 s32 max_events = 0; 63 *
79 s32 max_mutexes = 0; 64 * @param resource The resource type to apply the limit to.
80 s32 max_semaphores = 0; 65 * @param value The limit to apply to the given resource type.
81 s32 max_timers = 0; 66 *
82 s32 max_shared_mems = 0; 67 * @return A result code indicating if setting the limit value
83 s32 max_address_arbiters = 0; 68 * was successful or not.
69 *
70 * @note The supplied limit value *must* be greater than or equal to
71 * the current resource value for the given resource type,
72 * otherwise ERR_INVALID_STATE will be returned.
73 */
74 ResultCode SetLimitValue(ResourceType resource, s64 value);
84 75
85 /// Max CPU time that the processes in this category can utilize 76private:
86 s32 max_cpu_time = 0; 77 explicit ResourceLimit(KernelCore& kernel);
78 ~ResourceLimit() override;
87 79
88 // TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind 80 // TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
89 // that APPLICATION resource limits should not be affected by the objects created by service 81 // functions
90 // modules. 82 //
91 // Currently we have no way of distinguishing if a Create was called by the running application, 83 // Currently we have no way of distinguishing if a Create was called by the running application,
92 // or by a service module. Approach this once we have separated the service modules into their 84 // or by a service module. Approach this once we have separated the service modules into their
93 // own processes 85 // own processes
94 86
95 /// Current memory that the processes in this category are using 87 using ResourceArray =
96 s32 current_commit = 0; 88 std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
97 89
98 ///< Current number of objects among all processes in this category 90 /// Maximum values a resource type may reach.
99 s32 current_threads = 0; 91 ResourceArray limits{};
100 s32 current_events = 0; 92 /// Current resource limit values.
101 s32 current_mutexes = 0; 93 ResourceArray values{};
102 s32 current_semaphores = 0;
103 s32 current_timers = 0;
104 s32 current_shared_mems = 0;
105 s32 current_address_arbiters = 0;
106 94
107 /// Current CPU time that the processes in this category are utilizing 95 /// Name of resource limit object.
108 s32 current_cpu_time = 0; 96 std::string name;
109
110private:
111 explicit ResourceLimit(KernelCore& kernel);
112 ~ResourceLimit() override;
113}; 97};
114 98
115} // namespace Kernel 99} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index a016a86b6..0494581f5 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -61,7 +61,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
61} 61}
62 62
63SharedPtr<SharedMemory> SharedMemory::CreateForApplet( 63SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
64 KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size, 64 KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size,
65 MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { 65 MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
66 SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel)); 66 SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
67 67
@@ -78,10 +78,10 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
78 return shared_memory; 78 return shared_memory;
79} 79}
80 80
81ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, 81ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions,
82 MemoryPermission other_permissions) { 82 MemoryPermission other_permissions) {
83 const MemoryPermission own_other_permissions = 83 const MemoryPermission own_other_permissions =
84 target_process == owner_process ? this->permissions : this->other_permissions; 84 &target_process == owner_process ? this->permissions : this->other_permissions;
85 85
86 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare 86 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
87 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { 87 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
@@ -106,7 +106,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
106 VAddr target_address = address; 106 VAddr target_address = address;
107 107
108 // Map the memory block into the target process 108 // Map the memory block into the target process
109 auto result = target_process->VMManager().MapMemoryBlock( 109 auto result = target_process.VMManager().MapMemoryBlock(
110 target_address, backing_block, backing_block_offset, size, MemoryState::Shared); 110 target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
111 if (result.Failed()) { 111 if (result.Failed()) {
112 LOG_ERROR( 112 LOG_ERROR(
@@ -116,14 +116,14 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
116 return result.Code(); 116 return result.Code();
117 } 117 }
118 118
119 return target_process->VMManager().ReprotectRange(target_address, size, 119 return target_process.VMManager().ReprotectRange(target_address, size,
120 ConvertPermissions(permissions)); 120 ConvertPermissions(permissions));
121} 121}
122 122
123ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { 123ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) {
124 // TODO(Subv): Verify what happens if the application tries to unmap an address that is not 124 // TODO(Subv): Verify what happens if the application tries to unmap an address that is not
125 // mapped to a SharedMemory. 125 // mapped to a SharedMemory.
126 return target_process->VMManager().UnmapRange(address, size); 126 return target_process.VMManager().UnmapRange(address, size);
127} 127}
128 128
129VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { 129VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
@@ -132,7 +132,11 @@ VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
132 return static_cast<VMAPermission>(masked_permissions); 132 return static_cast<VMAPermission>(masked_permissions);
133} 133}
134 134
135u8* SharedMemory::GetPointer(u32 offset) { 135u8* SharedMemory::GetPointer(std::size_t offset) {
136 return backing_block->data() + backing_block_offset + offset;
137}
138
139const u8* SharedMemory::GetPointer(std::size_t offset) const {
136 return backing_block->data() + backing_block_offset + offset; 140 return backing_block->data() + backing_block_offset + offset;
137} 141}
138 142
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 2c06bb7ce..0b48db699 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -64,7 +64,7 @@ public:
64 */ 64 */
65 static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel, 65 static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
66 std::shared_ptr<std::vector<u8>> heap_block, 66 std::shared_ptr<std::vector<u8>> heap_block,
67 u32 offset, u32 size, 67 std::size_t offset, u64 size,
68 MemoryPermission permissions, 68 MemoryPermission permissions,
69 MemoryPermission other_permissions, 69 MemoryPermission other_permissions,
70 std::string name = "Unknown Applet"); 70 std::string name = "Unknown Applet");
@@ -81,6 +81,11 @@ public:
81 return HANDLE_TYPE; 81 return HANDLE_TYPE;
82 } 82 }
83 83
84 /// Gets the size of the underlying memory block in bytes.
85 u64 GetSize() const {
86 return size;
87 }
88
84 /** 89 /**
85 * Converts the specified MemoryPermission into the equivalent VMAPermission. 90 * Converts the specified MemoryPermission into the equivalent VMAPermission.
86 * @param permission The MemoryPermission to convert. 91 * @param permission The MemoryPermission to convert.
@@ -94,44 +99,51 @@ public:
94 * @param permissions Memory block map permissions (specified by SVC field) 99 * @param permissions Memory block map permissions (specified by SVC field)
95 * @param other_permissions Memory block map other permissions (specified by SVC field) 100 * @param other_permissions Memory block map other permissions (specified by SVC field)
96 */ 101 */
97 ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions, 102 ResultCode Map(Process& target_process, VAddr address, MemoryPermission permissions,
98 MemoryPermission other_permissions); 103 MemoryPermission other_permissions);
99 104
100 /** 105 /**
101 * Unmaps a shared memory block from the specified address in system memory 106 * Unmaps a shared memory block from the specified address in system memory
102 * @param target_process Process from which to umap the memory block. 107 * @param target_process Process from which to unmap the memory block.
103 * @param address Address in system memory where the shared memory block is mapped 108 * @param address Address in system memory where the shared memory block is mapped
104 * @return Result code of the unmap operation 109 * @return Result code of the unmap operation
105 */ 110 */
106 ResultCode Unmap(Process* target_process, VAddr address); 111 ResultCode Unmap(Process& target_process, VAddr address);
107 112
108 /** 113 /**
109 * Gets a pointer to the shared memory block 114 * Gets a pointer to the shared memory block
110 * @param offset Offset from the start of the shared memory block to get pointer 115 * @param offset Offset from the start of the shared memory block to get pointer
111 * @return Pointer to the shared memory block from the specified offset 116 * @return A pointer to the shared memory block from the specified offset
112 */ 117 */
113 u8* GetPointer(u32 offset = 0); 118 u8* GetPointer(std::size_t offset = 0);
119
120 /**
121 * Gets a constant pointer to the shared memory block
122 * @param offset Offset from the start of the shared memory block to get pointer
123 * @return A constant pointer to the shared memory block from the specified offset
124 */
125 const u8* GetPointer(std::size_t offset = 0) const;
126
127private:
128 explicit SharedMemory(KernelCore& kernel);
129 ~SharedMemory() override;
114 130
115 /// Process that created this shared memory block.
116 SharedPtr<Process> owner_process;
117 /// Address of shared memory block in the owner process if specified.
118 VAddr base_address;
119 /// Backing memory for this shared memory block. 131 /// Backing memory for this shared memory block.
120 std::shared_ptr<std::vector<u8>> backing_block; 132 std::shared_ptr<std::vector<u8>> backing_block;
121 /// Offset into the backing block for this shared memory. 133 /// Offset into the backing block for this shared memory.
122 std::size_t backing_block_offset; 134 std::size_t backing_block_offset = 0;
123 /// Size of the memory block. Page-aligned. 135 /// Size of the memory block. Page-aligned.
124 u64 size; 136 u64 size = 0;
125 /// Permission restrictions applied to the process which created the block. 137 /// Permission restrictions applied to the process which created the block.
126 MemoryPermission permissions; 138 MemoryPermission permissions{};
127 /// Permission restrictions applied to other processes mapping the block. 139 /// Permission restrictions applied to other processes mapping the block.
128 MemoryPermission other_permissions; 140 MemoryPermission other_permissions{};
141 /// Process that created this shared memory block.
142 SharedPtr<Process> owner_process;
143 /// Address of shared memory block in the owner process if specified.
144 VAddr base_address = 0;
129 /// Name of shared memory object. 145 /// Name of shared memory object.
130 std::string name; 146 std::string name;
131
132private:
133 explicit SharedMemory(KernelCore& kernel);
134 ~SharedMemory() override;
135}; 147};
136 148
137} // namespace Kernel 149} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 2e7c9d094..f287f7c97 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -671,7 +671,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
671 break; 671 break;
672 } 672 }
673 default: 673 default:
674 UNIMPLEMENTED(); 674 LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id);
675 return ERR_INVALID_ENUM_VALUE;
675 } 676 }
676 677
677 return RESULT_SUCCESS; 678 return RESULT_SUCCESS;
@@ -736,13 +737,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
736 737
737 const auto* const current_process = Core::CurrentProcess(); 738 const auto* const current_process = Core::CurrentProcess();
738 739
739 // Note: The kernel uses the current process's resource limit instead of
740 // the one from the thread owner's resource limit.
741 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
742 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
743 return ERR_INVALID_THREAD_PRIORITY;
744 }
745
746 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); 740 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
747 if (!thread) { 741 if (!thread) {
748 return ERR_INVALID_HANDLE; 742 return ERR_INVALID_HANDLE;
@@ -796,7 +790,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
796 return ERR_INVALID_MEMORY_RANGE; 790 return ERR_INVALID_MEMORY_RANGE;
797 } 791 }
798 792
799 return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare); 793 return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare);
800} 794}
801 795
802static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 796static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
@@ -826,7 +820,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
826 return ERR_INVALID_MEMORY_RANGE; 820 return ERR_INVALID_MEMORY_RANGE;
827 } 821 }
828 822
829 return shared_memory->Unmap(current_process, addr); 823 return shared_memory->Unmap(*current_process, addr);
830} 824}
831 825
832/// Query process memory 826/// Query process memory
@@ -885,10 +879,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
885 } 879 }
886 880
887 auto* const current_process = Core::CurrentProcess(); 881 auto* const current_process = Core::CurrentProcess();
888 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
889 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
890 return ERR_INVALID_THREAD_PRIORITY;
891 }
892 882
893 if (processor_id == THREADPROCESSORID_DEFAULT) { 883 if (processor_id == THREADPROCESSORID_DEFAULT) {
894 // Set the target CPU to the one specified in the process' exheader. 884 // Set the target CPU to the one specified in the process' exheader.
@@ -1181,7 +1171,7 @@ static ResultCode CloseHandle(Handle handle) {
1181 1171
1182/// Reset an event 1172/// Reset an event
1183static ResultCode ResetSignal(Handle handle) { 1173static ResultCode ResetSignal(Handle handle) {
1184 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1174 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
1185 1175
1186 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1176 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1187 auto event = handle_table.Get<Event>(handle); 1177 auto event = handle_table.Get<Event>(handle);
@@ -1194,9 +1184,39 @@ static ResultCode ResetSignal(Handle handle) {
1194 1184
1195/// Creates a TransferMemory object 1185/// Creates a TransferMemory object
1196static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { 1186static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
1197 LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, 1187 LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
1198 permissions); 1188 permissions);
1199 *handle = 0; 1189
1190 if (!Common::Is4KBAligned(addr)) {
1191 LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
1192 return ERR_INVALID_ADDRESS;
1193 }
1194
1195 if (!Common::Is4KBAligned(size) || size == 0) {
1196 LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
1197 return ERR_INVALID_ADDRESS;
1198 }
1199
1200 if (!IsValidAddressRange(addr, size)) {
1201 LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
1202 addr, size);
1203 return ERR_INVALID_ADDRESS_STATE;
1204 }
1205
1206 const auto perms = static_cast<MemoryPermission>(permissions);
1207 if (perms != MemoryPermission::None && perms != MemoryPermission::Read &&
1208 perms != MemoryPermission::ReadWrite) {
1209 LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
1210 permissions);
1211 return ERR_INVALID_MEMORY_PERMISSIONS;
1212 }
1213
1214 auto& kernel = Core::System::GetInstance().Kernel();
1215 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1216 const auto shared_mem_handle = SharedMemory::Create(
1217 kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr);
1218
1219 CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
1200 return RESULT_SUCCESS; 1220 return RESULT_SUCCESS;
1201} 1221}
1202 1222
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index ec7fd6150..100f8f6bf 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -298,7 +298,7 @@ ResultCode VMManager::HeapFree(VAddr target, u64 size) {
298 return RESULT_SUCCESS; 298 return RESULT_SUCCESS;
299} 299}
300 300
301ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 301ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
302 const auto vma = FindVMA(src_addr); 302 const auto vma = FindVMA(src_addr);
303 303
304 ASSERT_MSG(vma != vma_map.end(), "Invalid memory address"); 304 ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
@@ -312,8 +312,8 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
312 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; 312 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
313 const std::size_t backing_block_offset = vma->second.offset + vma_offset; 313 const std::size_t backing_block_offset = vma->second.offset + vma_offset;
314 314
315 CASCADE_RESULT(auto new_vma, MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, 315 CASCADE_RESULT(auto new_vma,
316 MemoryState::Mapped)); 316 MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state));
317 // Protect mirror with permissions from old region 317 // Protect mirror with permissions from old region
318 Reprotect(new_vma, vma->second.permissions); 318 Reprotect(new_vma, vma->second.permissions);
319 // Remove permissions from old region 319 // Remove permissions from old region
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 248cc46dc..d522404fe 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -189,7 +189,8 @@ public:
189 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 189 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
190 ResultCode HeapFree(VAddr target, u64 size); 190 ResultCode HeapFree(VAddr target, u64 size);
191 191
192 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); 192 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
193 MemoryState state = MemoryState::Mapped);
193 194
194 /** 195 /**
195 * Scans all VMAs and updates the page table range of any that use the given vector as backing 196 * Scans all VMAs and updates the page table range of any that use the given vector as backing
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 0477ce66e..4f17b52f9 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -6,14 +6,19 @@
6#include <cinttypes> 6#include <cinttypes>
7#include <cstring> 7#include <cstring>
8#include <stack> 8#include <stack>
9#include "audio_core/audio_renderer.h"
9#include "core/core.h" 10#include "core/core.h"
10#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/event.h" 12#include "core/hle/kernel/event.h"
12#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/shared_memory.h"
13#include "core/hle/service/acc/profile_manager.h" 15#include "core/hle/service/acc/profile_manager.h"
14#include "core/hle/service/am/am.h" 16#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applet_ae.h" 17#include "core/hle/service/am/applet_ae.h"
16#include "core/hle/service/am/applet_oe.h" 18#include "core/hle/service/am/applet_oe.h"
19#include "core/hle/service/am/applets/applets.h"
20#include "core/hle/service/am/applets/software_keyboard.h"
21#include "core/hle/service/am/applets/stub_applet.h"
17#include "core/hle/service/am/idle.h" 22#include "core/hle/service/am/idle.h"
18#include "core/hle/service/am/omm.h" 23#include "core/hle/service/am/omm.h"
19#include "core/hle/service/am/spsm.h" 24#include "core/hle/service/am/spsm.h"
@@ -28,6 +33,13 @@
28 33
29namespace Service::AM { 34namespace Service::AM {
30 35
36constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
37constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
38
39enum class AppletId : u32 {
40 SoftwareKeyboard = 0x11,
41};
42
31constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; 43constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
32 44
33struct LaunchParameters { 45struct LaunchParameters {
@@ -203,8 +215,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
203ISelfController::~ISelfController() = default; 215ISelfController::~ISelfController() = default;
204 216
205void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 217void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
206 // Takes 3 input u8s with each field located immediately after the previous u8, these are 218 // Takes 3 input u8s with each field located immediately after the previous
207 // bool flags. No output. 219 // u8, these are bool flags. No output.
208 220
209 IPC::RequestParser rp{ctx}; 221 IPC::RequestParser rp{ctx};
210 222
@@ -258,8 +270,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
258} 270}
259 271
260void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { 272void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
261 // Takes 3 input u8s with each field located immediately after the previous u8, these are 273 // Takes 3 input u8s with each field located immediately after the previous
262 // bool flags. No output. 274 // u8, these are bool flags. No output.
263 IPC::RequestParser rp{ctx}; 275 IPC::RequestParser rp{ctx};
264 276
265 bool enabled = rp.Pop<bool>(); 277 bool enabled = rp.Pop<bool>();
@@ -302,8 +314,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
302} 314}
303 315
304void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { 316void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
305 // TODO(Subv): Find out how AM determines the display to use, for now just create the layer 317 // TODO(Subv): Find out how AM determines the display to use, for now just
306 // in the Default display. 318 // create the layer in the Default display.
307 u64 display_id = nvflinger->OpenDisplay("Default"); 319 u64 display_id = nvflinger->OpenDisplay("Default");
308 u64 layer_id = nvflinger->CreateLayer(display_id); 320 u64 layer_id = nvflinger->CreateLayer(display_id);
309 321
@@ -471,16 +483,38 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
471 rb.Push(RESULT_SUCCESS); 483 rb.Push(RESULT_SUCCESS);
472 484
473 if (Settings::values.use_docked_mode) { 485 if (Settings::values.use_docked_mode) {
474 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 486 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
475 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); 487 static_cast<u32>(Settings::values.resolution_factor));
488 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
489 static_cast<u32>(Settings::values.resolution_factor));
476 } else { 490 } else {
477 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); 491 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
478 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); 492 static_cast<u32>(Settings::values.resolution_factor));
493 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
494 static_cast<u32>(Settings::values.resolution_factor));
479 } 495 }
480 496
481 LOG_DEBUG(Service_AM, "called"); 497 LOG_DEBUG(Service_AM, "called");
482} 498}
483 499
500IStorage::IStorage(std::vector<u8> buffer)
501 : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
502 // clang-format off
503 static const FunctionInfo functions[] = {
504 {0, &IStorage::Open, "Open"},
505 {1, nullptr, "OpenTransferStorage"},
506 };
507 // clang-format on
508
509 RegisterHandlers(functions);
510}
511
512IStorage::~IStorage() = default;
513
514const std::vector<u8>& IStorage::GetData() const {
515 return buffer;
516}
517
484void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { 518void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
485 const bool use_docked_mode{Settings::values.use_docked_mode}; 519 const bool use_docked_mode{Settings::values.use_docked_mode};
486 IPC::ResponseBuilder rb{ctx, 3}; 520 IPC::ResponseBuilder rb{ctx, 3};
@@ -500,15 +534,30 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
500 LOG_DEBUG(Service_AM, "called"); 534 LOG_DEBUG(Service_AM, "called");
501} 535}
502 536
503class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { 537class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
504public: 538public:
505 explicit IStorageAccessor(std::vector<u8> buffer) 539 explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet)
506 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { 540 : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) {
507 // clang-format off 541 // clang-format off
508 static const FunctionInfo functions[] = { 542 static const FunctionInfo functions[] = {
509 {0, &IStorageAccessor::GetSize, "GetSize"}, 543 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
510 {10, &IStorageAccessor::Write, "Write"}, 544 {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
511 {11, &IStorageAccessor::Read, "Read"}, 545 {10, &ILibraryAppletAccessor::Start, "Start"},
546 {20, nullptr, "RequestExit"},
547 {25, nullptr, "Terminate"},
548 {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
549 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
550 {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
551 {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
552 {102, nullptr, "PushExtraStorage"},
553 {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
554 {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
555 {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
556 {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
557 {110, nullptr, "NeedsToExitProcess"},
558 {120, nullptr, "GetLibraryAppletInfo"},
559 {150, nullptr, "RequestForAppletToGetForeground"},
560 {160, nullptr, "GetIndirectLayerConsumerHandle"},
512 }; 561 };
513 // clang-format on 562 // clang-format on
514 563
@@ -516,158 +565,187 @@ public:
516 } 565 }
517 566
518private: 567private:
519 std::vector<u8> buffer; 568 void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
569 const auto event = applet->GetBroker().GetStateChangedEvent();
570 event->Signal();
520 571
521 void GetSize(Kernel::HLERequestContext& ctx) { 572 IPC::ResponseBuilder rb{ctx, 2, 1};
522 IPC::ResponseBuilder rb{ctx, 4}; 573 rb.Push(RESULT_SUCCESS);
574 rb.PushCopyObjects(event);
575
576 LOG_DEBUG(Service_AM, "called");
577 }
523 578
579 void IsCompleted(Kernel::HLERequestContext& ctx) {
580 IPC::ResponseBuilder rb{ctx, 3};
524 rb.Push(RESULT_SUCCESS); 581 rb.Push(RESULT_SUCCESS);
525 rb.Push(static_cast<u64>(buffer.size())); 582 rb.Push<u32>(applet->TransactionComplete());
526 583
527 LOG_DEBUG(Service_AM, "called"); 584 LOG_DEBUG(Service_AM, "called");
528 } 585 }
529 586
530 void Write(Kernel::HLERequestContext& ctx) { 587 void GetResult(Kernel::HLERequestContext& ctx) {
531 IPC::RequestParser rp{ctx}; 588 IPC::ResponseBuilder rb{ctx, 2};
589 rb.Push(applet->GetStatus());
532 590
533 const u64 offset{rp.Pop<u64>()}; 591 LOG_DEBUG(Service_AM, "called");
534 const std::vector<u8> data{ctx.ReadBuffer()}; 592 }
535 593
536 ASSERT(offset + data.size() <= buffer.size()); 594 void Start(Kernel::HLERequestContext& ctx) {
595 ASSERT(applet != nullptr);
537 596
538 std::memcpy(&buffer[offset], data.data(), data.size()); 597 applet->Initialize();
598 applet->Execute();
539 599
540 IPC::ResponseBuilder rb{ctx, 2}; 600 IPC::ResponseBuilder rb{ctx, 2};
541 rb.Push(RESULT_SUCCESS); 601 rb.Push(RESULT_SUCCESS);
542 602
543 LOG_DEBUG(Service_AM, "called, offset={}", offset); 603 LOG_DEBUG(Service_AM, "called");
544 } 604 }
545 605
546 void Read(Kernel::HLERequestContext& ctx) { 606 void PushInData(Kernel::HLERequestContext& ctx) {
547 IPC::RequestParser rp{ctx}; 607 IPC::RequestParser rp{ctx};
608 applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
609
610 IPC::ResponseBuilder rb{ctx, 2};
611 rb.Push(RESULT_SUCCESS);
612
613 LOG_DEBUG(Service_AM, "called");
614 }
548 615
549 const u64 offset{rp.Pop<u64>()}; 616 void PopOutData(Kernel::HLERequestContext& ctx) {
550 const std::size_t size{ctx.GetWriteBufferSize()}; 617 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
551 618
552 ASSERT(offset + size <= buffer.size()); 619 const auto storage = applet->GetBroker().PopNormalDataToGame();
620 if (storage == nullptr) {
621 rb.Push(ERR_NO_DATA_IN_CHANNEL);
622 return;
623 }
553 624
554 ctx.WriteBuffer(buffer.data() + offset, size); 625 rb.Push(RESULT_SUCCESS);
626 rb.PushIpcInterface<IStorage>(std::move(*storage));
627
628 LOG_DEBUG(Service_AM, "called");
629 }
630
631 void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
632 IPC::RequestParser rp{ctx};
633 applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
634
635 ASSERT(applet->IsInitialized());
636 applet->ExecuteInteractive();
637 applet->Execute();
555 638
556 IPC::ResponseBuilder rb{ctx, 2}; 639 IPC::ResponseBuilder rb{ctx, 2};
557 rb.Push(RESULT_SUCCESS); 640 rb.Push(RESULT_SUCCESS);
558 641
559 LOG_DEBUG(Service_AM, "called, offset={}", offset); 642 LOG_DEBUG(Service_AM, "called");
560 } 643 }
561};
562 644
563class IStorage final : public ServiceFramework<IStorage> { 645 void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
564public: 646 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
565 explicit IStorage(std::vector<u8> buffer)
566 : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
567 // clang-format off
568 static const FunctionInfo functions[] = {
569 {0, &IStorage::Open, "Open"},
570 {1, nullptr, "OpenTransferStorage"},
571 };
572 // clang-format on
573 647
574 RegisterHandlers(functions); 648 const auto storage = applet->GetBroker().PopInteractiveDataToGame();
649 if (storage == nullptr) {
650 rb.Push(ERR_NO_DATA_IN_CHANNEL);
651 return;
652 }
653
654 rb.Push(RESULT_SUCCESS);
655 rb.PushIpcInterface<IStorage>(std::move(*storage));
656
657 LOG_DEBUG(Service_AM, "called");
575 } 658 }
576 659
577private: 660 void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
578 std::vector<u8> buffer; 661 IPC::ResponseBuilder rb{ctx, 2, 1};
662 rb.Push(RESULT_SUCCESS);
663 rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent());
579 664
580 void Open(Kernel::HLERequestContext& ctx) { 665 LOG_DEBUG(Service_AM, "called");
581 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 666 }
582 667
668 void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
669 IPC::ResponseBuilder rb{ctx, 2, 1};
583 rb.Push(RESULT_SUCCESS); 670 rb.Push(RESULT_SUCCESS);
584 rb.PushIpcInterface<AM::IStorageAccessor>(buffer); 671 rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
585 672
586 LOG_DEBUG(Service_AM, "called"); 673 LOG_DEBUG(Service_AM, "called");
587 } 674 }
675
676 std::shared_ptr<Applets::Applet> applet;
588}; 677};
589 678
590class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { 679void IStorage::Open(Kernel::HLERequestContext& ctx) {
591public: 680 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
592 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { 681
593 // clang-format off 682 rb.Push(RESULT_SUCCESS);
683 rb.PushIpcInterface<IStorageAccessor>(*this);
684
685 LOG_DEBUG(Service_AM, "called");
686}
687
688IStorageAccessor::IStorageAccessor(IStorage& storage)
689 : ServiceFramework("IStorageAccessor"), backing(storage) {
690 // clang-format off
594 static const FunctionInfo functions[] = { 691 static const FunctionInfo functions[] = {
595 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, 692 {0, &IStorageAccessor::GetSize, "GetSize"},
596 {1, nullptr, "IsCompleted"}, 693 {10, &IStorageAccessor::Write, "Write"},
597 {10, &ILibraryAppletAccessor::Start, "Start"}, 694 {11, &IStorageAccessor::Read, "Read"},
598 {20, nullptr, "RequestExit"},
599 {25, nullptr, "Terminate"},
600 {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
601 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
602 {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
603 {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
604 {102, nullptr, "PushExtraStorage"},
605 {103, nullptr, "PushInteractiveInData"},
606 {104, nullptr, "PopInteractiveOutData"},
607 {105, nullptr, "GetPopOutDataEvent"},
608 {106, nullptr, "GetPopInteractiveOutDataEvent"},
609 {110, nullptr, "NeedsToExitProcess"},
610 {120, nullptr, "GetLibraryAppletInfo"},
611 {150, nullptr, "RequestForAppletToGetForeground"},
612 {160, nullptr, "GetIndirectLayerConsumerHandle"},
613 }; 695 };
614 // clang-format on 696 // clang-format on
615 697
616 RegisterHandlers(functions); 698 RegisterHandlers(functions);
699}
617 700
618 auto& kernel = Core::System::GetInstance().Kernel(); 701IStorageAccessor::~IStorageAccessor() = default;
619 state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
620 "ILibraryAppletAccessor:StateChangedEvent");
621 }
622 702
623private: 703void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
624 void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { 704 IPC::ResponseBuilder rb{ctx, 4};
625 state_changed_event->Signal();
626 705
627 IPC::ResponseBuilder rb{ctx, 2, 1}; 706 rb.Push(RESULT_SUCCESS);
628 rb.Push(RESULT_SUCCESS); 707 rb.Push(static_cast<u64>(backing.buffer.size()));
629 rb.PushCopyObjects(state_changed_event);
630 708
631 LOG_WARNING(Service_AM, "(STUBBED) called"); 709 LOG_DEBUG(Service_AM, "called");
632 } 710}
633 711
634 void GetResult(Kernel::HLERequestContext& ctx) { 712void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
635 IPC::ResponseBuilder rb{ctx, 2}; 713 IPC::RequestParser rp{ctx};
636 rb.Push(RESULT_SUCCESS);
637 714
638 LOG_WARNING(Service_AM, "(STUBBED) called"); 715 const u64 offset{rp.Pop<u64>()};
639 } 716 const std::vector<u8> data{ctx.ReadBuffer()};
640 717
641 void Start(Kernel::HLERequestContext& ctx) { 718 if (data.size() > backing.buffer.size() - offset) {
642 IPC::ResponseBuilder rb{ctx, 2}; 719 IPC::ResponseBuilder rb{ctx, 2};
643 rb.Push(RESULT_SUCCESS); 720 rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
644
645 LOG_WARNING(Service_AM, "(STUBBED) called");
646 } 721 }
647 722
648 void PushInData(Kernel::HLERequestContext& ctx) { 723 std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
649 IPC::RequestParser rp{ctx};
650 storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
651 724
652 IPC::ResponseBuilder rb{ctx, 2}; 725 IPC::ResponseBuilder rb{ctx, 2};
653 rb.Push(RESULT_SUCCESS); 726 rb.Push(RESULT_SUCCESS);
654 727
655 LOG_DEBUG(Service_AM, "called"); 728 LOG_DEBUG(Service_AM, "called, offset={}", offset);
656 } 729}
657 730
658 void PopOutData(Kernel::HLERequestContext& ctx) { 731void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
659 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 732 IPC::RequestParser rp{ctx};
660 rb.Push(RESULT_SUCCESS);
661 rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
662 733
663 storage_stack.pop(); 734 const u64 offset{rp.Pop<u64>()};
735 const std::size_t size{ctx.GetWriteBufferSize()};
664 736
665 LOG_DEBUG(Service_AM, "called"); 737 if (size > backing.buffer.size() - offset) {
738 IPC::ResponseBuilder rb{ctx, 2};
739 rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
666 } 740 }
667 741
668 std::stack<std::shared_ptr<AM::IStorage>> storage_stack; 742 ctx.WriteBuffer(backing.buffer.data() + offset, size);
669 Kernel::SharedPtr<Kernel::Event> state_changed_event; 743
670}; 744 IPC::ResponseBuilder rb{ctx, 2};
745 rb.Push(RESULT_SUCCESS);
746
747 LOG_DEBUG(Service_AM, "called, offset={}", offset);
748}
671 749
672ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { 750ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
673 static const FunctionInfo functions[] = { 751 static const FunctionInfo functions[] = {
@@ -675,7 +753,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
675 {1, nullptr, "TerminateAllLibraryApplets"}, 753 {1, nullptr, "TerminateAllLibraryApplets"},
676 {2, nullptr, "AreAnyLibraryAppletsLeft"}, 754 {2, nullptr, "AreAnyLibraryAppletsLeft"},
677 {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, 755 {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
678 {11, nullptr, "CreateTransferMemoryStorage"}, 756 {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
679 {12, nullptr, "CreateHandleStorage"}, 757 {12, nullptr, "CreateHandleStorage"},
680 }; 758 };
681 RegisterHandlers(functions); 759 RegisterHandlers(functions);
@@ -683,11 +761,37 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
683 761
684ILibraryAppletCreator::~ILibraryAppletCreator() = default; 762ILibraryAppletCreator::~ILibraryAppletCreator() = default;
685 763
764static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
765 switch (id) {
766 case AppletId::SoftwareKeyboard:
767 return std::make_shared<Applets::SoftwareKeyboard>();
768 default:
769 LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
770 static_cast<u32>(id));
771 return std::make_shared<Applets::StubApplet>();
772 }
773}
774
686void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { 775void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
776 IPC::RequestParser rp{ctx};
777 const auto applet_id = rp.PopRaw<AppletId>();
778 const auto applet_mode = rp.PopRaw<u32>();
779
780 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
781 static_cast<u32>(applet_id), applet_mode);
782
783 const auto applet = GetAppletFromId(applet_id);
784
785 if (applet == nullptr) {
786 IPC::ResponseBuilder rb{ctx, 2};
787 rb.Push(ResultCode(-1));
788 return;
789 }
790
687 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 791 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
688 792
689 rb.Push(RESULT_SUCCESS); 793 rb.Push(RESULT_SUCCESS);
690 rb.PushIpcInterface<AM::ILibraryAppletAccessor>(); 794 rb.PushIpcInterface<AM::ILibraryAppletAccessor>(applet);
691 795
692 LOG_DEBUG(Service_AM, "called"); 796 LOG_DEBUG(Service_AM, "called");
693} 797}
@@ -704,6 +808,31 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
704 LOG_DEBUG(Service_AM, "called, size={}", size); 808 LOG_DEBUG(Service_AM, "called, size={}", size);
705} 809}
706 810
811void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
812 IPC::RequestParser rp{ctx};
813
814 rp.SetCurrentOffset(3);
815 const auto handle{rp.Pop<Kernel::Handle>()};
816
817 const auto shared_mem =
818 Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>(
819 handle);
820
821 if (shared_mem == nullptr) {
822 IPC::ResponseBuilder rb{ctx, 2};
823 rb.Push(ResultCode(-1));
824 return;
825 }
826
827 const u8* mem_begin = shared_mem->GetPointer();
828 const u8* mem_end = mem_begin + shared_mem->GetSize();
829 std::vector<u8> memory{mem_begin, mem_end};
830
831 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
832 rb.Push(RESULT_SUCCESS);
833 rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
834}
835
707IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { 836IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
708 // clang-format off 837 // clang-format off
709 static const FunctionInfo functions[] = { 838 static const FunctionInfo functions[] = {
@@ -733,7 +862,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
733 {70, nullptr, "RequestToShutdown"}, 862 {70, nullptr, "RequestToShutdown"},
734 {71, nullptr, "RequestToReboot"}, 863 {71, nullptr, "RequestToReboot"},
735 {80, nullptr, "ExitAndRequestToShowThanksMessage"}, 864 {80, nullptr, "ExitAndRequestToShowThanksMessage"},
736 {90, nullptr, "EnableApplicationCrashReport"}, 865 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
737 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"}, 866 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
738 {101, nullptr, "SetApplicationCopyrightImage"}, 867 {101, nullptr, "SetApplicationCopyrightImage"},
739 {102, nullptr, "SetApplicationCopyrightVisibility"}, 868 {102, nullptr, "SetApplicationCopyrightVisibility"},
@@ -752,6 +881,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
752 881
753IApplicationFunctions::~IApplicationFunctions() = default; 882IApplicationFunctions::~IApplicationFunctions() = default;
754 883
884void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
885 IPC::ResponseBuilder rb{ctx, 2};
886 rb.Push(RESULT_SUCCESS);
887 LOG_WARNING(Service_AM, "(STUBBED) called");
888}
889
755void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed( 890void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
756 Kernel::HLERequestContext& ctx) { 891 Kernel::HLERequestContext& ctx) {
757 IPC::ResponseBuilder rb{ctx, 2}; 892 IPC::ResponseBuilder rb{ctx, 2};
@@ -821,7 +956,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
821 956
822void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { 957void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
823 // Takes an input u32 Result, no output. 958 // Takes an input u32 Result, no output.
824 // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. 959 // For example, in some cases official apps use this with error 0x2A2 then
960 // uses svcBreak.
825 961
826 IPC::RequestParser rp{ctx}; 962 IPC::RequestParser rp{ctx};
827 u32 result = rp.Pop<u32>(); 963 u32 result = rp.Pop<u32>();
@@ -884,8 +1020,8 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
884void InstallInterfaces(SM::ServiceManager& service_manager, 1020void InstallInterfaces(SM::ServiceManager& service_manager,
885 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { 1021 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
886 auto message_queue = std::make_shared<AppletMessageQueue>(); 1022 auto message_queue = std::make_shared<AppletMessageQueue>();
887 message_queue->PushMessage( 1023 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
888 AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on game boot 1024 // game boot
889 1025
890 std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager); 1026 std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
891 std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager); 1027 std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 2f1c20bce..44c1bcde5 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -155,6 +155,34 @@ private:
155 std::shared_ptr<AppletMessageQueue> msg_queue; 155 std::shared_ptr<AppletMessageQueue> msg_queue;
156}; 156};
157 157
158class IStorage final : public ServiceFramework<IStorage> {
159public:
160 explicit IStorage(std::vector<u8> buffer);
161 ~IStorage() override;
162
163 const std::vector<u8>& GetData() const;
164
165private:
166 void Open(Kernel::HLERequestContext& ctx);
167
168 std::vector<u8> buffer;
169
170 friend class IStorageAccessor;
171};
172
173class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
174public:
175 explicit IStorageAccessor(IStorage& backing);
176 ~IStorageAccessor() override;
177
178private:
179 void GetSize(Kernel::HLERequestContext& ctx);
180 void Write(Kernel::HLERequestContext& ctx);
181 void Read(Kernel::HLERequestContext& ctx);
182
183 IStorage& backing;
184};
185
158class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { 186class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
159public: 187public:
160 ILibraryAppletCreator(); 188 ILibraryAppletCreator();
@@ -163,6 +191,7 @@ public:
163private: 191private:
164 void CreateLibraryApplet(Kernel::HLERequestContext& ctx); 192 void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
165 void CreateStorage(Kernel::HLERequestContext& ctx); 193 void CreateStorage(Kernel::HLERequestContext& ctx);
194 void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
166}; 195};
167 196
168class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { 197class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@@ -185,6 +214,7 @@ private:
185 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); 214 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
186 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); 215 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
187 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); 216 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
217 void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
188}; 218};
189 219
190class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 220class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
new file mode 100644
index 000000000..becbadd06
--- /dev/null
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -0,0 +1,113 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/assert.h"
7#include "core/core.h"
8#include "core/hle/kernel/event.h"
9#include "core/hle/kernel/server_port.h"
10#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/applets.h"
12
13namespace Service::AM::Applets {
14
15AppletDataBroker::AppletDataBroker() {
16 auto& kernel = Core::System::GetInstance().Kernel();
17 state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
18 "ILibraryAppletAccessor:StateChangedEvent");
19 pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
20 "ILibraryAppletAccessor:PopDataOutEvent");
21 pop_interactive_out_data_event = Kernel::Event::Create(
22 kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
23}
24
25AppletDataBroker::~AppletDataBroker() = default;
26
27std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
28 if (out_channel.empty())
29 return nullptr;
30
31 auto out = std::move(out_channel.front());
32 out_channel.pop();
33 return out;
34}
35
36std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
37 if (in_channel.empty())
38 return nullptr;
39
40 auto out = std::move(in_channel.front());
41 in_channel.pop();
42 return out;
43}
44
45std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
46 if (out_interactive_channel.empty())
47 return nullptr;
48
49 auto out = std::move(out_interactive_channel.front());
50 out_interactive_channel.pop();
51 return out;
52}
53
54std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
55 if (in_interactive_channel.empty())
56 return nullptr;
57
58 auto out = std::move(in_interactive_channel.front());
59 in_interactive_channel.pop();
60 return out;
61}
62
63void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
64 in_channel.push(std::make_unique<IStorage>(storage));
65}
66
67void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
68 out_channel.push(std::make_unique<IStorage>(storage));
69 pop_out_data_event->Signal();
70}
71
72void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
73 in_interactive_channel.push(std::make_unique<IStorage>(storage));
74}
75
76void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
77 out_interactive_channel.push(std::make_unique<IStorage>(storage));
78 pop_interactive_out_data_event->Signal();
79}
80
81void AppletDataBroker::SignalStateChanged() const {
82 state_changed_event->Signal();
83}
84
85Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const {
86 return pop_out_data_event;
87}
88
89Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const {
90 return pop_interactive_out_data_event;
91}
92
93Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const {
94 return state_changed_event;
95}
96
97Applet::Applet() = default;
98
99Applet::~Applet() = default;
100
101void Applet::Initialize() {
102 const auto common = broker.PopNormalDataToApplet();
103 ASSERT(common != nullptr);
104
105 const auto common_data = common->GetData();
106
107 ASSERT(common_data.size() >= sizeof(CommonArguments));
108 std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments));
109
110 initialized = true;
111}
112
113} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
new file mode 100644
index 000000000..f65ea119c
--- /dev/null
+++ b/src/core/hle/service/am/applets/applets.h
@@ -0,0 +1,112 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <queue>
9#include "common/swap.h"
10#include "core/hle/kernel/kernel.h"
11
12union ResultCode;
13
14namespace Kernel {
15class Event;
16}
17
18namespace Service::AM {
19
20class IStorage;
21
22namespace Applets {
23
24class AppletDataBroker final {
25public:
26 AppletDataBroker();
27 ~AppletDataBroker();
28
29 std::unique_ptr<IStorage> PopNormalDataToGame();
30 std::unique_ptr<IStorage> PopNormalDataToApplet();
31
32 std::unique_ptr<IStorage> PopInteractiveDataToGame();
33 std::unique_ptr<IStorage> PopInteractiveDataToApplet();
34
35 void PushNormalDataFromGame(IStorage storage);
36 void PushNormalDataFromApplet(IStorage storage);
37
38 void PushInteractiveDataFromGame(IStorage storage);
39 void PushInteractiveDataFromApplet(IStorage storage);
40
41 void SignalStateChanged() const;
42
43 Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const;
44 Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const;
45 Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const;
46
47private:
48 // Queues are named from applet's perspective
49
50 // PopNormalDataToApplet and PushNormalDataFromGame
51 std::queue<std::unique_ptr<IStorage>> in_channel;
52
53 // PopNormalDataToGame and PushNormalDataFromApplet
54 std::queue<std::unique_ptr<IStorage>> out_channel;
55
56 // PopInteractiveDataToApplet and PushInteractiveDataFromGame
57 std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
58
59 // PopInteractiveDataToGame and PushInteractiveDataFromApplet
60 std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
61
62 Kernel::SharedPtr<Kernel::Event> state_changed_event;
63
64 // Signaled on PushNormalDataFromApplet
65 Kernel::SharedPtr<Kernel::Event> pop_out_data_event;
66
67 // Signaled on PushInteractiveDataFromApplet
68 Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event;
69};
70
71class Applet {
72public:
73 Applet();
74 virtual ~Applet();
75
76 virtual void Initialize();
77
78 virtual bool TransactionComplete() const = 0;
79 virtual ResultCode GetStatus() const = 0;
80 virtual void ExecuteInteractive() = 0;
81 virtual void Execute() = 0;
82
83 bool IsInitialized() const {
84 return initialized;
85 }
86
87 AppletDataBroker& GetBroker() {
88 return broker;
89 }
90
91 const AppletDataBroker& GetBroker() const {
92 return broker;
93 }
94
95protected:
96 struct CommonArguments {
97 u32_le arguments_version;
98 u32_le size;
99 u32_le library_version;
100 u32_le theme_color;
101 u8 play_startup_sound;
102 u64_le system_tick;
103 };
104 static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
105
106 CommonArguments common_args{};
107 AppletDataBroker broker;
108 bool initialized = false;
109};
110
111} // namespace Applets
112} // namespace Service::AM
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
new file mode 100644
index 000000000..981bdec51
--- /dev/null
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -0,0 +1,161 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/assert.h"
7#include "common/string_util.h"
8#include "core/core.h"
9#include "core/frontend/applets/software_keyboard.h"
10#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/software_keyboard.h"
12
13namespace Service::AM::Applets {
14
15constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
16constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
17constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
18constexpr bool INTERACTIVE_STATUS_OK = false;
19
20static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
21 KeyboardConfig config, std::u16string initial_text) {
22 Core::Frontend::SoftwareKeyboardParameters params{};
23
24 params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
25 config.submit_text.data(), config.submit_text.size());
26 params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
27 config.header_text.data(), config.header_text.size());
28 params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(),
29 config.sub_text.size());
30 params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
31 config.guide_text.size());
32 params.initial_text = initial_text;
33 params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
34 params.password = static_cast<bool>(config.is_password);
35 params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
36 params.value = static_cast<u8>(config.keyset_disable_bitmask);
37
38 return params;
39}
40
41SoftwareKeyboard::SoftwareKeyboard() = default;
42
43SoftwareKeyboard::~SoftwareKeyboard() = default;
44
45void SoftwareKeyboard::Initialize() {
46 complete = false;
47 initial_text.clear();
48 final_data.clear();
49
50 Applet::Initialize();
51
52 const auto keyboard_config_storage = broker.PopNormalDataToApplet();
53 ASSERT(keyboard_config_storage != nullptr);
54 const auto& keyboard_config = keyboard_config_storage->GetData();
55
56 ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
57 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
58
59 const auto work_buffer_storage = broker.PopNormalDataToApplet();
60 ASSERT(work_buffer_storage != nullptr);
61 const auto& work_buffer = work_buffer_storage->GetData();
62
63 if (config.initial_string_size == 0)
64 return;
65
66 std::vector<char16_t> string(config.initial_string_size);
67 std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset,
68 string.size() * 2);
69 initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size());
70}
71
72bool SoftwareKeyboard::TransactionComplete() const {
73 return complete;
74}
75
76ResultCode SoftwareKeyboard::GetStatus() const {
77 return RESULT_SUCCESS;
78}
79
80void SoftwareKeyboard::ExecuteInteractive() {
81 if (complete)
82 return;
83
84 const auto storage = broker.PopInteractiveDataToApplet();
85 ASSERT(storage != nullptr);
86 const auto data = storage->GetData();
87 const auto status = static_cast<bool>(data[0]);
88
89 if (status == INTERACTIVE_STATUS_OK) {
90 complete = true;
91 } else {
92 const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
93
94 std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
95 std::memcpy(string.data(), data.data() + 4, string.size() * 2);
96 frontend.SendTextCheckDialog(
97 Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
98 [this] { broker.SignalStateChanged(); });
99 }
100}
101
102void SoftwareKeyboard::Execute() {
103 if (complete) {
104 broker.PushNormalDataFromApplet(IStorage{final_data});
105 return;
106 }
107
108 const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
109
110 const auto parameters = ConvertToFrontendParameters(config, initial_text);
111
112 frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
113 parameters);
114}
115
116void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
117 std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE);
118
119 if (text.has_value()) {
120 std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
121
122 if (config.utf_8) {
123 const u64 size = text->size() + 8;
124 const auto new_text = Common::UTF16ToUTF8(*text);
125
126 std::memcpy(output_sub.data(), &size, sizeof(u64));
127 std::memcpy(output_sub.data() + 8, new_text.data(),
128 std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8));
129
130 output_main[0] = INTERACTIVE_STATUS_OK;
131 std::memcpy(output_main.data() + 4, new_text.data(),
132 std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
133 } else {
134 const u64 size = text->size() * 2 + 8;
135 std::memcpy(output_sub.data(), &size, sizeof(u64));
136 std::memcpy(output_sub.data() + 8, text->data(),
137 std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
138
139 output_main[0] = INTERACTIVE_STATUS_OK;
140 std::memcpy(output_main.data() + 4, text->data(),
141 std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4));
142 }
143
144 complete = !config.text_check;
145 final_data = output_main;
146
147 if (complete) {
148 broker.PushNormalDataFromApplet(IStorage{output_main});
149 } else {
150 broker.PushInteractiveDataFromApplet(IStorage{output_sub});
151 }
152
153 broker.SignalStateChanged();
154 } else {
155 output_main[0] = 1;
156 complete = true;
157 broker.PushNormalDataFromApplet(IStorage{output_main});
158 broker.SignalStateChanged();
159 }
160}
161} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
new file mode 100644
index 000000000..efd5753a1
--- /dev/null
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -0,0 +1,74 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <string>
9#include <vector>
10
11#include "common/common_funcs.h"
12#include "common/swap.h"
13#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applets/applets.h"
15
16namespace Service::AM::Applets {
17
18enum class KeysetDisable : u32 {
19 Space = 0x02,
20 Address = 0x04,
21 Percent = 0x08,
22 Slashes = 0x10,
23 Numbers = 0x40,
24 DownloadCode = 0x80,
25};
26
27struct KeyboardConfig {
28 INSERT_PADDING_BYTES(4);
29 std::array<char16_t, 9> submit_text;
30 u16_le left_symbol_key;
31 u16_le right_symbol_key;
32 INSERT_PADDING_BYTES(1);
33 KeysetDisable keyset_disable_bitmask;
34 u32_le initial_cursor_position;
35 std::array<char16_t, 65> header_text;
36 std::array<char16_t, 129> sub_text;
37 std::array<char16_t, 257> guide_text;
38 u32_le length_limit;
39 INSERT_PADDING_BYTES(4);
40 u32_le is_password;
41 INSERT_PADDING_BYTES(5);
42 bool utf_8;
43 bool draw_background;
44 u32_le initial_string_offset;
45 u32_le initial_string_size;
46 u32_le user_dictionary_offset;
47 u32_le user_dictionary_size;
48 bool text_check;
49 u64_le text_check_callback;
50};
51static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
52
53class SoftwareKeyboard final : public Applet {
54public:
55 SoftwareKeyboard();
56 ~SoftwareKeyboard() override;
57
58 void Initialize() override;
59
60 bool TransactionComplete() const override;
61 ResultCode GetStatus() const override;
62 void ExecuteInteractive() override;
63 void Execute() override;
64
65 void WriteText(std::optional<std::u16string> text);
66
67private:
68 KeyboardConfig config;
69 std::u16string initial_text;
70 bool complete = false;
71 std::vector<u8> final_data;
72};
73
74} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.cpp b/src/core/hle/service/am/applets/stub_applet.cpp
new file mode 100644
index 000000000..ed166b87d
--- /dev/null
+++ b/src/core/hle/service/am/applets/stub_applet.cpp
@@ -0,0 +1,70 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6
7#include "common/hex_util.h"
8#include "common/logging/log.h"
9#include "core/hle/result.h"
10#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/stub_applet.h"
12
13namespace Service::AM::Applets {
14
15static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
16 std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
17 for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
18 const auto data = storage->GetData();
19 LOG_INFO(Service_AM,
20 "called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
21 prefix, data.size(), Common::HexVectorToString(data));
22 }
23
24 storage = broker.PopInteractiveDataToApplet();
25 for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
26 const auto data = storage->GetData();
27 LOG_INFO(Service_AM,
28 "called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
29 prefix, data.size(), Common::HexVectorToString(data));
30 }
31}
32
33StubApplet::StubApplet() = default;
34
35StubApplet::~StubApplet() = default;
36
37void StubApplet::Initialize() {
38 LOG_WARNING(Service_AM, "called (STUBBED)");
39 Applet::Initialize();
40 LogCurrentStorage(broker, "Initialize");
41}
42
43bool StubApplet::TransactionComplete() const {
44 LOG_WARNING(Service_AM, "called (STUBBED)");
45 return true;
46}
47
48ResultCode StubApplet::GetStatus() const {
49 LOG_WARNING(Service_AM, "called (STUBBED)");
50 return RESULT_SUCCESS;
51}
52
53void StubApplet::ExecuteInteractive() {
54 LOG_WARNING(Service_AM, "called (STUBBED)");
55 LogCurrentStorage(broker, "ExecuteInteractive");
56
57 broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
58 broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
59 broker.SignalStateChanged();
60}
61
62void StubApplet::Execute() {
63 LOG_WARNING(Service_AM, "called (STUBBED)");
64 LogCurrentStorage(broker, "Execute");
65
66 broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
67 broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
68 broker.SignalStateChanged();
69}
70} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.h b/src/core/hle/service/am/applets/stub_applet.h
new file mode 100644
index 000000000..7d8dc968d
--- /dev/null
+++ b/src/core/hle/service/am/applets/stub_applet.h
@@ -0,0 +1,24 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/am/applets/applets.h"
8
9namespace Service::AM::Applets {
10
11class StubApplet final : public Applet {
12public:
13 StubApplet();
14 ~StubApplet() override;
15
16 void Initialize() override;
17
18 bool TransactionComplete() const override;
19 ResultCode GetStatus() const override;
20 void ExecuteInteractive() override;
21 void Execute() override;
22};
23
24} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index ff1edefbb..23e1f1165 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -44,8 +44,10 @@ enum class AudioState : u32 {
44 44
45class IAudioOut final : public ServiceFramework<IAudioOut> { 45class IAudioOut final : public ServiceFramework<IAudioOut> {
46public: 46public:
47 IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) 47 IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name,
48 : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params) { 48 std::string&& unique_name)
49 : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params),
50 device_name(std::move(device_name)) {
49 51
50 static const FunctionInfo functions[] = { 52 static const FunctionInfo functions[] = {
51 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 53 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
@@ -69,7 +71,7 @@ public:
69 Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); 71 Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
70 72
71 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, 73 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
72 "IAudioOut", [=]() { buffer_event->Signal(); }); 74 std::move(unique_name), [=]() { buffer_event->Signal(); });
73 } 75 }
74 76
75private: 77private:
@@ -177,6 +179,7 @@ private:
177 179
178 AudioCore::AudioOut& audio_core; 180 AudioCore::AudioOut& audio_core;
179 AudioCore::StreamPtr stream; 181 AudioCore::StreamPtr stream;
182 std::string device_name;
180 183
181 AudoutParams audio_params{}; 184 AudoutParams audio_params{};
182 185
@@ -199,7 +202,15 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
199void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { 202void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
200 LOG_DEBUG(Service_Audio, "called"); 203 LOG_DEBUG(Service_Audio, "called");
201 204
202 ctx.WriteBuffer(DefaultDevice); 205 const auto device_name_data{ctx.ReadBuffer()};
206 std::string device_name;
207 if (device_name_data[0] != '\0') {
208 device_name.assign(device_name_data.begin(), device_name_data.end());
209 } else {
210 device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
211 }
212 ctx.WriteBuffer(device_name);
213
203 IPC::RequestParser rp{ctx}; 214 IPC::RequestParser rp{ctx};
204 auto params{rp.PopRaw<AudoutParams>()}; 215 auto params{rp.PopRaw<AudoutParams>()};
205 if (params.channel_count <= 2) { 216 if (params.channel_count <= 2) {
@@ -212,10 +223,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
212 params.sample_rate = DefaultSampleRate; 223 params.sample_rate = DefaultSampleRate;
213 } 224 }
214 225
215 // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl 226 std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
216 // will likely need to be updated as well. 227 auto audio_out_interface = std::make_shared<IAudioOut>(
217 ASSERT_MSG(!audio_out_interface, "Unimplemented"); 228 params, *audio_core, std::move(device_name), std::move(unique_name));
218 audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core);
219 229
220 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 230 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
221 rb.Push(RESULT_SUCCESS); 231 rb.Push(RESULT_SUCCESS);
@@ -224,6 +234,8 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
224 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); 234 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
225 rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); 235 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
226 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); 236 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
237
238 audio_out_interfaces.push_back(std::move(audio_out_interface));
227} 239}
228 240
229AudOutU::AudOutU() : ServiceFramework("audout:u") { 241AudOutU::AudOutU() : ServiceFramework("audout:u") {
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index dcaf64708..aed4c43b2 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector>
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace AudioCore { 10namespace AudioCore {
@@ -24,7 +25,7 @@ public:
24 ~AudOutU() override; 25 ~AudOutU() override;
25 26
26private: 27private:
27 std::shared_ptr<IAudioOut> audio_out_interface; 28 std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
28 std::unique_ptr<AudioCore::AudioOut> audio_core; 29 std::unique_ptr<AudioCore::AudioOut> audio_core;
29 30
30 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); 31 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 5d6294016..2aa77f68d 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -341,6 +341,10 @@ std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
341 return registered_cache_union; 341 return registered_cache_union;
342} 342}
343 343
344void ClearUnionContents() {
345 registered_cache_union = nullptr;
346}
347
344FileSys::RegisteredCache* GetSystemNANDContents() { 348FileSys::RegisteredCache* GetSystemNANDContents() {
345 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 349 LOG_TRACE(Service_FS, "Opening System NAND Contents");
346 350
@@ -391,6 +395,7 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
391 bis_factory = nullptr; 395 bis_factory = nullptr;
392 save_data_factory = nullptr; 396 save_data_factory = nullptr;
393 sdmc_factory = nullptr; 397 sdmc_factory = nullptr;
398 ClearUnionContents();
394 } 399 }
395 400
396 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 401 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index ff9182e84..0a6cb6635 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -49,6 +49,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space)
49ResultVal<FileSys::VirtualDir> OpenSDMC(); 49ResultVal<FileSys::VirtualDir> OpenSDMC();
50 50
51std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); 51std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
52void ClearUnionContents();
52 53
53FileSys::RegisteredCache* GetSystemNANDContents(); 54FileSys::RegisteredCache* GetSystemNANDContents();
54FileSys::RegisteredCache* GetUserNANDContents(); 55FileSys::RegisteredCache* GetUserNANDContents();
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 3d100763f..c22357d8c 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,9 +6,14 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/debug_pad.h" 8#include "core/hle/service/hid/controllers/debug_pad.h"
9#include "core/settings.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11 12
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
16
12Controller_DebugPad::Controller_DebugPad() = default; 17Controller_DebugPad::Controller_DebugPad() = default;
13Controller_DebugPad::~Controller_DebugPad() = default; 18Controller_DebugPad::~Controller_DebugPad() = default;
14 19
@@ -33,10 +38,44 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
33 38
34 cur_entry.sampling_number = last_entry.sampling_number + 1; 39 cur_entry.sampling_number = last_entry.sampling_number + 1;
35 cur_entry.sampling_number2 = cur_entry.sampling_number; 40 cur_entry.sampling_number2 = cur_entry.sampling_number;
36 // TODO(ogniK): Update debug pad states 41 cur_entry.attribute.connected.Assign(1);
42 auto& pad = cur_entry.pad_state;
43
44 using namespace Settings::NativeButton;
45 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
46 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
47 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
48 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
49 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
50 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
51 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
52 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
53 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
54 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
55 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
56 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
57 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
59
60 const auto [stick_l_x_f, stick_l_y_f] =
61 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
62 const auto [stick_r_x_f, stick_r_y_f] =
63 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
64 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
65 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
66 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
67 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
37 68
38 std::memcpy(data, &shared_memory, sizeof(SharedMemory)); 69 std::memcpy(data, &shared_memory, sizeof(SharedMemory));
39} 70}
40 71
41void Controller_DebugPad::OnLoadInputDevices() {} 72void Controller_DebugPad::OnLoadInputDevices() {
73 std::transform(Settings::values.debug_pad_buttons.begin(),
74 Settings::values.debug_pad_buttons.begin() +
75 Settings::NativeButton::NUM_BUTTONS_HID,
76 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
77 std::transform(Settings::values.debug_pad_analogs.begin(),
78 Settings::values.debug_pad_analogs.end(), analogs.begin(),
79 Input::CreateDevice<Input::AnalogDevice>);
80}
42} // namespace Service::HID 81} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 62b4f2682..68b734248 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -5,10 +5,13 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/bit_field.h"
8#include "common/common_funcs.h" 9#include "common/common_funcs.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/swap.h" 11#include "common/swap.h"
12#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 13#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/settings.h"
12 15
13namespace Service::HID { 16namespace Service::HID {
14class Controller_DebugPad final : public ControllerBase { 17class Controller_DebugPad final : public ControllerBase {
@@ -35,11 +38,40 @@ private:
35 }; 38 };
36 static_assert(sizeof(AnalogStick) == 0x8); 39 static_assert(sizeof(AnalogStick) == 0x8);
37 40
41 struct PadState {
42 union {
43 u32_le raw{};
44 BitField<0, 1, u32_le> a;
45 BitField<1, 1, u32_le> b;
46 BitField<2, 1, u32_le> x;
47 BitField<3, 1, u32_le> y;
48 BitField<4, 1, u32_le> l;
49 BitField<5, 1, u32_le> r;
50 BitField<6, 1, u32_le> zl;
51 BitField<7, 1, u32_le> zr;
52 BitField<8, 1, u32_le> plus;
53 BitField<9, 1, u32_le> minus;
54 BitField<10, 1, u32_le> d_left;
55 BitField<11, 1, u32_le> d_up;
56 BitField<12, 1, u32_le> d_right;
57 BitField<13, 1, u32_le> d_down;
58 };
59 };
60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
61
62 struct Attributes {
63 union {
64 u32_le raw{};
65 BitField<0, 1, u32_le> connected;
66 };
67 };
68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
69
38 struct PadStates { 70 struct PadStates {
39 s64_le sampling_number; 71 s64_le sampling_number;
40 s64_le sampling_number2; 72 s64_le sampling_number2;
41 u32_le attribute; 73 Attributes attribute;
42 u32_le button_state; 74 PadState pad_state;
43 AnalogStick r_stick; 75 AnalogStick r_stick;
44 AnalogStick l_stick; 76 AnalogStick l_stick;
45 }; 77 };
@@ -52,5 +84,10 @@ private:
52 }; 84 };
53 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); 85 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
54 SharedMemory shared_memory{}; 86 SharedMemory shared_memory{};
87
88 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
89 buttons;
90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
91 analogs;
55}; 92};
56} // namespace Service::HID 93} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index ccfbce9ac..ca75adc2b 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -6,9 +6,11 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/keyboard.h" 8#include "core/hle/service/hid/controllers/keyboard.h"
9#include "core/settings.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8;
12 14
13Controller_Keyboard::Controller_Keyboard() = default; 15Controller_Keyboard::Controller_Keyboard() = default;
14Controller_Keyboard::~Controller_Keyboard() = default; 16Controller_Keyboard::~Controller_Keyboard() = default;
@@ -34,10 +36,24 @@ void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
34 36
35 cur_entry.sampling_number = last_entry.sampling_number + 1; 37 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number; 38 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update keyboard states 39
40 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
41 for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) {
42 cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k);
43 }
44 }
45
46 for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
47 cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
48 }
38 49
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 50 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40} 51}
41 52
42void Controller_Keyboard::OnLoadInputDevices() {} 53void Controller_Keyboard::OnLoadInputDevices() {
54 std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
55 keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
56 std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
57 keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
58}
43} // namespace Service::HID 59} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 493e68fce..f52775456 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -8,7 +8,9 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/settings.h"
12 14
13namespace Service::HID { 15namespace Service::HID {
14class Controller_Keyboard final : public ControllerBase { 16class Controller_Keyboard final : public ControllerBase {
@@ -46,5 +48,10 @@ private:
46 }; 48 };
47 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); 49 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
48 SharedMemory shared_memory{}; 50 SharedMemory shared_memory{};
51
52 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
53 keyboard_keys;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
55 keyboard_mods;
49}; 56};
50} // namespace Service::HID 57} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 4e246a57d..63391dbe9 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -5,6 +5,7 @@
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
8#include "core/hle/service/hid/controllers/mouse.h" 9#include "core/hle/service/hid/controllers/mouse.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
@@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default;
14Controller_Mouse::~Controller_Mouse() = default; 15Controller_Mouse::~Controller_Mouse() = default;
15 16
16void Controller_Mouse::OnInit() {} 17void Controller_Mouse::OnInit() {}
17
18void Controller_Mouse::OnRelease() {} 18void Controller_Mouse::OnRelease() {}
19 19
20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { 20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
@@ -34,10 +34,29 @@ void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
34 34
35 cur_entry.sampling_number = last_entry.sampling_number + 1; 35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number; 36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update mouse states 37
38 if (Settings::values.mouse_enabled) {
39 const auto [px, py, sx, sy] = mouse_device->GetStatus();
40 const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
41 const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
42 cur_entry.x = x;
43 cur_entry.y = y;
44 cur_entry.delta_x = x - last_entry.x;
45 cur_entry.delta_y = y - last_entry.y;
46 cur_entry.mouse_wheel_x = sx;
47 cur_entry.mouse_wheel_y = sy;
48
49 for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) {
50 cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i);
51 }
52 }
38 53
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 54 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40} 55}
41 56
42void Controller_Mouse::OnLoadInputDevices() {} 57void Controller_Mouse::OnLoadInputDevices() {
58 mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
59 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
60 mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
61}
43} // namespace Service::HID 62} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 543b0b71f..70b654d07 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -7,7 +7,9 @@
7#include <array> 7#include <array>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/swap.h" 9#include "common/swap.h"
10#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/controller_base.h" 11#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/settings.h"
11 13
12namespace Service::HID { 14namespace Service::HID {
13class Controller_Mouse final : public ControllerBase { 15class Controller_Mouse final : public ControllerBase {
@@ -35,7 +37,8 @@ private:
35 s32_le y; 37 s32_le y;
36 s32_le delta_x; 38 s32_le delta_x;
37 s32_le delta_y; 39 s32_le delta_y;
38 s32_le mouse_wheel; 40 s32_le mouse_wheel_x;
41 s32_le mouse_wheel_y;
39 s32_le button; 42 s32_le button;
40 s32_le attribute; 43 s32_le attribute;
41 }; 44 };
@@ -46,5 +49,9 @@ private:
46 std::array<MouseState, 17> mouse_states; 49 std::array<MouseState, 17> mouse_states;
47 }; 50 };
48 SharedMemory shared_memory{}; 51 SharedMemory shared_memory{};
52
53 std::unique_ptr<Input::MouseDevice> mouse_device;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
55 mouse_button_devices;
49}; 56};
50} // namespace Service::HID 57} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 205e4fd14..46604887c 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -17,22 +17,13 @@
17#include "core/settings.h" 17#include "core/settings.h"
18 18
19namespace Service::HID { 19namespace Service::HID {
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 20constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
26constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 21constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
27constexpr std::size_t NPAD_OFFSET = 0x9A00; 22constexpr std::size_t NPAD_OFFSET = 0x9A00;
28constexpr u32 BATTERY_FULL = 2; 23constexpr u32 BATTERY_FULL = 2;
29constexpr u32 NPAD_HANDHELD = 32;
30constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
31constexpr u32 MAX_NPAD_ID = 7; 24constexpr u32 MAX_NPAD_ID = 7;
32constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
33 Controller_NPad::NPadControllerType::JoyDual;
34constexpr std::array<u32, 10> npad_id_list{ 25constexpr std::array<u32, 10> npad_id_list{
35 0, 1, 2, 3, 4, 5, 6, 7, 32, 16, 26 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
36}; 27};
37 28
38enum class JoystickId : std::size_t { 29enum class JoystickId : std::size_t {
@@ -40,7 +31,23 @@ enum class JoystickId : std::size_t {
40 Joystick_Right, 31 Joystick_Right,
41}; 32};
42 33
43static std::size_t NPadIdToIndex(u32 npad_id) { 34static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
35 switch (type) {
36 case Settings::ControllerType::ProController:
37 return Controller_NPad::NPadControllerType::ProController;
38 case Settings::ControllerType::DualJoycon:
39 return Controller_NPad::NPadControllerType::JoyDual;
40 case Settings::ControllerType::LeftJoycon:
41 return Controller_NPad::NPadControllerType::JoyLeft;
42 case Settings::ControllerType::RightJoycon:
43 return Controller_NPad::NPadControllerType::JoyRight;
44 default:
45 UNREACHABLE();
46 return Controller_NPad::NPadControllerType::JoyDual;
47 }
48}
49
50std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
44 switch (npad_id) { 51 switch (npad_id) {
45 case 0: 52 case 0:
46 case 1: 53 case 1:
@@ -63,6 +70,27 @@ static std::size_t NPadIdToIndex(u32 npad_id) {
63 } 70 }
64} 71}
65 72
73u32 Controller_NPad::IndexToNPad(std::size_t index) {
74 switch (index) {
75 case 0:
76 case 1:
77 case 2:
78 case 3:
79 case 4:
80 case 5:
81 case 6:
82 case 7:
83 return static_cast<u32>(index);
84 case 8:
85 return NPAD_HANDHELD;
86 case 9:
87 return NPAD_UNKNOWN;
88 default:
89 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
90 return 0;
91 };
92}
93
66Controller_NPad::Controller_NPad() = default; 94Controller_NPad::Controller_NPad() = default;
67Controller_NPad::~Controller_NPad() = default; 95Controller_NPad::~Controller_NPad() = default;
68 96
@@ -79,22 +107,32 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
79 controller.joy_styles.handheld.Assign(1); 107 controller.joy_styles.handheld.Assign(1);
80 controller.device_type.handheld.Assign(1); 108 controller.device_type.handheld.Assign(1);
81 controller.pad_assignment = NPadAssignments::Dual; 109 controller.pad_assignment = NPadAssignments::Dual;
110 controller.properties.is_vertical.Assign(1);
111 controller.properties.use_plus.Assign(1);
112 controller.properties.use_minus.Assign(1);
82 break; 113 break;
83 case NPadControllerType::JoyDual: 114 case NPadControllerType::JoyDual:
84 controller.joy_styles.joycon_dual.Assign(1); 115 controller.joy_styles.joycon_dual.Assign(1);
85 controller.device_type.joycon_left.Assign(1); 116 controller.device_type.joycon_left.Assign(1);
86 controller.device_type.joycon_right.Assign(1); 117 controller.device_type.joycon_right.Assign(1);
118 controller.properties.is_vertical.Assign(1);
119 controller.properties.use_plus.Assign(1);
120 controller.properties.use_minus.Assign(1);
87 controller.pad_assignment = NPadAssignments::Dual; 121 controller.pad_assignment = NPadAssignments::Dual;
88 break; 122 break;
89 case NPadControllerType::JoyLeft: 123 case NPadControllerType::JoyLeft:
90 controller.joy_styles.joycon_left.Assign(1); 124 controller.joy_styles.joycon_left.Assign(1);
91 controller.device_type.joycon_left.Assign(1); 125 controller.device_type.joycon_left.Assign(1);
92 controller.pad_assignment = NPadAssignments::Dual; 126 controller.properties.is_horizontal.Assign(1);
127 controller.properties.use_minus.Assign(1);
128 controller.pad_assignment = NPadAssignments::Single;
93 break; 129 break;
94 case NPadControllerType::JoyRight: 130 case NPadControllerType::JoyRight:
95 controller.joy_styles.joycon_right.Assign(1); 131 controller.joy_styles.joycon_right.Assign(1);
96 controller.device_type.joycon_right.Assign(1); 132 controller.device_type.joycon_right.Assign(1);
97 controller.pad_assignment = NPadAssignments::Dual; 133 controller.properties.is_horizontal.Assign(1);
134 controller.properties.use_plus.Assign(1);
135 controller.pad_assignment = NPadAssignments::Single;
98 break; 136 break;
99 case NPadControllerType::Pokeball: 137 case NPadControllerType::Pokeball:
100 controller.joy_styles.pokeball.Assign(1); 138 controller.joy_styles.pokeball.Assign(1);
@@ -104,6 +142,9 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
104 case NPadControllerType::ProController: 142 case NPadControllerType::ProController:
105 controller.joy_styles.pro_controller.Assign(1); 143 controller.joy_styles.pro_controller.Assign(1);
106 controller.device_type.pro_controller.Assign(1); 144 controller.device_type.pro_controller.Assign(1);
145 controller.properties.is_vertical.Assign(1);
146 controller.properties.use_plus.Assign(1);
147 controller.properties.use_minus.Assign(1);
107 controller.pad_assignment = NPadAssignments::Single; 148 controller.pad_assignment = NPadAssignments::Single;
108 break; 149 break;
109 } 150 }
@@ -113,14 +154,12 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
113 controller.single_color.button_color = 0; 154 controller.single_color.button_color = 0;
114 155
115 controller.dual_color_error = ColorReadError::ReadOk; 156 controller.dual_color_error = ColorReadError::ReadOk;
116 controller.left_color.body_color = JOYCON_BODY_NEON_BLUE; 157 controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
117 controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE; 158 controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
118 controller.right_color.body_color = JOYCON_BODY_NEON_RED; 159 controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
119 controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED; 160 controller.right_color.button_color =
120 161 Settings::values.players[controller_idx].button_color_right;
121 controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations 162
122 controller.properties.use_plus.Assign(1);
123 controller.properties.use_minus.Assign(1);
124 controller.battery_level[0] = BATTERY_FULL; 163 controller.battery_level[0] = BATTERY_FULL;
125 controller.battery_level[1] = BATTERY_FULL; 164 controller.battery_level[1] = BATTERY_FULL;
126 controller.battery_level[2] = BATTERY_FULL; 165 controller.battery_level[2] = BATTERY_FULL;
@@ -144,26 +183,109 @@ void Controller_NPad::OnInit() {
144 style.pro_controller.Assign(1); 183 style.pro_controller.Assign(1);
145 style.pokeball.Assign(1); 184 style.pokeball.Assign(1);
146 } 185 }
186
187 std::transform(
188 Settings::values.players.begin(), Settings::values.players.end(),
189 connected_controllers.begin(), [](const Settings::PlayerInput& player) {
190 return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
191 });
192
193 std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
194 [](const ControllerHolder& holder) { return holder.is_connected; });
195
196 // Account for handheld
197 if (connected_controllers[8].is_connected)
198 connected_controllers[8].type = NPadControllerType::Handheld;
199
200 supported_npad_id_types.resize(npad_id_list.size());
201 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
202 npad_id_list.size() * sizeof(u32));
203
204 // Add a default dual joycon controller if none are present.
147 if (std::none_of(connected_controllers.begin(), connected_controllers.end(), 205 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
148 [](const ControllerHolder& controller) { return controller.is_connected; })) { 206 [](const ControllerHolder& controller) { return controller.is_connected; })) {
149 supported_npad_id_types.resize(npad_id_list.size()); 207 supported_npad_id_types.resize(npad_id_list.size());
150 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 208 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
151 npad_id_list.size() * sizeof(u32)); 209 npad_id_list.size() * sizeof(u32));
152 AddNewController(PREFERRED_CONTROLLER); 210 AddNewController(NPadControllerType::JoyDual);
211 }
212
213 for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
214 const auto& controller = connected_controllers[i];
215 if (controller.is_connected) {
216 AddNewControllerAt(controller.type, IndexToNPad(i));
217 }
153 } 218 }
154} 219}
155 220
156void Controller_NPad::OnLoadInputDevices() { 221void Controller_NPad::OnLoadInputDevices() {
157 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 222 const auto& players = Settings::values.players;
158 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 223 for (std::size_t i = 0; i < players.size(); ++i) {
159 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 224 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
160 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 225 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
161 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, 226 buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>);
162 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>); 227 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
228 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
229 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
230 }
163} 231}
164 232
165void Controller_NPad::OnRelease() {} 233void Controller_NPad::OnRelease() {}
166 234
235void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
236 const auto controller_idx = NPadIdToIndex(npad_id);
237 const auto controller_type = connected_controllers[controller_idx].type;
238 if (!connected_controllers[controller_idx].is_connected) {
239 return;
240 }
241 auto& pad_state = npad_pad_states[controller_idx].pad_states;
242 auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
243 auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
244 const auto& button_state = buttons[controller_idx];
245 const auto& analog_state = sticks[controller_idx];
246
247 using namespace Settings::NativeButton;
248 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
249 pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
250 pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
251 pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
252 pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
253 pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
254 pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
255 pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
256 pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
257 pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
258 pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
259 pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
260
261 pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
262 pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
263 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
264 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
265
266 pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
267 pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
268 pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
269 pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
270
271 pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
272 pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
273 pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
274 pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
275
276 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
277 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
278
279 const auto [stick_l_x_f, stick_l_y_f] =
280 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
281 const auto [stick_r_x_f, stick_r_y_f] =
282 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
283 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
284 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
285 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
286 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
287}
288
167void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { 289void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
168 if (!IsControllerActivated()) 290 if (!IsControllerActivated())
169 return; 291 return;
@@ -199,97 +321,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
199 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { 321 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
200 continue; 322 continue;
201 } 323 }
202 324 const u32 npad_index = static_cast<u32>(i);
203 // Pad states 325 RequestPadStateUpdate(npad_index);
204 ControllerPadState pad_state{}; 326 auto& pad_state = npad_pad_states[npad_index];
205 using namespace Settings::NativeButton;
206 pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
207 pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
208 pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
209 pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
210 pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
211 pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
212 pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
213 pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
214 pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
215 pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
216 pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
217 pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
218
219 pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
220 pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
221 pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
222 pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
223
224 pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
225 pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
226 pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
227 pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
228
229 pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
230 pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
231 pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
232 pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
233
234 pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
235 pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
236
237 AnalogPosition lstick_entry{};
238 AnalogPosition rstick_entry{};
239
240 const auto [stick_l_x_f, stick_l_y_f] =
241 sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
242 const auto [stick_r_x_f, stick_r_y_f] =
243 sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
244 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
245 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
246 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
247 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
248
249 if (controller_type == NPadControllerType::JoyLeft ||
250 controller_type == NPadControllerType::JoyRight) {
251 if (npad.properties.is_horizontal) {
252 ControllerPadState state{};
253 AnalogPosition temp_lstick_entry{};
254 AnalogPosition temp_rstick_entry{};
255 if (controller_type == NPadControllerType::JoyLeft) {
256 state.d_down.Assign(pad_state.d_left.Value());
257 state.d_left.Assign(pad_state.d_up.Value());
258 state.d_right.Assign(pad_state.d_down.Value());
259 state.d_up.Assign(pad_state.d_right.Value());
260 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
261 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
262
263 state.zl.Assign(pad_state.zl.Value());
264 state.plus.Assign(pad_state.minus.Value());
265
266 temp_lstick_entry = lstick_entry;
267 temp_rstick_entry = rstick_entry;
268 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
269 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
270 temp_lstick_entry.y *= -1;
271 } else if (controller_type == NPadControllerType::JoyRight) {
272 state.x.Assign(pad_state.a.Value());
273 state.a.Assign(pad_state.b.Value());
274 state.b.Assign(pad_state.y.Value());
275 state.y.Assign(pad_state.b.Value());
276
277 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
278 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
279 state.zr.Assign(pad_state.zr.Value());
280 state.plus.Assign(pad_state.plus.Value());
281
282 temp_lstick_entry = lstick_entry;
283 temp_rstick_entry = rstick_entry;
284 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
285 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
286 temp_rstick_entry.x *= -1;
287 }
288 pad_state.raw = state.raw;
289 lstick_entry = temp_lstick_entry;
290 rstick_entry = temp_rstick_entry;
291 }
292 }
293 327
294 auto& main_controller = 328 auto& main_controller =
295 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; 329 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
@@ -304,8 +338,51 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
304 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; 338 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
305 339
306 if (hold_type == NpadHoldType::Horizontal) { 340 if (hold_type == NpadHoldType::Horizontal) {
307 // TODO(ogniK): Remap buttons for different orientations 341 ControllerPadState state{};
342 AnalogPosition temp_lstick_entry{};
343 AnalogPosition temp_rstick_entry{};
344 if (controller_type == NPadControllerType::JoyLeft) {
345 state.d_down.Assign(pad_state.pad_states.d_left.Value());
346 state.d_left.Assign(pad_state.pad_states.d_up.Value());
347 state.d_right.Assign(pad_state.pad_states.d_down.Value());
348 state.d_up.Assign(pad_state.pad_states.d_right.Value());
349 state.l.Assign(pad_state.pad_states.l.Value() |
350 pad_state.pad_states.left_sl.Value());
351 state.r.Assign(pad_state.pad_states.r.Value() |
352 pad_state.pad_states.left_sr.Value());
353
354 state.zl.Assign(pad_state.pad_states.zl.Value());
355 state.plus.Assign(pad_state.pad_states.minus.Value());
356
357 temp_lstick_entry = pad_state.l_stick;
358 temp_rstick_entry = pad_state.r_stick;
359 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
360 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
361 temp_lstick_entry.y *= -1;
362 } else if (controller_type == NPadControllerType::JoyRight) {
363 state.x.Assign(pad_state.pad_states.a.Value());
364 state.a.Assign(pad_state.pad_states.b.Value());
365 state.b.Assign(pad_state.pad_states.y.Value());
366 state.y.Assign(pad_state.pad_states.b.Value());
367
368 state.l.Assign(pad_state.pad_states.l.Value() |
369 pad_state.pad_states.right_sl.Value());
370 state.r.Assign(pad_state.pad_states.r.Value() |
371 pad_state.pad_states.right_sr.Value());
372 state.zr.Assign(pad_state.pad_states.zr.Value());
373 state.plus.Assign(pad_state.pad_states.plus.Value());
374
375 temp_lstick_entry = pad_state.l_stick;
376 temp_rstick_entry = pad_state.r_stick;
377 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
378 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
379 temp_rstick_entry.x *= -1;
380 }
381 pad_state.pad_states.raw = state.raw;
382 pad_state.l_stick = temp_lstick_entry;
383 pad_state.r_stick = temp_rstick_entry;
308 } 384 }
385
309 libnx_entry.connection_status.raw = 0; 386 libnx_entry.connection_status.raw = 0;
310 387
311 switch (controller_type) { 388 switch (controller_type) {
@@ -316,9 +393,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
316 handheld_entry.connection_status.IsRightJoyConnected.Assign(1); 393 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
317 handheld_entry.connection_status.IsLeftJoyWired.Assign(1); 394 handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
318 handheld_entry.connection_status.IsRightJoyWired.Assign(1); 395 handheld_entry.connection_status.IsRightJoyWired.Assign(1);
319 handheld_entry.pad_states.raw = pad_state.raw; 396 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
320 handheld_entry.l_stick = lstick_entry; 397 handheld_entry.pad.l_stick = pad_state.l_stick;
321 handheld_entry.r_stick = rstick_entry; 398 handheld_entry.pad.r_stick = pad_state.r_stick;
322 break; 399 break;
323 case NPadControllerType::JoyDual: 400 case NPadControllerType::JoyDual:
324 dual_entry.connection_status.raw = 0; 401 dual_entry.connection_status.raw = 0;
@@ -331,25 +408,25 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
331 libnx_entry.connection_status.IsRightJoyConnected.Assign(1); 408 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
332 libnx_entry.connection_status.IsConnected.Assign(1); 409 libnx_entry.connection_status.IsConnected.Assign(1);
333 410
334 dual_entry.pad_states.raw = pad_state.raw; 411 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
335 dual_entry.l_stick = lstick_entry; 412 dual_entry.pad.l_stick = pad_state.l_stick;
336 dual_entry.r_stick = rstick_entry; 413 dual_entry.pad.r_stick = pad_state.r_stick;
337 break; 414 break;
338 case NPadControllerType::JoyLeft: 415 case NPadControllerType::JoyLeft:
339 left_entry.connection_status.raw = 0; 416 left_entry.connection_status.raw = 0;
340 417
341 left_entry.connection_status.IsConnected.Assign(1); 418 left_entry.connection_status.IsConnected.Assign(1);
342 left_entry.pad_states.raw = pad_state.raw; 419 left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
343 left_entry.l_stick = lstick_entry; 420 left_entry.pad.l_stick = pad_state.l_stick;
344 left_entry.r_stick = rstick_entry; 421 left_entry.pad.r_stick = pad_state.r_stick;
345 break; 422 break;
346 case NPadControllerType::JoyRight: 423 case NPadControllerType::JoyRight:
347 right_entry.connection_status.raw = 0; 424 right_entry.connection_status.raw = 0;
348 425
349 right_entry.connection_status.IsConnected.Assign(1); 426 right_entry.connection_status.IsConnected.Assign(1);
350 right_entry.pad_states.raw = pad_state.raw; 427 right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
351 right_entry.l_stick = lstick_entry; 428 right_entry.pad.l_stick = pad_state.l_stick;
352 right_entry.r_stick = rstick_entry; 429 right_entry.pad.r_stick = pad_state.r_stick;
353 break; 430 break;
354 case NPadControllerType::Pokeball: 431 case NPadControllerType::Pokeball:
355 pokeball_entry.connection_status.raw = 0; 432 pokeball_entry.connection_status.raw = 0;
@@ -357,30 +434,30 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
357 pokeball_entry.connection_status.IsConnected.Assign(1); 434 pokeball_entry.connection_status.IsConnected.Assign(1);
358 pokeball_entry.connection_status.IsWired.Assign(1); 435 pokeball_entry.connection_status.IsWired.Assign(1);
359 436
360 pokeball_entry.pad_states.raw = pad_state.raw; 437 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
361 pokeball_entry.l_stick = lstick_entry; 438 pokeball_entry.pad.l_stick = pad_state.l_stick;
362 pokeball_entry.r_stick = rstick_entry; 439 pokeball_entry.pad.r_stick = pad_state.r_stick;
363 break; 440 break;
364 case NPadControllerType::ProController: 441 case NPadControllerType::ProController:
365 main_controller.connection_status.raw = 0; 442 main_controller.connection_status.raw = 0;
366 443
367 main_controller.connection_status.IsConnected.Assign(1); 444 main_controller.connection_status.IsConnected.Assign(1);
368 main_controller.connection_status.IsWired.Assign(1); 445 main_controller.connection_status.IsWired.Assign(1);
369 main_controller.pad_states.raw = pad_state.raw; 446 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
370 main_controller.l_stick = lstick_entry; 447 main_controller.pad.l_stick = pad_state.l_stick;
371 main_controller.r_stick = rstick_entry; 448 main_controller.pad.r_stick = pad_state.r_stick;
372 break; 449 break;
373 } 450 }
374 451
375 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 452 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
376 // any controllers. 453 // any controllers.
377 libnx_entry.pad_states.raw = pad_state.raw; 454 libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
378 libnx_entry.l_stick = lstick_entry; 455 libnx_entry.pad.l_stick = pad_state.l_stick;
379 libnx_entry.r_stick = rstick_entry; 456 libnx_entry.pad.r_stick = pad_state.r_stick;
380 } 457 }
381 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), 458 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
382 shared_memory_entries.size() * sizeof(NPadEntry)); 459 shared_memory_entries.size() * sizeof(NPadEntry));
383} // namespace Service::HID 460}
384 461
385void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { 462void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
386 style.raw = style_set.raw; 463 style.raw = style_set.raw;
@@ -401,23 +478,24 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
401 if (!controller.is_connected) { 478 if (!controller.is_connected) {
402 continue; 479 continue;
403 } 480 }
404 if (!IsControllerSupported(PREFERRED_CONTROLLER)) { 481 const auto requested_controller =
405 const auto best_type = DecideBestController(PREFERRED_CONTROLLER); 482 i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
406 const bool is_handheld = (best_type == NPadControllerType::Handheld || 483 : NPadControllerType::Handheld;
407 PREFERRED_CONTROLLER == NPadControllerType::Handheld); 484 if (!IsControllerSupported(requested_controller)) {
485 const auto is_handheld = requested_controller == NPadControllerType::Handheld;
408 if (is_handheld) { 486 if (is_handheld) {
409 controller.type = NPadControllerType::None; 487 controller.type = NPadControllerType::None;
410 controller.is_connected = false; 488 controller.is_connected = false;
411 AddNewController(best_type); 489 AddNewController(requested_controller);
412 } else { 490 } else {
413 controller.type = best_type; 491 controller.type = requested_controller;
414 InitNewlyAddedControler(i); 492 InitNewlyAddedControler(i);
415 } 493 }
416 had_controller_update = true; 494 had_controller_update = true;
417 } 495 }
418 } 496 if (had_controller_update) {
419 if (had_controller_update) { 497 styleset_changed_event->Signal();
420 styleset_changed_event->Signal(); 498 }
421 } 499 }
422} 500}
423 501
@@ -450,15 +528,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
450 return; 528 return;
451 } 529 }
452 for (std::size_t i = 0; i < controller_ids.size(); i++) { 530 for (std::size_t i = 0; i < controller_ids.size(); i++) {
453 std::size_t controller_pos = i; 531 std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
454 // Handheld controller conversion
455 if (controller_pos == NPAD_HANDHELD) {
456 controller_pos = 8;
457 }
458 // Unknown controller conversion
459 if (controller_pos == NPAD_UNKNOWN) {
460 controller_pos = 9;
461 }
462 if (connected_controllers[controller_pos].is_connected) { 532 if (connected_controllers[controller_pos].is_connected) {
463 // TODO(ogniK): Vibrate the physical controller 533 // TODO(ogniK): Vibrate the physical controller
464 } 534 }
@@ -477,7 +547,9 @@ Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() cons
477Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 547Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
478 return last_processed_vibration; 548 return last_processed_vibration;
479} 549}
550
480void Controller_NPad::AddNewController(NPadControllerType controller) { 551void Controller_NPad::AddNewController(NPadControllerType controller) {
552 controller = DecideBestController(controller);
481 if (controller == NPadControllerType::Handheld) { 553 if (controller == NPadControllerType::Handheld) {
482 connected_controllers[8] = {controller, true}; 554 connected_controllers[8] = {controller, true};
483 InitNewlyAddedControler(8); 555 InitNewlyAddedControler(8);
@@ -495,6 +567,18 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
495 InitNewlyAddedControler(controller_id); 567 InitNewlyAddedControler(controller_id);
496} 568}
497 569
570void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
571 controller = DecideBestController(controller);
572 if (controller == NPadControllerType::Handheld) {
573 connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
574 InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
575 return;
576 }
577
578 connected_controllers[npad_id] = {controller, true};
579 InitNewlyAddedControler(npad_id);
580}
581
498void Controller_NPad::ConnectNPad(u32 npad_id) { 582void Controller_NPad::ConnectNPad(u32 npad_id) {
499 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; 583 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
500} 584}
@@ -503,6 +587,36 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) {
503 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; 587 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
504} 588}
505 589
590bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
591 if (controller == NPadControllerType::Handheld) {
592 // Handheld is not even a supported type, lets stop here
593 if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
594 NPAD_HANDHELD) == supported_npad_id_types.end()) {
595 return false;
596 }
597 // Handheld should not be supported in docked mode
598 if (Settings::values.use_docked_mode) {
599 return false;
600 }
601 }
602 switch (controller) {
603 case NPadControllerType::ProController:
604 return style.pro_controller;
605 case NPadControllerType::Handheld:
606 return style.handheld;
607 case NPadControllerType::JoyDual:
608 return style.joycon_dual;
609 case NPadControllerType::JoyLeft:
610 return style.joycon_left;
611 case NPadControllerType::JoyRight:
612 return style.joycon_right;
613 case NPadControllerType::Pokeball:
614 return style.pokeball;
615 default:
616 return false;
617 }
618}
619
506Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { 620Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
507 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) { 621 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
508 // These are controllers without led patterns 622 // These are controllers without led patterns
@@ -534,6 +648,36 @@ void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
534 can_controllers_vibrate = can_vibrate; 648 can_controllers_vibrate = can_vibrate;
535} 649}
536 650
651void Controller_NPad::ClearAllConnectedControllers() {
652 for (auto& controller : connected_controllers) {
653 if (controller.is_connected && controller.type != NPadControllerType::None) {
654 controller.type = NPadControllerType::None;
655 controller.is_connected = false;
656 }
657 }
658}
659void Controller_NPad::DisconnectAllConnectedControllers() {
660 std::for_each(connected_controllers.begin(), connected_controllers.end(),
661 [](ControllerHolder& controller) { controller.is_connected = false; });
662}
663
664void Controller_NPad::ConnectAllDisconnectedControllers() {
665 std::for_each(connected_controllers.begin(), connected_controllers.end(),
666 [](ControllerHolder& controller) {
667 if (controller.type != NPadControllerType::None && !controller.is_connected) {
668 controller.is_connected = false;
669 }
670 });
671}
672
673void Controller_NPad::ClearAllControllers() {
674 std::for_each(connected_controllers.begin(), connected_controllers.end(),
675 [](ControllerHolder& controller) {
676 controller.type = NPadControllerType::None;
677 controller.is_connected = false;
678 });
679}
680
537bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { 681bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
538 const bool support_handheld = 682 const bool support_handheld =
539 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) != 683 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index ac86985ff..ea8057b80 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -5,13 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/bit_field.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "core/frontend/input.h" 10#include "core/frontend/input.h"
11#include "core/hle/kernel/event.h"
10#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
11#include "core/settings.h" 13#include "core/settings.h"
12 14
13namespace Service::HID { 15namespace Service::HID {
14 16
17constexpr u32 NPAD_HANDHELD = 32;
18constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
19
15class Controller_NPad final : public ControllerBase { 20class Controller_NPad final : public ControllerBase {
16public: 21public:
17 Controller_NPad(); 22 Controller_NPad();
@@ -107,11 +112,19 @@ public:
107 Vibration GetLastVibration() const; 112 Vibration GetLastVibration() const;
108 113
109 void AddNewController(NPadControllerType controller); 114 void AddNewController(NPadControllerType controller);
115 void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
110 116
111 void ConnectNPad(u32 npad_id); 117 void ConnectNPad(u32 npad_id);
112 void DisconnectNPad(u32 npad_id); 118 void DisconnectNPad(u32 npad_id);
113 LedPattern GetLedPattern(u32 npad_id); 119 LedPattern GetLedPattern(u32 npad_id);
114 void SetVibrationEnabled(bool can_vibrate); 120 void SetVibrationEnabled(bool can_vibrate);
121 void ClearAllConnectedControllers();
122 void DisconnectAllConnectedControllers();
123 void ConnectAllDisconnectedControllers();
124 void ClearAllControllers();
125
126 static std::size_t NPadIdToIndex(u32 npad_id);
127 static u32 IndexToNPad(std::size_t index);
115 128
116private: 129private:
117 struct CommonHeader { 130 struct CommonHeader {
@@ -164,8 +177,11 @@ private:
164 BitField<23, 1, u64_le> r_stick_down; 177 BitField<23, 1, u64_le> r_stick_down;
165 178
166 // Not always active? 179 // Not always active?
167 BitField<24, 1, u64_le> sl; 180 BitField<24, 1, u64_le> left_sl;
168 BitField<25, 1, u64_le> sr; 181 BitField<25, 1, u64_le> left_sr;
182
183 BitField<26, 1, u64_le> right_sl;
184 BitField<27, 1, u64_le> right_sr;
169 }; 185 };
170 }; 186 };
171 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); 187 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -189,12 +205,17 @@ private:
189 }; 205 };
190 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); 206 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
191 207
192 struct GenericStates { 208 struct ControllerPad {
193 s64_le timestamp;
194 s64_le timestamp2;
195 ControllerPadState pad_states; 209 ControllerPadState pad_states;
196 AnalogPosition l_stick; 210 AnalogPosition l_stick;
197 AnalogPosition r_stick; 211 AnalogPosition r_stick;
212 };
213 static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
214
215 struct GenericStates {
216 s64_le timestamp;
217 s64_le timestamp2;
218 ControllerPad pad;
198 ConnectionState connection_status; 219 ConnectionState connection_status;
199 }; 220 };
200 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); 221 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
@@ -266,15 +287,20 @@ private:
266 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); 287 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
267 288
268 struct ControllerHolder { 289 struct ControllerHolder {
269 Controller_NPad::NPadControllerType type; 290 NPadControllerType type;
270 bool is_connected; 291 bool is_connected;
271 }; 292 };
272 293
273 NPadType style{}; 294 NPadType style{};
274 std::array<NPadEntry, 10> shared_memory_entries{}; 295 std::array<NPadEntry, 10> shared_memory_entries{};
275 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 296 std::array<
297 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
298 10>
276 buttons; 299 buttons;
277 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; 300 std::array<
301 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
302 10>
303 sticks;
278 std::vector<u32> supported_npad_id_types{}; 304 std::vector<u32> supported_npad_id_types{};
279 NpadHoldType hold_type{NpadHoldType::Vertical}; 305 NpadHoldType hold_type{NpadHoldType::Vertical};
280 Kernel::SharedPtr<Kernel::Event> styleset_changed_event; 306 Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
@@ -285,5 +311,8 @@ private:
285 void InitNewlyAddedControler(std::size_t controller_idx); 311 void InitNewlyAddedControler(std::size_t controller_idx);
286 bool IsControllerSupported(NPadControllerType controller) const; 312 bool IsControllerSupported(NPadControllerType controller) const;
287 NPadControllerType DecideBestController(NPadControllerType priority) const; 313 NPadControllerType DecideBestController(NPadControllerType priority) const;
314 void RequestPadStateUpdate(u32 npad_id);
315 std::array<ControllerPad, 10> npad_pad_states{};
316 bool IsControllerSupported(NPadControllerType controller);
288}; 317};
289} // namespace Service::HID 318} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 43efef803..f666b1bd8 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -41,16 +41,17 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
41 41
42 const auto [x, y, pressed] = touch_device->GetStatus(); 42 const auto [x, y, pressed] = touch_device->GetStatus();
43 auto& touch_entry = cur_entry.states[0]; 43 auto& touch_entry = cur_entry.states[0];
44 if (pressed) { 44 touch_entry.attribute.raw = 0;
45 if (pressed && Settings::values.touchscreen.enabled) {
45 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); 46 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
46 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); 47 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
47 touch_entry.diameter_x = 15; 48 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
48 touch_entry.diameter_y = 15; 49 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
49 touch_entry.rotation_angle = 0; 50 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
50 const u64 tick = CoreTiming::GetTicks(); 51 const u64 tick = CoreTiming::GetTicks();
51 touch_entry.delta_time = tick - last_touch; 52 touch_entry.delta_time = tick - last_touch;
52 last_touch = tick; 53 last_touch = tick;
53 touch_entry.finger = 0; 54 touch_entry.finger = Settings::values.touchscreen.finger;
54 cur_entry.entry_count = 1; 55 cur_entry.entry_count = 1;
55 } else { 56 } else {
56 cur_entry.entry_count = 0; 57 cur_entry.entry_count = 0;
@@ -60,6 +61,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
60} 61}
61 62
62void Controller_Touchscreen::OnLoadInputDevices() { 63void Controller_Touchscreen::OnLoadInputDevices() {
63 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device); 64 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
64} 65}
65} // namespace Service::HID 66} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index e5db6e6ba..94cd0eba9 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h"
7#include "common/common_funcs.h" 8#include "common/common_funcs.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/swap.h" 10#include "common/swap.h"
@@ -29,9 +30,18 @@ public:
29 void OnLoadInputDevices() override; 30 void OnLoadInputDevices() override;
30 31
31private: 32private:
33 struct Attributes {
34 union {
35 u32 raw{};
36 BitField<0, 1, u32_le> start_touch;
37 BitField<1, 1, u32_le> end_touch;
38 };
39 };
40 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
41
32 struct TouchState { 42 struct TouchState {
33 u64_le delta_time; 43 u64_le delta_time;
34 u32_le attribute; 44 Attributes attribute;
35 u32_le finger; 45 u32_le finger;
36 u32_le x; 46 u32_le x;
37 u32_le y; 47 u32_le y;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 39631b14f..7c0dac5dc 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -34,8 +34,8 @@
34namespace Service::HID { 34namespace Service::HID {
35 35
36// Updating period for each HID device. 36// Updating period for each HID device.
37// TODO(shinyquagsire23): These need better values. 37// TODO(ogniK): Find actual polling rate of hid
38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index d607d985e..7a9d0d0dd 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -4,7 +4,10 @@
4 4
5#include <memory> 5#include <memory>
6#include <fmt/format.h> 6#include <fmt/format.h>
7#include <mbedtls/sha256.h>
7 8
9#include "common/alignment.h"
10#include "common/hex_util.h"
8#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
10#include "core/hle/service/ldr/ldr.h" 13#include "core/hle/service/ldr/ldr.h"
@@ -13,6 +16,21 @@
13 16
14namespace Service::LDR { 17namespace Service::LDR {
15 18
19constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
20constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
21constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
22constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
23constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
24constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
25constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
26constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
27constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
28constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
29constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
30constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
31
32constexpr u64 MAXIMUM_LOADED_RO = 0x40;
33
16class DebugMonitor final : public ServiceFramework<DebugMonitor> { 34class DebugMonitor final : public ServiceFramework<DebugMonitor> {
17public: 35public:
18 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { 36 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} {
@@ -64,9 +82,9 @@ public:
64 // clang-format off 82 // clang-format off
65 static const FunctionInfo functions[] = { 83 static const FunctionInfo functions[] = {
66 {0, &RelocatableObject::LoadNro, "LoadNro"}, 84 {0, &RelocatableObject::LoadNro, "LoadNro"},
67 {1, nullptr, "UnloadNro"}, 85 {1, &RelocatableObject::UnloadNro, "UnloadNro"},
68 {2, &RelocatableObject::LoadNrr, "LoadNrr"}, 86 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
69 {3, nullptr, "UnloadNrr"}, 87 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
70 {4, &RelocatableObject::Initialize, "Initialize"}, 88 {4, &RelocatableObject::Initialize, "Initialize"},
71 }; 89 };
72 // clang-format on 90 // clang-format on
@@ -75,9 +93,123 @@ public:
75 } 93 }
76 94
77 void LoadNrr(Kernel::HLERequestContext& ctx) { 95 void LoadNrr(Kernel::HLERequestContext& ctx) {
96 IPC::RequestParser rp{ctx};
97 rp.Skip(2, false);
98 const VAddr nrr_addr{rp.Pop<VAddr>()};
99 const u64 nrr_size{rp.Pop<u64>()};
100
101 if (!initialized) {
102 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
103 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(ERROR_NOT_INITIALIZED);
105 return;
106 }
107
108 if (nrr.size() >= MAXIMUM_LOADED_RO) {
109 LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs "
110 "(0x40)! Failing...");
111 IPC::ResponseBuilder rb{ctx, 2};
112 rb.Push(ERROR_MAXIMUM_NRR);
113 return;
114 }
115
116 // NRR Address does not fall on 0x1000 byte boundary
117 if (!Common::Is4KBAligned(nrr_addr)) {
118 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
119 IPC::ResponseBuilder rb{ctx, 2};
120 rb.Push(ERROR_INVALID_ALIGNMENT);
121 return;
122 }
123
124 // NRR Size is zero or causes overflow
125 if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
126 LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
127 nrr_addr, nrr_size);
128 IPC::ResponseBuilder rb{ctx, 2};
129 rb.Push(ERROR_INVALID_SIZE);
130 return;
131 }
132 // Read NRR data from memory
133 std::vector<u8> nrr_data(nrr_size);
134 Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
135 NRRHeader header;
136 std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
137
138 if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) {
139 LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic);
140 IPC::ResponseBuilder rb{ctx, 2};
141 rb.Push(ERROR_INVALID_NRR);
142 return;
143 }
144
145 if (header.size != nrr_size) {
146 LOG_ERROR(Service_LDR,
147 "NRR header reported size did not match LoadNrr parameter size! "
148 "(header_size={:016X}, loadnrr_size={:016X})",
149 header.size, nrr_size);
150 IPC::ResponseBuilder rb{ctx, 2};
151 rb.Push(ERROR_INVALID_SIZE);
152 return;
153 }
154
155 if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
156 LOG_ERROR(Service_LDR,
157 "Attempting to load NRR with title ID other than current process. (actual "
158 "{:016X})!",
159 header.title_id);
160 IPC::ResponseBuilder rb{ctx, 2};
161 rb.Push(ERROR_INVALID_NRR);
162 return;
163 }
164
165 std::vector<SHA256Hash> hashes;
166
167 // Copy all hashes in the NRR (specified by hash count/hash offset) into vector.
168 for (std::size_t i = header.hash_offset;
169 i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) {
170 SHA256Hash hash;
171 std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash));
172 hashes.emplace_back(hash);
173 }
174
175 nrr.insert_or_assign(nrr_addr, std::move(hashes));
176
177 IPC::ResponseBuilder rb{ctx, 2};
178 rb.Push(RESULT_SUCCESS);
179 }
180
181 void UnloadNrr(Kernel::HLERequestContext& ctx) {
182 if (!initialized) {
183 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
184 IPC::ResponseBuilder rb{ctx, 2};
185 rb.Push(ERROR_NOT_INITIALIZED);
186 return;
187 }
188
189 IPC::RequestParser rp{ctx};
190 rp.Skip(2, false);
191 const auto nrr_addr{rp.Pop<VAddr>()};
192
193 if (!Common::Is4KBAligned(nrr_addr)) {
194 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
195 IPC::ResponseBuilder rb{ctx, 2};
196 rb.Push(ERROR_INVALID_ALIGNMENT);
197 return;
198 }
199
200 const auto iter = nrr.find(nrr_addr);
201 if (iter == nrr.end()) {
202 LOG_ERROR(Service_LDR,
203 "Attempting to unload NRR which has not been loaded! (addr={:016X})",
204 nrr_addr);
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(ERROR_INVALID_NRR_ADDRESS);
207 return;
208 }
209
210 nrr.erase(iter);
78 IPC::ResponseBuilder rb{ctx, 2}; 211 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(RESULT_SUCCESS); 212 rb.Push(RESULT_SUCCESS);
80 LOG_WARNING(Service_LDR, "(STUBBED) called");
81 } 213 }
82 214
83 void LoadNro(Kernel::HLERequestContext& ctx) { 215 void LoadNro(Kernel::HLERequestContext& ctx) {
@@ -88,33 +220,253 @@ public:
88 const VAddr bss_addr{rp.Pop<VAddr>()}; 220 const VAddr bss_addr{rp.Pop<VAddr>()};
89 const u64 bss_size{rp.Pop<u64>()}; 221 const u64 bss_size{rp.Pop<u64>()};
90 222
223 if (!initialized) {
224 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
225 IPC::ResponseBuilder rb{ctx, 2};
226 rb.Push(ERROR_NOT_INITIALIZED);
227 return;
228 }
229
230 if (nro.size() >= MAXIMUM_LOADED_RO) {
231 LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs "
232 "(0x40)! Failing...");
233 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ERROR_MAXIMUM_NRO);
235 return;
236 }
237
238 // NRO Address does not fall on 0x1000 byte boundary
239 if (!Common::Is4KBAligned(nro_addr)) {
240 LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
241 IPC::ResponseBuilder rb{ctx, 2};
242 rb.Push(ERROR_INVALID_ALIGNMENT);
243 return;
244 }
245
246 // NRO Size or BSS Size is zero or causes overflow
247 const auto nro_size_valid =
248 nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
249 const auto bss_size_valid =
250 nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
251
252 if (!nro_size_valid || !bss_size_valid) {
253 LOG_ERROR(Service_LDR,
254 "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
255 "bss_address={:016X}, bss_size={:016X})",
256 nro_addr, nro_size, bss_addr, bss_size);
257 IPC::ResponseBuilder rb{ctx, 2};
258 rb.Push(ERROR_INVALID_SIZE);
259 return;
260 }
261
91 // Read NRO data from memory 262 // Read NRO data from memory
92 std::vector<u8> nro_data(nro_size); 263 std::vector<u8> nro_data(nro_size);
93 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); 264 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
94 265
266 SHA256Hash hash{};
267 mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
268
269 // NRO Hash is already loaded
270 if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
271 return info.second.hash == hash;
272 })) {
273 LOG_ERROR(Service_LDR, "NRO is already loaded!");
274 IPC::ResponseBuilder rb{ctx, 2};
275 rb.Push(ERROR_ALREADY_LOADED);
276 return;
277 }
278
279 // NRO Hash is not in any loaded NRR
280 if (!IsValidNROHash(hash)) {
281 LOG_ERROR(Service_LDR,
282 "NRO hash is not present in any currently loaded NRRs (hash={})!",
283 Common::HexArrayToString(hash));
284 IPC::ResponseBuilder rb{ctx, 2};
285 rb.Push(ERROR_MISSING_NRR_HASH);
286 return;
287 }
288
289 NROHeader header;
290 std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
291
292 if (!IsValidNRO(header, nro_size, bss_size)) {
293 LOG_ERROR(Service_LDR, "NRO was invalid!");
294 IPC::ResponseBuilder rb{ctx, 2};
295 rb.Push(ERROR_INVALID_NRO);
296 return;
297 }
298
95 // Load NRO as new executable module 299 // Load NRO as new executable module
96 const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; 300 auto* process = Core::CurrentProcess();
97 Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); 301 auto& vm_manager = process->VMManager();
302 auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
303
304 if (!map_address.Succeeded() ||
305 *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) {
306
307 LOG_ERROR(Service_LDR,
308 "General error while allocation memory or no available memory to allocate!");
309 IPC::ResponseBuilder rb{ctx, 2};
310 rb.Push(ERROR_INVALID_MEMORY_STATE);
311 return;
312 }
313
314 ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
315 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
316 ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
98 317
99 // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. 318 if (bss_size > 0) {
100 // It is currently missing: 319 ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
101 // - Signature checks with LoadNRR 320 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
102 // - Checking if a module has already been loaded 321 ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
103 // - Using/validating BSS, etc. params (these are used from NRO header instead) 322 }
104 // - Error checking 323
105 // - ...Probably other things 324 vm_manager.ReprotectRange(*map_address, header.text_size,
325 Kernel::VMAPermission::ReadExecute);
326 vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size,
327 Kernel::VMAPermission::Read);
328 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
329 Kernel::VMAPermission::ReadWrite);
330
331 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
332 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
333 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
334 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
335
336 nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
106 337
107 IPC::ResponseBuilder rb{ctx, 4}; 338 IPC::ResponseBuilder rb{ctx, 4};
108 rb.Push(RESULT_SUCCESS); 339 rb.Push(RESULT_SUCCESS);
109 rb.Push(addr); 340 rb.Push(*map_address);
110 LOG_WARNING(Service_LDR, "(STUBBED) called"); 341 }
342
343 void UnloadNro(Kernel::HLERequestContext& ctx) {
344 IPC::RequestParser rp{ctx};
345 rp.Skip(2, false);
346 const VAddr mapped_addr{rp.PopRaw<VAddr>()};
347 const VAddr heap_addr{rp.PopRaw<VAddr>()};
348
349 if (!initialized) {
350 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
351 IPC::ResponseBuilder rb{ctx, 2};
352 rb.Push(ERROR_NOT_INITIALIZED);
353 return;
354 }
355
356 if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
357 LOG_ERROR(Service_LDR,
358 "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
359 "bss_addr={:016X})!",
360 mapped_addr, heap_addr);
361 IPC::ResponseBuilder rb{ctx, 2};
362 rb.Push(ERROR_INVALID_ALIGNMENT);
363 return;
364 }
365
366 const auto iter = nro.find(mapped_addr);
367 if (iter == nro.end()) {
368 LOG_ERROR(Service_LDR,
369 "The NRO attempting to unmap was not mapped or has an invalid address "
370 "(actual {:016X})!",
371 mapped_addr);
372 IPC::ResponseBuilder rb{ctx, 2};
373 rb.Push(ERROR_INVALID_NRO_ADDRESS);
374 return;
375 }
376
377 auto* process = Core::CurrentProcess();
378 auto& vm_manager = process->VMManager();
379 const auto& nro_size = iter->second.size;
380
381 ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
382 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
383 ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
384
385 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
386 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
387 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
388 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
389
390 nro.erase(iter);
391 IPC::ResponseBuilder rb{ctx, 2};
392 rb.Push(RESULT_SUCCESS);
111 } 393 }
112 394
113 void Initialize(Kernel::HLERequestContext& ctx) { 395 void Initialize(Kernel::HLERequestContext& ctx) {
396 initialized = true;
397
114 IPC::ResponseBuilder rb{ctx, 2}; 398 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(RESULT_SUCCESS); 399 rb.Push(RESULT_SUCCESS);
116 LOG_WARNING(Service_LDR, "(STUBBED) called"); 400 LOG_WARNING(Service_LDR, "(STUBBED) called");
117 } 401 }
402
403private:
404 using SHA256Hash = std::array<u8, 0x20>;
405
406 struct NROHeader {
407 u32_le entrypoint_insn;
408 u32_le mod_offset;
409 INSERT_PADDING_WORDS(2);
410 u32_le magic;
411 INSERT_PADDING_WORDS(1);
412 u32_le nro_size;
413 INSERT_PADDING_WORDS(1);
414 u32_le text_offset;
415 u32_le text_size;
416 u32_le ro_offset;
417 u32_le ro_size;
418 u32_le rw_offset;
419 u32_le rw_size;
420 u32_le bss_size;
421 INSERT_PADDING_WORDS(1);
422 std::array<u8, 0x20> build_id;
423 INSERT_PADDING_BYTES(0x20);
424 };
425 static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
426
427 struct NRRHeader {
428 u32_le magic;
429 INSERT_PADDING_BYTES(0x1C);
430 u64_le title_id_mask;
431 u64_le title_id_pattern;
432 std::array<u8, 0x100> modulus;
433 std::array<u8, 0x100> signature_1;
434 std::array<u8, 0x100> signature_2;
435 u64_le title_id;
436 u32_le size;
437 INSERT_PADDING_BYTES(4);
438 u32_le hash_offset;
439 u32_le hash_count;
440 INSERT_PADDING_BYTES(8);
441 };
442 static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size.");
443
444 struct NROInfo {
445 SHA256Hash hash;
446 u64 size;
447 };
448
449 bool initialized = false;
450
451 std::map<VAddr, NROInfo> nro;
452 std::map<VAddr, std::vector<SHA256Hash>> nrr;
453
454 bool IsValidNROHash(const SHA256Hash& hash) {
455 return std::any_of(
456 nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
457 return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
458 });
459 }
460
461 static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
462 return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
463 header.nro_size == nro_size && header.bss_size == bss_size &&
464 header.ro_offset == header.text_offset + header.text_size &&
465 header.rw_offset == header.ro_offset + header.ro_size &&
466 nro_size == header.rw_offset + header.rw_size &&
467 Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
468 Common::Is4KBAligned(header.rw_size);
469 }
118}; 470};
119 471
120void InstallInterfaces(SM::ServiceManager& sm) { 472void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index c89157a4d..4e5fdb16e 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -18,7 +18,7 @@ public:
18 ILogger() : ServiceFramework("ILogger") { 18 ILogger() : ServiceFramework("ILogger") {
19 static const FunctionInfo functions[] = { 19 static const FunctionInfo functions[] = {
20 {0x00000000, &ILogger::Initialize, "Initialize"}, 20 {0x00000000, &ILogger::Initialize, "Initialize"},
21 {0x00000001, nullptr, "SetDestination"}, 21 {0x00000001, &ILogger::SetDestination, "SetDestination"},
22 }; 22 };
23 RegisterHandlers(functions); 23 RegisterHandlers(functions);
24 } 24 }
@@ -178,6 +178,17 @@ private:
178 } 178 }
179 } 179 }
180 180
181 // This service function is intended to be used as a way to
182 // redirect logging output to different destinations, however,
183 // given we always want to see the logging output, it's sufficient
184 // to do nothing and return success here.
185 void SetDestination(Kernel::HLERequestContext& ctx) {
186 LOG_DEBUG(Service_LM, "called");
187
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(RESULT_SUCCESS);
190 }
191
181 std::ostringstream log_stream; 192 std::ostringstream log_stream;
182}; 193};
183 194
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 44accecb7..1066bf505 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -351,6 +351,14 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
351 font_sizes.push_back(region.size); 351 font_sizes.push_back(region.size);
352 } 352 }
353 353
354 // Resize buffers if game requests smaller size output.
355 font_codes.resize(
356 std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32)));
357 font_offsets.resize(
358 std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
359 font_sizes.resize(
360 std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
361
354 ctx.WriteBuffer(font_codes, 0); 362 ctx.WriteBuffer(font_codes, 0);
355 ctx.WriteBuffer(font_offsets, 1); 363 ctx.WriteBuffer(font_offsets, 1);
356 ctx.WriteBuffer(font_sizes, 2); 364 ctx.WriteBuffer(font_sizes, 2);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 7a88ae029..792d26e52 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -5,6 +5,8 @@
5#include <cstring> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core_timing.h"
9#include "core/core_timing_util.h"
8#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 10#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
9 11
10namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
@@ -33,6 +35,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
33 return ZBCQueryTable(input, output); 35 return ZBCQueryTable(input, output);
34 case IoctlCommand::IocFlushL2: 36 case IoctlCommand::IocFlushL2:
35 return FlushL2(input, output); 37 return FlushL2(input, output);
38 case IoctlCommand::IocGetGpuTime:
39 return GetGpuTime(input, output);
36 } 40 }
37 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 41 UNIMPLEMENTED_MSG("Unimplemented ioctl");
38 return 0; 42 return 0;
@@ -169,4 +173,13 @@ u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& outp
169 return 0; 173 return 0;
170} 174}
171 175
176u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
177 LOG_DEBUG(Service_NVDRV, "called");
178 IoctlGetGpuTime params{};
179 std::memcpy(&params, input.data(), input.size());
180 params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks());
181 std::memcpy(output.data(), &params, output.size());
182 return 0;
183}
184
172} // namespace Service::Nvidia::Devices 185} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 3bbf028ad..240435eea 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -156,6 +156,11 @@ private:
156 }; 156 };
157 static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size"); 157 static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size");
158 158
159 struct IoctlGetGpuTime {
160 u64_le gpu_time;
161 };
162 static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
163
159 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); 164 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
160 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); 165 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
161 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); 166 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
@@ -164,6 +169,7 @@ private:
164 u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); 169 u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
165 u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); 170 u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
166 u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); 171 u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
172 u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
167}; 173};
168 174
169} // namespace Service::Nvidia::Devices 175} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index ac3859353..602086eed 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -88,6 +88,20 @@ void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
88 rb.Push(RESULT_SUCCESS); 88 rb.Push(RESULT_SUCCESS);
89} 89}
90 90
91void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) {
92 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
93 IPC::ResponseBuilder rb{ctx, 2};
94 rb.Push(RESULT_SUCCESS);
95}
96
97void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
98 // According to SwitchBrew, this has no inputs and no outputs, so effectively does nothing on
99 // retail hardware.
100 LOG_DEBUG(Service_NVDRV, "called");
101 IPC::ResponseBuilder rb{ctx, 2};
102 rb.Push(RESULT_SUCCESS);
103}
104
91NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) 105NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
92 : ServiceFramework(name), nvdrv(std::move(nvdrv)) { 106 : ServiceFramework(name), nvdrv(std::move(nvdrv)) {
93 static const FunctionInfo functions[] = { 107 static const FunctionInfo functions[] = {
@@ -97,10 +111,10 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
97 {3, &NVDRV::Initialize, "Initialize"}, 111 {3, &NVDRV::Initialize, "Initialize"},
98 {4, &NVDRV::QueryEvent, "QueryEvent"}, 112 {4, &NVDRV::QueryEvent, "QueryEvent"},
99 {5, nullptr, "MapSharedMem"}, 113 {5, nullptr, "MapSharedMem"},
100 {6, nullptr, "GetStatus"}, 114 {6, &NVDRV::GetStatus, "GetStatus"},
101 {7, nullptr, "ForceSetClientPID"}, 115 {7, nullptr, "ForceSetClientPID"},
102 {8, &NVDRV::SetClientPID, "SetClientPID"}, 116 {8, &NVDRV::SetClientPID, "SetClientPID"},
103 {9, nullptr, "DumpGraphicsMemoryInfo"}, 117 {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
104 {10, nullptr, "InitializeDevtools"}, 118 {10, nullptr, "InitializeDevtools"},
105 {11, &NVDRV::Ioctl, "Ioctl2"}, 119 {11, &NVDRV::Ioctl, "Ioctl2"},
106 {12, nullptr, "Ioctl3"}, 120 {12, nullptr, "Ioctl3"},
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index d340893c2..5a1e4baa7 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -24,6 +24,8 @@ private:
24 void QueryEvent(Kernel::HLERequestContext& ctx); 24 void QueryEvent(Kernel::HLERequestContext& ctx);
25 void SetClientPID(Kernel::HLERequestContext& ctx); 25 void SetClientPID(Kernel::HLERequestContext& ctx);
26 void FinishInitialize(Kernel::HLERequestContext& ctx); 26 void FinishInitialize(Kernel::HLERequestContext& ctx);
27 void GetStatus(Kernel::HLERequestContext& ctx);
28 void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
27 29
28 std::shared_ptr<Module> nvdrv; 30 std::shared_ptr<Module> nvdrv;
29 31
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 464e79d01..9ca8483a5 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -63,6 +63,17 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService
63 return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)); 63 return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port));
64} 64}
65 65
66ResultCode ServiceManager::UnregisterService(const std::string& name) {
67 CASCADE_CODE(ValidateServiceName(name));
68
69 const auto iter = registered_services.find(name);
70 if (iter == registered_services.end())
71 return ERR_SERVICE_NOT_REGISTERED;
72
73 registered_services.erase(iter);
74 return RESULT_SUCCESS;
75}
76
66ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort( 77ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort(
67 const std::string& name) { 78 const std::string& name) {
68 79
@@ -127,13 +138,52 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
127 } 138 }
128} 139}
129 140
141void SM::RegisterService(Kernel::HLERequestContext& ctx) {
142 IPC::RequestParser rp{ctx};
143
144 const auto name_buf = rp.PopRaw<std::array<char, 8>>();
145 const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
146
147 const std::string name(name_buf.begin(), end);
148
149 const auto unk_bool = static_cast<bool>(rp.PopRaw<u32>());
150 const auto session_count = rp.PopRaw<u32>();
151
152 LOG_DEBUG(Service_SM, "called with unk_bool={}", unk_bool);
153
154 auto handle = service_manager->RegisterService(name, session_count);
155 if (handle.Failed()) {
156 LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}",
157 handle.Code().raw);
158 IPC::ResponseBuilder rb{ctx, 2};
159 rb.Push(handle.Code());
160 return;
161 }
162
163 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
164 rb.Push(handle.Code());
165 rb.PushMoveObjects(std::move(handle).Unwrap());
166}
167
168void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
169 IPC::RequestParser rp{ctx};
170
171 const auto name_buf = rp.PopRaw<std::array<char, 8>>();
172 const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
173
174 const std::string name(name_buf.begin(), end);
175
176 IPC::ResponseBuilder rb{ctx, 2};
177 rb.Push(service_manager->UnregisterService(name));
178}
179
130SM::SM(std::shared_ptr<ServiceManager> service_manager) 180SM::SM(std::shared_ptr<ServiceManager> service_manager)
131 : ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) { 181 : ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) {
132 static const FunctionInfo functions[] = { 182 static const FunctionInfo functions[] = {
133 {0x00000000, &SM::Initialize, "Initialize"}, 183 {0x00000000, &SM::Initialize, "Initialize"},
134 {0x00000001, &SM::GetService, "GetService"}, 184 {0x00000001, &SM::GetService, "GetService"},
135 {0x00000002, nullptr, "RegisterService"}, 185 {0x00000002, &SM::RegisterService, "RegisterService"},
136 {0x00000003, nullptr, "UnregisterService"}, 186 {0x00000003, &SM::UnregisterService, "UnregisterService"},
137 }; 187 };
138 RegisterHandlers(functions); 188 RegisterHandlers(functions);
139} 189}
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 4f8145dda..bef25433e 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -35,6 +35,8 @@ public:
35private: 35private:
36 void Initialize(Kernel::HLERequestContext& ctx); 36 void Initialize(Kernel::HLERequestContext& ctx);
37 void GetService(Kernel::HLERequestContext& ctx); 37 void GetService(Kernel::HLERequestContext& ctx);
38 void RegisterService(Kernel::HLERequestContext& ctx);
39 void UnregisterService(Kernel::HLERequestContext& ctx);
38 40
39 std::shared_ptr<ServiceManager> service_manager; 41 std::shared_ptr<ServiceManager> service_manager;
40}; 42};
@@ -48,6 +50,7 @@ public:
48 50
49 ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name, 51 ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name,
50 unsigned int max_sessions); 52 unsigned int max_sessions);
53 ResultCode UnregisterService(const std::string& name);
51 ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name); 54 ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name);
52 ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name); 55 ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name);
53 56
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index e3cbd7004..b3a196f65 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -23,7 +23,8 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, 23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, 24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, 25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
26 {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, 26 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
27 "CalculateStandardUserSystemClockDifferenceByUser"},
27 {501, nullptr, "CalculateSpanBetween"}, 28 {501, nullptr, "CalculateSpanBetween"},
28 }; 29 };
29 RegisterHandlers(functions); 30 RegisterHandlers(functions);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 85e7b1195..e561a0c52 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -299,6 +299,21 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
299 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot)); 299 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
300} 300}
301 301
302void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
303 Kernel::HLERequestContext& ctx) {
304 LOG_DEBUG(Service_Time, "called");
305
306 IPC::RequestParser rp{ctx};
307 const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
308 const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
309 const u64 difference =
310 snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
311
312 IPC::ResponseBuilder rb{ctx, 4};
313 rb.Push(RESULT_SUCCESS);
314 rb.PushRaw<u64>(difference);
315}
316
302Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 317Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
303 : ServiceFramework(name), time(std::move(time)) {} 318 : ServiceFramework(name), time(std::move(time)) {}
304 319
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 77871ae07..ea43fbea7 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -84,6 +84,7 @@ public:
84 void GetTimeZoneService(Kernel::HLERequestContext& ctx); 84 void GetTimeZoneService(Kernel::HLERequestContext& ctx);
85 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); 85 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
86 void GetClockSnapshot(Kernel::HLERequestContext& ctx); 86 void GetClockSnapshot(Kernel::HLERequestContext& ctx);
87 void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
87 88
88 protected: 89 protected:
89 std::shared_ptr<Module> time; 90 std::shared_ptr<Module> time;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index d764b2406..a72416084 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -237,6 +237,22 @@ private:
237 Data data{}; 237 Data data{};
238}; 238};
239 239
240/// Represents a parcel containing one int '0' as its data
241/// Used by DetachBuffer and Disconnect
242class IGBPEmptyResponseParcel : public Parcel {
243protected:
244 void SerializeData() override {
245 Write(data);
246 }
247
248private:
249 struct Data {
250 u32_le unk_0;
251 };
252
253 Data data{};
254};
255
240class IGBPSetPreallocatedBufferRequestParcel : public Parcel { 256class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
241public: 257public:
242 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) 258 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
@@ -494,7 +510,11 @@ private:
494 510
495 if (transaction == TransactionId::Connect) { 511 if (transaction == TransactionId::Connect) {
496 IGBPConnectRequestParcel request{ctx.ReadBuffer()}; 512 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
497 IGBPConnectResponseParcel response{1280, 720}; 513 IGBPConnectResponseParcel response{
514 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
515 Settings::values.resolution_factor),
516 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
517 Settings::values.resolution_factor)};
498 ctx.WriteBuffer(response.Serialize()); 518 ctx.WriteBuffer(response.Serialize());
499 } else if (transaction == TransactionId::SetPreallocatedBuffer) { 519 } else if (transaction == TransactionId::SetPreallocatedBuffer) {
500 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; 520 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
@@ -554,6 +574,12 @@ private:
554 ctx.WriteBuffer(response.Serialize()); 574 ctx.WriteBuffer(response.Serialize());
555 } else if (transaction == TransactionId::CancelBuffer) { 575 } else if (transaction == TransactionId::CancelBuffer) {
556 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); 576 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
577 } else if (transaction == TransactionId::Disconnect ||
578 transaction == TransactionId::DetachBuffer) {
579 const auto buffer = ctx.ReadBuffer();
580
581 IGBPEmptyResponseParcel response{};
582 ctx.WriteBuffer(response.Serialize());
557 } else { 583 } else {
558 ASSERT_MSG(false, "Unimplemented"); 584 ASSERT_MSG(false, "Unimplemented");
559 } 585 }
@@ -670,11 +696,15 @@ private:
670 rb.Push(RESULT_SUCCESS); 696 rb.Push(RESULT_SUCCESS);
671 697
672 if (Settings::values.use_docked_mode) { 698 if (Settings::values.use_docked_mode) {
673 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 699 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
674 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); 700 static_cast<u32>(Settings::values.resolution_factor));
701 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
702 static_cast<u32>(Settings::values.resolution_factor));
675 } else { 703 } else {
676 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); 704 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
677 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); 705 static_cast<u32>(Settings::values.resolution_factor));
706 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
707 static_cast<u32>(Settings::values.resolution_factor));
678 } 708 }
679 709
680 rb.PushRaw<float>(60.0f); 710 rb.PushRaw<float>(60.0f);
@@ -879,11 +909,15 @@ private:
879 rb.Push(RESULT_SUCCESS); 909 rb.Push(RESULT_SUCCESS);
880 910
881 if (Settings::values.use_docked_mode) { 911 if (Settings::values.use_docked_mode) {
882 rb.Push(static_cast<u64>(DisplayResolution::DockedWidth)); 912 rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) *
883 rb.Push(static_cast<u64>(DisplayResolution::DockedHeight)); 913 static_cast<u32>(Settings::values.resolution_factor));
914 rb.Push(static_cast<u64>(DisplayResolution::DockedHeight) *
915 static_cast<u32>(Settings::values.resolution_factor));
884 } else { 916 } else {
885 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth)); 917 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
886 rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight)); 918 static_cast<u32>(Settings::values.resolution_factor));
919 rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
920 static_cast<u32>(Settings::values.resolution_factor));
887 } 921 }
888 } 922 }
889 923
@@ -900,6 +934,8 @@ private:
900 void ListDisplays(Kernel::HLERequestContext& ctx) { 934 void ListDisplays(Kernel::HLERequestContext& ctx) {
901 IPC::RequestParser rp{ctx}; 935 IPC::RequestParser rp{ctx};
902 DisplayInfo display_info; 936 DisplayInfo display_info;
937 display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
938 display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
903 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 939 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
904 IPC::ResponseBuilder rb{ctx, 4}; 940 IPC::ResponseBuilder rb{ctx, 4};
905 rb.Push(RESULT_SUCCESS); 941 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index c8e491fec..fbbd6b0de 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -170,17 +170,20 @@ static constexpr u32 PageAlignSize(u32 size) {
170 arg_data.size()); 170 arg_data.size());
171 } 171 }
172 172
173 // Read MOD header
174 ModHeader mod_header{};
175 // Default .bss to NRO header bss size if MOD0 section doesn't exist 173 // Default .bss to NRO header bss size if MOD0 section doesn't exist
176 u32 bss_size{PageAlignSize(nro_header.bss_size)}; 174 u32 bss_size{PageAlignSize(nro_header.bss_size)};
175
176 // Read MOD header
177 ModHeader mod_header{};
177 std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset, 178 std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
178 sizeof(ModHeader)); 179 sizeof(ModHeader));
180
179 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; 181 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
180 if (has_mod_header) { 182 if (has_mod_header) {
181 // Resize program image to include .bss section and page align each section 183 // Resize program image to include .bss section and page align each section
182 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 184 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
183 } 185 }
186
184 codeset.DataSegment().size += bss_size; 187 codeset.DataSegment().size += bss_size;
185 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 188 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
186 189
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 0da159559..26fcd3405 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -10,6 +10,56 @@
10 10
11namespace Settings { 11namespace Settings {
12 12
13namespace NativeButton {
14const std::array<const char*, NumButtons> mapping = {{
15 "button_a",
16 "button_b",
17 "button_x",
18 "button_y",
19 "button_lstick",
20 "button_rstick",
21 "button_l",
22 "button_r",
23 "button_zl",
24 "button_zr",
25 "button_plus",
26 "button_minus",
27 "button_dleft",
28 "button_dup",
29 "button_dright",
30 "button_ddown",
31 "button_lstick_left",
32 "button_lstick_up",
33 "button_lstick_right",
34 "button_lstick_down",
35 "button_rstick_left",
36 "button_rstick_up",
37 "button_rstick_right",
38 "button_rstick_down",
39 "button_sl",
40 "button_sr",
41 "button_home",
42 "button_screenshot",
43}};
44}
45
46namespace NativeAnalog {
47const std::array<const char*, NumAnalogs> mapping = {{
48 "lstick",
49 "rstick",
50}};
51}
52
53namespace NativeMouseButton {
54const std::array<const char*, NumMouseButtons> mapping = {{
55 "left",
56 "right",
57 "middle",
58 "forward",
59 "back",
60}};
61}
62
13Values values = {}; 63Values values = {};
14 64
15void Apply() { 65void Apply() {
diff --git a/src/core/settings.h b/src/core/settings.h
index e424479f2..a0c5fd447 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -60,36 +60,7 @@ constexpr int BUTTON_NS_END = NumButtons;
60constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; 60constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
61constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; 61constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
62 62
63static const std::array<const char*, NumButtons> mapping = {{ 63extern const std::array<const char*, NumButtons> mapping;
64 "button_a",
65 "button_b",
66 "button_x",
67 "button_y",
68 "button_lstick",
69 "button_rstick",
70 "button_l",
71 "button_r",
72 "button_zl",
73 "button_zr",
74 "button_plus",
75 "button_minus",
76 "button_dleft",
77 "button_dup",
78 "button_dright",
79 "button_ddown",
80 "button_lstick_left",
81 "button_lstick_up",
82 "button_lstick_right",
83 "button_lstick_down",
84 "button_rstick_left",
85 "button_rstick_up",
86 "button_rstick_right",
87 "button_rstick_down",
88 "button_sl",
89 "button_sr",
90 "button_home",
91 "button_screenshot",
92}};
93 64
94} // namespace NativeButton 65} // namespace NativeButton
95 66
@@ -105,12 +76,273 @@ constexpr int STICK_HID_BEGIN = LStick;
105constexpr int STICK_HID_END = NumAnalogs; 76constexpr int STICK_HID_END = NumAnalogs;
106constexpr int NUM_STICKS_HID = NumAnalogs; 77constexpr int NUM_STICKS_HID = NumAnalogs;
107 78
108static const std::array<const char*, NumAnalogs> mapping = {{ 79extern const std::array<const char*, NumAnalogs> mapping;
109 "lstick",
110 "rstick",
111}};
112} // namespace NativeAnalog 80} // namespace NativeAnalog
113 81
82namespace NativeMouseButton {
83enum Values {
84 Left,
85 Right,
86 Middle,
87 Forward,
88 Back,
89
90 NumMouseButtons,
91};
92
93constexpr int MOUSE_HID_BEGIN = Left;
94constexpr int MOUSE_HID_END = NumMouseButtons;
95constexpr int NUM_MOUSE_HID = NumMouseButtons;
96
97extern const std::array<const char*, NumMouseButtons> mapping;
98} // namespace NativeMouseButton
99
100namespace NativeKeyboard {
101enum Keys {
102 None,
103 Error,
104
105 A = 4,
106 B,
107 C,
108 D,
109 E,
110 F,
111 G,
112 H,
113 I,
114 J,
115 K,
116 L,
117 M,
118 N,
119 O,
120 P,
121 Q,
122 R,
123 S,
124 T,
125 U,
126 V,
127 W,
128 X,
129 Y,
130 Z,
131 N1,
132 N2,
133 N3,
134 N4,
135 N5,
136 N6,
137 N7,
138 N8,
139 N9,
140 N0,
141 Enter,
142 Escape,
143 Backspace,
144 Tab,
145 Space,
146 Minus,
147 Equal,
148 LeftBrace,
149 RightBrace,
150 Backslash,
151 Tilde,
152 Semicolon,
153 Apostrophe,
154 Grave,
155 Comma,
156 Dot,
157 Slash,
158 CapsLockKey,
159
160 F1,
161 F2,
162 F3,
163 F4,
164 F5,
165 F6,
166 F7,
167 F8,
168 F9,
169 F10,
170 F11,
171 F12,
172
173 SystemRequest,
174 ScrollLockKey,
175 Pause,
176 Insert,
177 Home,
178 PageUp,
179 Delete,
180 End,
181 PageDown,
182 Right,
183 Left,
184 Down,
185 Up,
186
187 NumLockKey,
188 KPSlash,
189 KPAsterisk,
190 KPMinus,
191 KPPlus,
192 KPEnter,
193 KP1,
194 KP2,
195 KP3,
196 KP4,
197 KP5,
198 KP6,
199 KP7,
200 KP8,
201 KP9,
202 KP0,
203 KPDot,
204
205 Key102,
206 Compose,
207 Power,
208 KPEqual,
209
210 F13,
211 F14,
212 F15,
213 F16,
214 F17,
215 F18,
216 F19,
217 F20,
218 F21,
219 F22,
220 F23,
221 F24,
222
223 Open,
224 Help,
225 Properties,
226 Front,
227 Stop,
228 Repeat,
229 Undo,
230 Cut,
231 Copy,
232 Paste,
233 Find,
234 Mute,
235 VolumeUp,
236 VolumeDown,
237 CapsLockActive,
238 NumLockActive,
239 ScrollLockActive,
240 KPComma,
241
242 KPLeftParenthesis,
243 KPRightParenthesis,
244
245 LeftControlKey = 0xE0,
246 LeftShiftKey,
247 LeftAltKey,
248 LeftMetaKey,
249 RightControlKey,
250 RightShiftKey,
251 RightAltKey,
252 RightMetaKey,
253
254 MediaPlayPause,
255 MediaStopCD,
256 MediaPrevious,
257 MediaNext,
258 MediaEject,
259 MediaVolumeUp,
260 MediaVolumeDown,
261 MediaMute,
262 MediaWebsite,
263 MediaBack,
264 MediaForward,
265 MediaStop,
266 MediaFind,
267 MediaScrollUp,
268 MediaScrollDown,
269 MediaEdit,
270 MediaSleep,
271 MediaCoffee,
272 MediaRefresh,
273 MediaCalculator,
274
275 NumKeyboardKeys,
276};
277
278static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
279
280enum Modifiers {
281 LeftControl,
282 LeftShift,
283 LeftAlt,
284 LeftMeta,
285 RightControl,
286 RightShift,
287 RightAlt,
288 RightMeta,
289 CapsLock,
290 ScrollLock,
291 NumLock,
292
293 NumKeyboardMods,
294};
295
296constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
297constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
298constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
299
300constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
301constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
302constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
303
304} // namespace NativeKeyboard
305
306using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
307using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
308using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
309using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
310using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
311
312constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
313constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
314constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
315constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
316
317enum class ControllerType {
318 ProController,
319 DualJoycon,
320 RightJoycon,
321 LeftJoycon,
322};
323
324struct PlayerInput {
325 bool connected;
326 ControllerType type;
327 ButtonsRaw buttons;
328 AnalogsRaw analogs;
329
330 u32 body_color_right;
331 u32 button_color_right;
332 u32 body_color_left;
333 u32 button_color_left;
334};
335
336struct TouchscreenInput {
337 bool enabled;
338 std::string device;
339
340 u32 finger;
341 u32 diameter_x;
342 u32 diameter_y;
343 u32 rotation_angle;
344};
345
114struct Values { 346struct Values {
115 // System 347 // System
116 bool use_docked_mode; 348 bool use_docked_mode;
@@ -120,10 +352,22 @@ struct Values {
120 s32 language_index; 352 s32 language_index;
121 353
122 // Controls 354 // Controls
123 std::array<std::string, NativeButton::NumButtons> buttons; 355 std::array<PlayerInput, 10> players;
124 std::array<std::string, NativeAnalog::NumAnalogs> analogs; 356
357 bool mouse_enabled;
358 std::string mouse_device;
359 MouseButtonsRaw mouse_buttons;
360
361 bool keyboard_enabled;
362 KeyboardKeysRaw keyboard_keys;
363 KeyboardModsRaw keyboard_mods;
364
365 bool debug_pad_enabled;
366 ButtonsRaw debug_pad_buttons;
367 AnalogsRaw debug_pad_analogs;
368
125 std::string motion_device; 369 std::string motion_device;
126 std::string touch_device; 370 TouchscreenInput touchscreen;
127 std::atomic_bool is_device_reload_pending{true}; 371 std::atomic_bool is_device_reload_pending{true};
128 372
129 // Core 373 // Core
@@ -159,6 +403,7 @@ struct Values {
159 bool use_gdbstub; 403 bool use_gdbstub;
160 u16 gdbstub_port; 404 u16 gdbstub_port;
161 std::string program_args; 405 std::string program_args;
406 bool dump_exefs;
162 bool dump_nso; 407 bool dump_nso;
163 408
164 // WebService 409 // WebService
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index a780215c1..3f906a517 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -21,6 +21,8 @@ add_library(video_core STATIC
21 macro_interpreter.h 21 macro_interpreter.h
22 memory_manager.cpp 22 memory_manager.cpp
23 memory_manager.h 23 memory_manager.h
24 morton.cpp
25 morton.h
24 rasterizer_cache.cpp 26 rasterizer_cache.cpp
25 rasterizer_cache.h 27 rasterizer_cache.h
26 rasterizer_interface.h 28 rasterizer_interface.h
@@ -62,7 +64,6 @@ add_library(video_core STATIC
62 textures/decoders.cpp 64 textures/decoders.cpp
63 textures/decoders.h 65 textures/decoders.h
64 textures/texture.h 66 textures/texture.h
65 utils.h
66 video_core.cpp 67 video_core.cpp
67 video_core.h 68 video_core.h
68) 69)
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 1772882b2..f0a5470b9 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -34,8 +34,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is 34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
35 // needed for ARMS. 35 // needed for ARMS.
36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { 36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
37 regs.viewport[viewport].depth_range_near = 0.0f; 37 regs.viewports[viewport].depth_range_near = 0.0f;
38 regs.viewport[viewport].depth_range_far = 1.0f; 38 regs.viewports[viewport].depth_range_far = 1.0f;
39 } 39 }
40 // Doom and Bomberman seems to use the uninitialized registers and just enable blend 40 // Doom and Bomberman seems to use the uninitialized registers and just enable blend
41 // so initialize blend registers with sane values 41 // so initialize blend registers with sane values
@@ -66,6 +66,18 @@ void Maxwell3D::InitializeRegisterDefaults() {
66 regs.stencil_back_func_func = Regs::ComparisonOp::Always; 66 regs.stencil_back_func_func = Regs::ComparisonOp::Always;
67 regs.stencil_back_func_mask = 0xFFFFFFFF; 67 regs.stencil_back_func_mask = 0xFFFFFFFF;
68 regs.stencil_back_mask = 0xFFFFFFFF; 68 regs.stencil_back_mask = 0xFFFFFFFF;
69 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
70 // register carrying a default value. Assume it's OpenGL's default (1).
71 regs.point_size = 1.0f;
72
73 // TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a
74 // default of enabled fixes rendering here.
75 for (std::size_t color_mask = 0; color_mask < Regs::NumRenderTargets; color_mask++) {
76 regs.color_mask[color_mask].R.Assign(1);
77 regs.color_mask[color_mask].G.Assign(1);
78 regs.color_mask[color_mask].B.Assign(1);
79 regs.color_mask[color_mask].A.Assign(1);
80 }
69} 81}
70 82
71void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 83void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 0848b7121..e44a23135 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -389,6 +389,13 @@ public:
389 ReverseSubtract = 3, 389 ReverseSubtract = 3,
390 Min = 4, 390 Min = 4,
391 Max = 5, 391 Max = 5,
392
393 // These values are used by Nouveau and some games.
394 AddGL = 0x8006,
395 SubtractGL = 0x8007,
396 ReverseSubtractGL = 0x8008,
397 MinGL = 0x800a,
398 MaxGL = 0x800b
392 }; 399 };
393 400
394 enum class Factor : u32 { 401 enum class Factor : u32 {
@@ -480,6 +487,67 @@ public:
480 }; 487 };
481 }; 488 };
482 489
490 struct ViewportTransform {
491 f32 scale_x;
492 f32 scale_y;
493 f32 scale_z;
494 f32 translate_x;
495 f32 translate_y;
496 f32 translate_z;
497 INSERT_PADDING_WORDS(2);
498
499 MathUtil::Rectangle<s32> GetRect() const {
500 return {
501 GetX(), // left
502 GetY() + GetHeight(), // top
503 GetX() + GetWidth(), // right
504 GetY() // bottom
505 };
506 };
507
508 s32 GetX() const {
509 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
510 }
511
512 s32 GetY() const {
513 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
514 }
515
516 s32 GetWidth() const {
517 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
518 }
519
520 s32 GetHeight() const {
521 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
522 }
523 };
524
525 struct ScissorTest {
526 u32 enable;
527 union {
528 BitField<0, 16, u32> min_x;
529 BitField<16, 16, u32> max_x;
530 };
531 union {
532 BitField<0, 16, u32> min_y;
533 BitField<16, 16, u32> max_y;
534 };
535 u32 fill;
536 };
537
538 struct ViewPort {
539 union {
540 BitField<0, 16, u32> x;
541 BitField<16, 16, u32> width;
542 };
543 union {
544 BitField<0, 16, u32> y;
545 BitField<16, 16, u32> height;
546 };
547 float depth_range_near;
548 float depth_range_far;
549 };
550
483 bool IsShaderConfigEnabled(std::size_t index) const { 551 bool IsShaderConfigEnabled(std::size_t index) const {
484 // The VertexB is always enabled. 552 // The VertexB is always enabled.
485 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) { 553 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -505,55 +573,11 @@ public:
505 573
506 INSERT_PADDING_WORDS(0x2E); 574 INSERT_PADDING_WORDS(0x2E);
507 575
508 RenderTargetConfig rt[NumRenderTargets]; 576 std::array<RenderTargetConfig, NumRenderTargets> rt;
509
510 struct {
511 f32 scale_x;
512 f32 scale_y;
513 f32 scale_z;
514 f32 translate_x;
515 f32 translate_y;
516 f32 translate_z;
517 INSERT_PADDING_WORDS(2);
518
519 MathUtil::Rectangle<s32> GetRect() const {
520 return {
521 GetX(), // left
522 GetY() + GetHeight(), // top
523 GetX() + GetWidth(), // right
524 GetY() // bottom
525 };
526 };
527
528 s32 GetX() const {
529 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
530 }
531
532 s32 GetY() const {
533 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
534 }
535
536 s32 GetWidth() const {
537 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
538 }
539 577
540 s32 GetHeight() const { 578 std::array<ViewportTransform, NumViewports> viewport_transform;
541 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
542 }
543 } viewport_transform[NumViewports];
544 579
545 struct { 580 std::array<ViewPort, NumViewports> viewports;
546 union {
547 BitField<0, 16, u32> x;
548 BitField<16, 16, u32> width;
549 };
550 union {
551 BitField<0, 16, u32> y;
552 BitField<16, 16, u32> height;
553 };
554 float depth_range_near;
555 float depth_range_far;
556 } viewport[NumViewports];
557 581
558 INSERT_PADDING_WORDS(0x1D); 582 INSERT_PADDING_WORDS(0x1D);
559 583
@@ -566,24 +590,22 @@ public:
566 590
567 float clear_color[4]; 591 float clear_color[4];
568 float clear_depth; 592 float clear_depth;
593
569 INSERT_PADDING_WORDS(0x3); 594 INSERT_PADDING_WORDS(0x3);
595
570 s32 clear_stencil; 596 s32 clear_stencil;
571 597
572 INSERT_PADDING_WORDS(0x17); 598 INSERT_PADDING_WORDS(0x7);
573 599
574 struct { 600 u32 polygon_offset_point_enable;
575 u32 enable; 601 u32 polygon_offset_line_enable;
576 union { 602 u32 polygon_offset_fill_enable;
577 BitField<0, 16, u32> min_x; 603
578 BitField<16, 16, u32> max_x; 604 INSERT_PADDING_WORDS(0xD);
579 }; 605
580 union { 606 std::array<ScissorTest, NumViewports> scissor_test;
581 BitField<0, 16, u32> min_y;
582 BitField<16, 16, u32> max_y;
583 };
584 } scissor_test;
585 607
586 INSERT_PADDING_WORDS(0x52); 608 INSERT_PADDING_WORDS(0x15);
587 609
588 s32 stencil_back_func_ref; 610 s32 stencil_back_func_ref;
589 u32 stencil_back_mask; 611 u32 stencil_back_mask;
@@ -617,7 +639,16 @@ public:
617 } 639 }
618 } zeta; 640 } zeta;
619 641
620 INSERT_PADDING_WORDS(0x5B); 642 INSERT_PADDING_WORDS(0x41);
643
644 union {
645 BitField<0, 4, u32> stencil;
646 BitField<4, 4, u32> unknown;
647 BitField<8, 4, u32> scissor;
648 BitField<12, 4, u32> viewport;
649 } clear_flags;
650
651 INSERT_PADDING_WORDS(0x19);
621 652
622 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; 653 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
623 654
@@ -700,9 +731,12 @@ public:
700 u32 stencil_front_func_mask; 731 u32 stencil_front_func_mask;
701 u32 stencil_front_mask; 732 u32 stencil_front_mask;
702 733
703 INSERT_PADDING_WORDS(0x3); 734 INSERT_PADDING_WORDS(0x2);
735
736 u32 frag_color_clamp;
704 737
705 union { 738 union {
739 BitField<0, 1, u32> y_negate;
706 BitField<4, 1, u32> triangle_rast_flip; 740 BitField<4, 1, u32> triangle_rast_flip;
707 } screen_y_control; 741 } screen_y_control;
708 742
@@ -718,7 +752,12 @@ public:
718 752
719 u32 zeta_enable; 753 u32 zeta_enable;
720 754
721 INSERT_PADDING_WORDS(0x8); 755 union {
756 BitField<0, 1, u32> alpha_to_coverage;
757 BitField<4, 1, u32> alpha_to_one;
758 } multisample_control;
759
760 INSERT_PADDING_WORDS(0x7);
722 761
723 struct { 762 struct {
724 u32 tsc_address_high; 763 u32 tsc_address_high;
@@ -731,7 +770,11 @@ public:
731 } 770 }
732 } tsc; 771 } tsc;
733 772
734 INSERT_PADDING_WORDS(0x3); 773 INSERT_PADDING_WORDS(0x1);
774
775 float polygon_offset_factor;
776
777 INSERT_PADDING_WORDS(0x1);
735 778
736 struct { 779 struct {
737 u32 tic_address_high; 780 u32 tic_address_high;
@@ -756,7 +799,9 @@ public:
756 799
757 u32 framebuffer_srgb; 800 u32 framebuffer_srgb;
758 801
759 INSERT_PADDING_WORDS(0x12); 802 float polygon_offset_units;
803
804 INSERT_PADDING_WORDS(0x11);
760 805
761 union { 806 union {
762 BitField<2, 1, u32> coord_origin; 807 BitField<2, 1, u32> coord_origin;
@@ -833,7 +878,9 @@ public:
833 878
834 INSERT_PADDING_WORDS(0x7); 879 INSERT_PADDING_WORDS(0x7);
835 880
836 INSERT_PADDING_WORDS(0x20); 881 INSERT_PADDING_WORDS(0x1F);
882
883 float polygon_offset_clamp;
837 884
838 struct { 885 struct {
839 u32 is_instanced[NumVertexArrays]; 886 u32 is_instanced[NumVertexArrays];
@@ -1105,12 +1152,15 @@ private:
1105ASSERT_REG_POSITION(macros, 0x45); 1152ASSERT_REG_POSITION(macros, 0x45);
1106ASSERT_REG_POSITION(tfb_enabled, 0x1D1); 1153ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
1107ASSERT_REG_POSITION(rt, 0x200); 1154ASSERT_REG_POSITION(rt, 0x200);
1108ASSERT_REG_POSITION(viewport_transform[0], 0x280); 1155ASSERT_REG_POSITION(viewport_transform, 0x280);
1109ASSERT_REG_POSITION(viewport, 0x300); 1156ASSERT_REG_POSITION(viewports, 0x300);
1110ASSERT_REG_POSITION(vertex_buffer, 0x35D); 1157ASSERT_REG_POSITION(vertex_buffer, 0x35D);
1111ASSERT_REG_POSITION(clear_color[0], 0x360); 1158ASSERT_REG_POSITION(clear_color[0], 0x360);
1112ASSERT_REG_POSITION(clear_depth, 0x364); 1159ASSERT_REG_POSITION(clear_depth, 0x364);
1113ASSERT_REG_POSITION(clear_stencil, 0x368); 1160ASSERT_REG_POSITION(clear_stencil, 0x368);
1161ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370);
1162ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371);
1163ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372);
1114ASSERT_REG_POSITION(scissor_test, 0x380); 1164ASSERT_REG_POSITION(scissor_test, 0x380);
1115ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); 1165ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
1116ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); 1166ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
@@ -1118,6 +1168,7 @@ ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
1118ASSERT_REG_POSITION(color_mask_common, 0x3E4); 1168ASSERT_REG_POSITION(color_mask_common, 0x3E4);
1119ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); 1169ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
1120ASSERT_REG_POSITION(zeta, 0x3F8); 1170ASSERT_REG_POSITION(zeta, 0x3F8);
1171ASSERT_REG_POSITION(clear_flags, 0x43E);
1121ASSERT_REG_POSITION(vertex_attrib_format, 0x458); 1172ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
1122ASSERT_REG_POSITION(rt_control, 0x487); 1173ASSERT_REG_POSITION(rt_control, 0x487);
1123ASSERT_REG_POSITION(zeta_width, 0x48a); 1174ASSERT_REG_POSITION(zeta_width, 0x48a);
@@ -1141,11 +1192,14 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
1141ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); 1192ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
1142ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); 1193ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
1143ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); 1194ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
1195ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
1144ASSERT_REG_POSITION(screen_y_control, 0x4EB); 1196ASSERT_REG_POSITION(screen_y_control, 0x4EB);
1145ASSERT_REG_POSITION(vb_element_base, 0x50D); 1197ASSERT_REG_POSITION(vb_element_base, 0x50D);
1146ASSERT_REG_POSITION(point_size, 0x546); 1198ASSERT_REG_POSITION(point_size, 0x546);
1147ASSERT_REG_POSITION(zeta_enable, 0x54E); 1199ASSERT_REG_POSITION(zeta_enable, 0x54E);
1200ASSERT_REG_POSITION(multisample_control, 0x54F);
1148ASSERT_REG_POSITION(tsc, 0x557); 1201ASSERT_REG_POSITION(tsc, 0x557);
1202ASSERT_REG_POSITION(polygon_offset_factor, 0x55b);
1149ASSERT_REG_POSITION(tic, 0x55D); 1203ASSERT_REG_POSITION(tic, 0x55D);
1150ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); 1204ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);
1151ASSERT_REG_POSITION(stencil_back_op_fail, 0x566); 1205ASSERT_REG_POSITION(stencil_back_op_fail, 0x566);
@@ -1153,11 +1207,13 @@ ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567);
1153ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); 1207ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568);
1154ASSERT_REG_POSITION(stencil_back_func_func, 0x569); 1208ASSERT_REG_POSITION(stencil_back_func_func, 0x569);
1155ASSERT_REG_POSITION(framebuffer_srgb, 0x56E); 1209ASSERT_REG_POSITION(framebuffer_srgb, 0x56E);
1210ASSERT_REG_POSITION(polygon_offset_units, 0x56F);
1156ASSERT_REG_POSITION(point_coord_replace, 0x581); 1211ASSERT_REG_POSITION(point_coord_replace, 0x581);
1157ASSERT_REG_POSITION(code_address, 0x582); 1212ASSERT_REG_POSITION(code_address, 0x582);
1158ASSERT_REG_POSITION(draw, 0x585); 1213ASSERT_REG_POSITION(draw, 0x585);
1159ASSERT_REG_POSITION(primitive_restart, 0x591); 1214ASSERT_REG_POSITION(primitive_restart, 0x591);
1160ASSERT_REG_POSITION(index_array, 0x5F2); 1215ASSERT_REG_POSITION(index_array, 0x5F2);
1216ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
1161ASSERT_REG_POSITION(instanced_arrays, 0x620); 1217ASSERT_REG_POSITION(instanced_arrays, 0x620);
1162ASSERT_REG_POSITION(cull, 0x646); 1218ASSERT_REG_POSITION(cull, 0x646);
1163ASSERT_REG_POSITION(logic_op, 0x671); 1219ASSERT_REG_POSITION(logic_op, 0x671);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 83a6fd875..b9faaf8e0 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -82,6 +82,8 @@ union Attribute {
82 Position = 7, 82 Position = 7,
83 Attribute_0 = 8, 83 Attribute_0 = 8,
84 Attribute_31 = 39, 84 Attribute_31 = 39,
85 ClipDistances0123 = 44,
86 ClipDistances4567 = 45,
85 PointCoord = 46, 87 PointCoord = 46,
86 // This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex 88 // This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex
87 // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval 89 // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
@@ -153,6 +155,7 @@ enum class PredCondition : u64 {
153 NotEqual = 5, 155 NotEqual = 5,
154 GreaterEqual = 6, 156 GreaterEqual = 6,
155 LessThanWithNan = 9, 157 LessThanWithNan = 9,
158 LessEqualWithNan = 11,
156 GreaterThanWithNan = 12, 159 GreaterThanWithNan = 12,
157 NotEqualWithNan = 13, 160 NotEqualWithNan = 13,
158 GreaterEqualWithNan = 14, 161 GreaterEqualWithNan = 14,
@@ -261,7 +264,7 @@ enum class FlowCondition : u64 {
261 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? 264 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for?
262}; 265};
263 266
264enum class ControlCode : u64 { 267enum class ConditionCode : u64 {
265 F = 0, 268 F = 0,
266 LT = 1, 269 LT = 1,
267 EQ = 2, 270 EQ = 2,
@@ -365,6 +368,11 @@ enum class HalfPrecision : u64 {
365 FMZ = 2, 368 FMZ = 2,
366}; 369};
367 370
371enum class R2pMode : u64 {
372 Pr = 0,
373 Cc = 1,
374};
375
368enum class IpaInterpMode : u64 { 376enum class IpaInterpMode : u64 {
369 Linear = 0, 377 Linear = 0,
370 Perspective = 1, 378 Perspective = 1,
@@ -569,7 +577,6 @@ union Instruction {
569 BitField<39, 2, u64> tab5cb8_2; 577 BitField<39, 2, u64> tab5cb8_2;
570 BitField<41, 3, u64> tab5c68_1; 578 BitField<41, 3, u64> tab5c68_1;
571 BitField<44, 2, u64> tab5c68_0; 579 BitField<44, 2, u64> tab5c68_0;
572 BitField<47, 1, u64> cc;
573 BitField<48, 1, u64> negate_b; 580 BitField<48, 1, u64> negate_b;
574 } fmul; 581 } fmul;
575 582
@@ -831,7 +838,7 @@ union Instruction {
831 union { 838 union {
832 BitField<0, 3, u64> pred0; 839 BitField<0, 3, u64> pred0;
833 BitField<3, 3, u64> pred3; 840 BitField<3, 3, u64> pred3;
834 BitField<8, 5, ControlCode> cc; // flag in cc 841 BitField<8, 5, ConditionCode> cc; // flag in cc
835 BitField<39, 3, u64> pred39; 842 BitField<39, 3, u64> pred39;
836 BitField<42, 1, u64> neg_pred39; 843 BitField<42, 1, u64> neg_pred39;
837 BitField<45, 4, PredOperation> op; // op with pred39 844 BitField<45, 4, PredOperation> op; // op with pred39
@@ -855,6 +862,12 @@ union Instruction {
855 } hsetp2; 862 } hsetp2;
856 863
857 union { 864 union {
865 BitField<40, 1, R2pMode> mode;
866 BitField<41, 2, u64> byte;
867 BitField<20, 7, u64> immediate_mask;
868 } r2p;
869
870 union {
858 BitField<39, 3, u64> pred39; 871 BitField<39, 3, u64> pred39;
859 BitField<42, 1, u64> neg_pred; 872 BitField<42, 1, u64> neg_pred;
860 BitField<43, 1, u64> neg_a; 873 BitField<43, 1, u64> neg_a;
@@ -1235,7 +1248,7 @@ union Instruction {
1235 BitField<60, 1, u64> is_b_gpr; 1248 BitField<60, 1, u64> is_b_gpr;
1236 BitField<59, 1, u64> is_c_gpr; 1249 BitField<59, 1, u64> is_c_gpr;
1237 BitField<20, 24, s64> smem_imm; 1250 BitField<20, 24, s64> smem_imm;
1238 BitField<0, 5, ControlCode> flow_control_code; 1251 BitField<0, 5, ConditionCode> flow_condition_code;
1239 1252
1240 Attribute attribute; 1253 Attribute attribute;
1241 Sampler sampler; 1254 Sampler sampler;
@@ -1256,6 +1269,7 @@ public:
1256 BFE_C, 1269 BFE_C,
1257 BFE_R, 1270 BFE_R,
1258 BFE_IMM, 1271 BFE_IMM,
1272 BFI_IMM_R,
1259 BRA, 1273 BRA,
1260 PBK, 1274 PBK,
1261 LD_A, 1275 LD_A,
@@ -1381,6 +1395,7 @@ public:
1381 PSETP, 1395 PSETP,
1382 PSET, 1396 PSET,
1383 CSETP, 1397 CSETP,
1398 R2P_IMM,
1384 XMAD_IMM, 1399 XMAD_IMM,
1385 XMAD_CR, 1400 XMAD_CR,
1386 XMAD_RC, 1401 XMAD_RC,
@@ -1396,6 +1411,7 @@ public:
1396 ArithmeticHalf, 1411 ArithmeticHalf,
1397 ArithmeticHalfImmediate, 1412 ArithmeticHalfImmediate,
1398 Bfe, 1413 Bfe,
1414 Bfi,
1399 Shift, 1415 Shift,
1400 Ffma, 1416 Ffma,
1401 Hfma2, 1417 Hfma2,
@@ -1410,6 +1426,7 @@ public:
1410 HalfSetPredicate, 1426 HalfSetPredicate,
1411 PredicateSetPredicate, 1427 PredicateSetPredicate,
1412 PredicateSetRegister, 1428 PredicateSetRegister,
1429 RegisterSetPredicate,
1413 Conversion, 1430 Conversion,
1414 Xmad, 1431 Xmad,
1415 Unknown, 1432 Unknown,
@@ -1613,6 +1630,7 @@ private:
1613 INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), 1630 INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"),
1614 INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), 1631 INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"),
1615 INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), 1632 INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"),
1633 INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"),
1616 INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), 1634 INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
1617 INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), 1635 INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),
1618 INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"), 1636 INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"),
@@ -1647,6 +1665,7 @@ private:
1647 INST("0101000010001---", Id::PSET, Type::PredicateSetRegister, "PSET"), 1665 INST("0101000010001---", Id::PSET, Type::PredicateSetRegister, "PSET"),
1648 INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), 1666 INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"),
1649 INST("010100001010----", Id::CSETP, Type::PredicateSetPredicate, "CSETP"), 1667 INST("010100001010----", Id::CSETP, Type::PredicateSetPredicate, "CSETP"),
1668 INST("0011100-11110---", Id::R2P_IMM, Type::RegisterSetPredicate, "R2P_IMM"),
1650 INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), 1669 INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"),
1651 INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), 1670 INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"),
1652 INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), 1671 INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"),
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h
index a0e015c4b..99c34649f 100644
--- a/src/video_core/engines/shader_header.h
+++ b/src/video_core/engines/shader_header.h
@@ -62,7 +62,16 @@ struct Header {
62 INSERT_PADDING_BYTES(1); // ImapSystemValuesB 62 INSERT_PADDING_BYTES(1); // ImapSystemValuesB
63 INSERT_PADDING_BYTES(16); // ImapGenericVector[32] 63 INSERT_PADDING_BYTES(16); // ImapGenericVector[32]
64 INSERT_PADDING_BYTES(2); // ImapColor 64 INSERT_PADDING_BYTES(2); // ImapColor
65 INSERT_PADDING_BYTES(2); // ImapSystemValuesC 65 union {
66 BitField<0, 8, u16> clip_distances;
67 BitField<8, 1, u16> point_sprite_s;
68 BitField<9, 1, u16> point_sprite_t;
69 BitField<10, 1, u16> fog_coordinate;
70 BitField<12, 1, u16> tessellation_eval_point_u;
71 BitField<13, 1, u16> tessellation_eval_point_v;
72 BitField<14, 1, u16> instance_id;
73 BitField<15, 1, u16> vertex_id;
74 };
66 INSERT_PADDING_BYTES(5); // ImapFixedFncTexture[10] 75 INSERT_PADDING_BYTES(5); // ImapFixedFncTexture[10]
67 INSERT_PADDING_BYTES(1); // ImapReserved 76 INSERT_PADDING_BYTES(1); // ImapReserved
68 INSERT_PADDING_BYTES(3); // OmapSystemValuesA 77 INSERT_PADDING_BYTES(3); // OmapSystemValuesA
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 83c7e5b0b..51b3904f6 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -17,6 +17,8 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
17 switch (format) { 17 switch (format) {
18 case PixelFormat::ABGR8: 18 case PixelFormat::ABGR8:
19 return 4; 19 return 4;
20 default:
21 return 4;
20 } 22 }
21 23
22 UNREACHABLE(); 24 UNREACHABLE();
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 335a8d407..2b0dea5cd 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -35,6 +35,7 @@ void MacroInterpreter::Reset() {
35 // The next parameter index starts at 1, because $r1 already has the value of the first 35 // The next parameter index starts at 1, because $r1 already has the value of the first
36 // parameter. 36 // parameter.
37 next_parameter_index = 1; 37 next_parameter_index = 1;
38 carry_flag = false;
38} 39}
39 40
40bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { 41bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
@@ -135,14 +136,28 @@ MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const {
135 return {macro_memory[offset + pc / sizeof(u32)]}; 136 return {macro_memory[offset + pc / sizeof(u32)]};
136} 137}
137 138
138u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const { 139u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) {
139 switch (operation) { 140 switch (operation) {
140 case ALUOperation::Add: 141 case ALUOperation::Add: {
141 return src_a + src_b; 142 const u64 result{static_cast<u64>(src_a) + src_b};
142 // TODO(Subv): Implement AddWithCarry 143 carry_flag = result > 0xffffffff;
143 case ALUOperation::Subtract: 144 return static_cast<u32>(result);
144 return src_a - src_b; 145 }
145 // TODO(Subv): Implement SubtractWithBorrow 146 case ALUOperation::AddWithCarry: {
147 const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)};
148 carry_flag = result > 0xffffffff;
149 return static_cast<u32>(result);
150 }
151 case ALUOperation::Subtract: {
152 const u64 result{static_cast<u64>(src_a) - src_b};
153 carry_flag = result < 0x100000000;
154 return static_cast<u32>(result);
155 }
156 case ALUOperation::SubtractWithBorrow: {
157 const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)};
158 carry_flag = result < 0x100000000;
159 return static_cast<u32>(result);
160 }
146 case ALUOperation::Xor: 161 case ALUOperation::Xor:
147 return src_a ^ src_b; 162 return src_a ^ src_b;
148 case ALUOperation::Or: 163 case ALUOperation::Or:
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h
index 62d1ce289..cde360288 100644
--- a/src/video_core/macro_interpreter.h
+++ b/src/video_core/macro_interpreter.h
@@ -117,7 +117,7 @@ private:
117 bool Step(u32 offset, bool is_delay_slot); 117 bool Step(u32 offset, bool is_delay_slot);
118 118
119 /// Calculates the result of an ALU operation. src_a OP src_b; 119 /// Calculates the result of an ALU operation. src_a OP src_b;
120 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const; 120 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b);
121 121
122 /// Performs the result operation on the input result and stores it in the specified register 122 /// Performs the result operation on the input result and stores it in the specified register
123 /// (if necessary). 123 /// (if necessary).
@@ -165,5 +165,7 @@ private:
165 std::vector<u32> parameters; 165 std::vector<u32> parameters;
166 /// Index of the next parameter that will be fetched by the 'parm' instruction. 166 /// Index of the next parameter that will be fetched by the 'parm' instruction.
167 u32 next_parameter_index = 0; 167 u32 next_parameter_index = 0;
168
169 bool carry_flag{};
168}; 170};
169} // namespace Tegra 171} // namespace Tegra
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 77a20bb84..47247f097 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -9,6 +9,13 @@
9 9
10namespace Tegra { 10namespace Tegra {
11 11
12MemoryManager::MemoryManager() {
13 // Mark the first page as reserved, so that 0 is not a valid GPUVAddr. Otherwise, games might
14 // try to use 0 as a valid address, which is also used to mean nullptr. This fixes a bug with
15 // Undertale using 0 for a render target.
16 PageSlot(0) = static_cast<u64>(PageStatus::Reserved);
17}
18
12GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { 19GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
13 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, align, PageStatus::Unmapped)}; 20 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, align, PageStatus::Unmapped)};
14 21
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 4eb338aa2..fb03497ca 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -18,7 +18,7 @@ using GPUVAddr = u64;
18 18
19class MemoryManager final { 19class MemoryManager final {
20public: 20public:
21 MemoryManager() = default; 21 MemoryManager();
22 22
23 GPUVAddr AllocateSpace(u64 size, u64 align); 23 GPUVAddr AllocateSpace(u64 size, u64 align);
24 GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align); 24 GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align);
@@ -37,6 +37,7 @@ private:
37 enum class PageStatus : u64 { 37 enum class PageStatus : u64 {
38 Unmapped = 0xFFFFFFFFFFFFFFFFULL, 38 Unmapped = 0xFFFFFFFFFFFFFFFFULL,
39 Allocated = 0xFFFFFFFFFFFFFFFEULL, 39 Allocated = 0xFFFFFFFFFFFFFFFEULL,
40 Reserved = 0xFFFFFFFFFFFFFFFDULL,
40 }; 41 };
41 42
42 std::optional<GPUVAddr> FindFreeBlock(GPUVAddr region_start, u64 size, u64 align, 43 std::optional<GPUVAddr> FindFreeBlock(GPUVAddr region_start, u64 size, u64 align,
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
new file mode 100644
index 000000000..f14abba7d
--- /dev/null
+++ b/src/video_core/morton.cpp
@@ -0,0 +1,353 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstring>
7#include "common/assert.h"
8#include "common/common_types.h"
9#include "core/memory.h"
10#include "video_core/morton.h"
11#include "video_core/surface.h"
12#include "video_core/textures/decoders.h"
13
14namespace VideoCore {
15
16using Surface::GetBytesPerPixel;
17using Surface::PixelFormat;
18
19using MortonCopyFn = void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr);
20using ConversionArray = std::array<MortonCopyFn, Surface::MaxPixelFormat>;
21
22template <bool morton_to_linear, PixelFormat format>
23static void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth,
24 u8* buffer, std::size_t buffer_size, VAddr addr) {
25 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format);
26
27 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
28 // pixel values.
29 const u32 tile_size_x{GetDefaultBlockWidth(format)};
30 const u32 tile_size_y{GetDefaultBlockHeight(format)};
31
32 if constexpr (morton_to_linear) {
33 Tegra::Texture::UnswizzleTexture(buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel,
34 stride, height, depth, block_height, block_depth);
35 } else {
36 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
37 (height + tile_size_y - 1) / tile_size_y, depth,
38 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
39 buffer, false, block_height, block_depth);
40 }
41}
42
43static constexpr ConversionArray morton_to_linear_fns = {
44 // clang-format off
45 MortonCopy<true, PixelFormat::ABGR8U>,
46 MortonCopy<true, PixelFormat::ABGR8S>,
47 MortonCopy<true, PixelFormat::ABGR8UI>,
48 MortonCopy<true, PixelFormat::B5G6R5U>,
49 MortonCopy<true, PixelFormat::A2B10G10R10U>,
50 MortonCopy<true, PixelFormat::A1B5G5R5U>,
51 MortonCopy<true, PixelFormat::R8U>,
52 MortonCopy<true, PixelFormat::R8UI>,
53 MortonCopy<true, PixelFormat::RGBA16F>,
54 MortonCopy<true, PixelFormat::RGBA16U>,
55 MortonCopy<true, PixelFormat::RGBA16UI>,
56 MortonCopy<true, PixelFormat::R11FG11FB10F>,
57 MortonCopy<true, PixelFormat::RGBA32UI>,
58 MortonCopy<true, PixelFormat::DXT1>,
59 MortonCopy<true, PixelFormat::DXT23>,
60 MortonCopy<true, PixelFormat::DXT45>,
61 MortonCopy<true, PixelFormat::DXN1>,
62 MortonCopy<true, PixelFormat::DXN2UNORM>,
63 MortonCopy<true, PixelFormat::DXN2SNORM>,
64 MortonCopy<true, PixelFormat::BC7U>,
65 MortonCopy<true, PixelFormat::BC6H_UF16>,
66 MortonCopy<true, PixelFormat::BC6H_SF16>,
67 MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
68 MortonCopy<true, PixelFormat::G8R8U>,
69 MortonCopy<true, PixelFormat::G8R8S>,
70 MortonCopy<true, PixelFormat::BGRA8>,
71 MortonCopy<true, PixelFormat::RGBA32F>,
72 MortonCopy<true, PixelFormat::RG32F>,
73 MortonCopy<true, PixelFormat::R32F>,
74 MortonCopy<true, PixelFormat::R16F>,
75 MortonCopy<true, PixelFormat::R16U>,
76 MortonCopy<true, PixelFormat::R16S>,
77 MortonCopy<true, PixelFormat::R16UI>,
78 MortonCopy<true, PixelFormat::R16I>,
79 MortonCopy<true, PixelFormat::RG16>,
80 MortonCopy<true, PixelFormat::RG16F>,
81 MortonCopy<true, PixelFormat::RG16UI>,
82 MortonCopy<true, PixelFormat::RG16I>,
83 MortonCopy<true, PixelFormat::RG16S>,
84 MortonCopy<true, PixelFormat::RGB32F>,
85 MortonCopy<true, PixelFormat::RGBA8_SRGB>,
86 MortonCopy<true, PixelFormat::RG8U>,
87 MortonCopy<true, PixelFormat::RG8S>,
88 MortonCopy<true, PixelFormat::RG32UI>,
89 MortonCopy<true, PixelFormat::R32UI>,
90 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
91 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
92 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
93 MortonCopy<true, PixelFormat::BGRA8_SRGB>,
94 MortonCopy<true, PixelFormat::DXT1_SRGB>,
95 MortonCopy<true, PixelFormat::DXT23_SRGB>,
96 MortonCopy<true, PixelFormat::DXT45_SRGB>,
97 MortonCopy<true, PixelFormat::BC7U_SRGB>,
98 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>,
99 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
100 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
101 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
102 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
103 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
104 MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
105 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
106 MortonCopy<true, PixelFormat::Z32F>,
107 MortonCopy<true, PixelFormat::Z16>,
108 MortonCopy<true, PixelFormat::Z24S8>,
109 MortonCopy<true, PixelFormat::S8Z24>,
110 MortonCopy<true, PixelFormat::Z32FS8>,
111 // clang-format on
112};
113
114static constexpr ConversionArray linear_to_morton_fns = {
115 // clang-format off
116 MortonCopy<false, PixelFormat::ABGR8U>,
117 MortonCopy<false, PixelFormat::ABGR8S>,
118 MortonCopy<false, PixelFormat::ABGR8UI>,
119 MortonCopy<false, PixelFormat::B5G6R5U>,
120 MortonCopy<false, PixelFormat::A2B10G10R10U>,
121 MortonCopy<false, PixelFormat::A1B5G5R5U>,
122 MortonCopy<false, PixelFormat::R8U>,
123 MortonCopy<false, PixelFormat::R8UI>,
124 MortonCopy<false, PixelFormat::RGBA16F>,
125 MortonCopy<false, PixelFormat::RGBA16U>,
126 MortonCopy<false, PixelFormat::RGBA16UI>,
127 MortonCopy<false, PixelFormat::R11FG11FB10F>,
128 MortonCopy<false, PixelFormat::RGBA32UI>,
129 MortonCopy<false, PixelFormat::DXT1>,
130 MortonCopy<false, PixelFormat::DXT23>,
131 MortonCopy<false, PixelFormat::DXT45>,
132 MortonCopy<false, PixelFormat::DXN1>,
133 MortonCopy<false, PixelFormat::DXN2UNORM>,
134 MortonCopy<false, PixelFormat::DXN2SNORM>,
135 MortonCopy<false, PixelFormat::BC7U>,
136 MortonCopy<false, PixelFormat::BC6H_UF16>,
137 MortonCopy<false, PixelFormat::BC6H_SF16>,
138 // TODO(Subv): Swizzling ASTC formats are not supported
139 nullptr,
140 MortonCopy<false, PixelFormat::G8R8U>,
141 MortonCopy<false, PixelFormat::G8R8S>,
142 MortonCopy<false, PixelFormat::BGRA8>,
143 MortonCopy<false, PixelFormat::RGBA32F>,
144 MortonCopy<false, PixelFormat::RG32F>,
145 MortonCopy<false, PixelFormat::R32F>,
146 MortonCopy<false, PixelFormat::R16F>,
147 MortonCopy<false, PixelFormat::R16U>,
148 MortonCopy<false, PixelFormat::R16S>,
149 MortonCopy<false, PixelFormat::R16UI>,
150 MortonCopy<false, PixelFormat::R16I>,
151 MortonCopy<false, PixelFormat::RG16>,
152 MortonCopy<false, PixelFormat::RG16F>,
153 MortonCopy<false, PixelFormat::RG16UI>,
154 MortonCopy<false, PixelFormat::RG16I>,
155 MortonCopy<false, PixelFormat::RG16S>,
156 MortonCopy<false, PixelFormat::RGB32F>,
157 MortonCopy<false, PixelFormat::RGBA8_SRGB>,
158 MortonCopy<false, PixelFormat::RG8U>,
159 MortonCopy<false, PixelFormat::RG8S>,
160 MortonCopy<false, PixelFormat::RG32UI>,
161 MortonCopy<false, PixelFormat::R32UI>,
162 nullptr,
163 nullptr,
164 nullptr,
165 MortonCopy<false, PixelFormat::BGRA8_SRGB>,
166 MortonCopy<false, PixelFormat::DXT1_SRGB>,
167 MortonCopy<false, PixelFormat::DXT23_SRGB>,
168 MortonCopy<false, PixelFormat::DXT45_SRGB>,
169 MortonCopy<false, PixelFormat::BC7U_SRGB>,
170 nullptr,
171 nullptr,
172 nullptr,
173 nullptr,
174 nullptr,
175 nullptr,
176 nullptr,
177 nullptr,
178 MortonCopy<false, PixelFormat::Z32F>,
179 MortonCopy<false, PixelFormat::Z16>,
180 MortonCopy<false, PixelFormat::Z24S8>,
181 MortonCopy<false, PixelFormat::S8Z24>,
182 MortonCopy<false, PixelFormat::Z32FS8>,
183 // clang-format on
184};
185
186constexpr MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) {
187 switch (mode) {
188 case MortonSwizzleMode::MortonToLinear:
189 return morton_to_linear_fns[static_cast<std::size_t>(format)];
190 case MortonSwizzleMode::LinearToMorton:
191 return linear_to_morton_fns[static_cast<std::size_t>(format)];
192 }
193}
194
195/// 8x8 Z-Order coordinate from 2D coordinates
196static u32 MortonInterleave(u32 x, u32 y) {
197 static const u32 xlut[] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15};
198 static const u32 ylut[] = {0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a};
199 return xlut[x % 8] + ylut[y % 8];
200}
201
202/// Calculates the offset of the position of the pixel in Morton order
203static u32 GetMortonOffset(u32 x, u32 y, u32 bytes_per_pixel) {
204 // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each
205 // of which is composed of four 2x2 subtiles each of which is composed of four texels.
206 // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g.
207 // texels are laid out in a 2x2 subtile like this:
208 // 2 3
209 // 0 1
210 //
211 // The full 8x8 tile has the texels arranged like this:
212 //
213 // 42 43 46 47 58 59 62 63
214 // 40 41 44 45 56 57 60 61
215 // 34 35 38 39 50 51 54 55
216 // 32 33 36 37 48 49 52 53
217 // 10 11 14 15 26 27 30 31
218 // 08 09 12 13 24 25 28 29
219 // 02 03 06 07 18 19 22 23
220 // 00 01 04 05 16 17 20 21
221 //
222 // This pattern is what's called Z-order curve, or Morton order.
223
224 const unsigned int block_height = 8;
225 const unsigned int coarse_x = x & ~7;
226
227 u32 i = MortonInterleave(x, y);
228
229 const unsigned int offset = coarse_x * block_height;
230
231 return (i + offset) * bytes_per_pixel;
232}
233
234static u32 MortonInterleave128(u32 x, u32 y) {
235 // 128x128 Z-Order coordinate from 2D coordinates
236 static constexpr u32 xlut[] = {
237 0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042,
238 0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809,
239 0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000,
240 0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043,
241 0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a,
242 0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001,
243 0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048,
244 0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b,
245 0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002,
246 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049,
247 0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840,
248 0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003,
249 0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a,
250 0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841,
251 0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008,
252 0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b,
253 0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842,
254 0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009,
255 0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800,
256 0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843,
257 0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a,
258 0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801,
259 0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848,
260 0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b,
261 0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802,
262 0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849,
263 0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040,
264 0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803,
265 0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a,
266 0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041,
267 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808,
268 0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b,
269 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042,
270 0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809,
271 0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b,
272 };
273 static constexpr u32 ylut[] = {
274 0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090,
275 0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124,
276 0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200,
277 0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294,
278 0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330,
279 0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404,
280 0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0,
281 0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534,
282 0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610,
283 0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4,
284 0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780,
285 0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014,
286 0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0,
287 0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184,
288 0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220,
289 0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4,
290 0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390,
291 0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424,
292 0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500,
293 0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594,
294 0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630,
295 0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704,
296 0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0,
297 0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034,
298 0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110,
299 0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4,
300 0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280,
301 0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314,
302 0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0,
303 0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484,
304 0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520,
305 0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4,
306 0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690,
307 0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724,
308 0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4,
309 };
310 return xlut[x % 128] + ylut[y % 128];
311}
312
313static u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
314 // Calculates the offset of the position of the pixel in Morton order
315 // Framebuffer images are split into 128x128 tiles.
316
317 constexpr u32 block_height = 128;
318 const u32 coarse_x = x & ~127;
319
320 const u32 i = MortonInterleave128(x, y);
321
322 const u32 offset = coarse_x * block_height;
323
324 return (i + offset) * bytes_per_pixel;
325}
326
327void MortonSwizzle(MortonSwizzleMode mode, Surface::PixelFormat format, u32 stride,
328 u32 block_height, u32 height, u32 block_depth, u32 depth, u8* buffer,
329 std::size_t buffer_size, VAddr addr) {
330
331 GetSwizzleFunction(mode, format)(stride, block_height, height, block_depth, depth, buffer,
332 buffer_size, addr);
333}
334
335void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 linear_bytes_per_pixel,
336 u8* morton_data, u8* linear_data, bool morton_to_linear) {
337 u8* data_ptrs[2];
338 for (u32 y = 0; y < height; ++y) {
339 for (u32 x = 0; x < width; ++x) {
340 const u32 coarse_y = y & ~127;
341 const u32 morton_offset =
342 GetMortonOffset128(x, y, bytes_per_pixel) + coarse_y * width * bytes_per_pixel;
343 const u32 linear_pixel_index = (x + y * width) * linear_bytes_per_pixel;
344
345 data_ptrs[morton_to_linear ? 1 : 0] = morton_data + morton_offset;
346 data_ptrs[morton_to_linear ? 0 : 1] = &linear_data[linear_pixel_index];
347
348 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
349 }
350 }
351}
352
353} // namespace VideoCore \ No newline at end of file
diff --git a/src/video_core/morton.h b/src/video_core/morton.h
new file mode 100644
index 000000000..b9b9eca86
--- /dev/null
+++ b/src/video_core/morton.h
@@ -0,0 +1,21 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "video_core/surface.h"
9
10namespace VideoCore {
11
12enum class MortonSwizzleMode { MortonToLinear, LinearToMorton };
13
14void MortonSwizzle(MortonSwizzleMode mode, VideoCore::Surface::PixelFormat format, u32 stride,
15 u32 block_height, u32 height, u32 block_depth, u32 depth, u8* buffer,
16 std::size_t buffer_size, VAddr addr);
17
18void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 linear_bytes_per_pixel,
19 u8* morton_data, u8* linear_data, bool morton_to_linear);
20
21} // namespace VideoCore \ No newline at end of file
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index cb0d0c16a..1f9acda36 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -98,17 +98,10 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
98 has_ARB_direct_state_access = true; 98 has_ARB_direct_state_access = true;
99 } else if (extension == "GL_ARB_multi_bind") { 99 } else if (extension == "GL_ARB_multi_bind") {
100 has_ARB_multi_bind = true; 100 has_ARB_multi_bind = true;
101 } else if (extension == "GL_ARB_separate_shader_objects") {
102 has_ARB_separate_shader_objects = true;
103 } else if (extension == "GL_ARB_vertex_attrib_binding") {
104 has_ARB_vertex_attrib_binding = true;
105 } 101 }
106 } 102 }
107 103
108 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
109 OpenGLState::ApplyDefaultState(); 104 OpenGLState::ApplyDefaultState();
110 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
111 state.clip_distance[0] = true;
112 105
113 // Create render framebuffer 106 // Create render framebuffer
114 framebuffer.Create(); 107 framebuffer.Create();
@@ -556,6 +549,30 @@ void RasterizerOpenGL::Clear() {
556 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); 549 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!");
557 use_stencil = true; 550 use_stencil = true;
558 clear_state.stencil.test_enabled = true; 551 clear_state.stencil.test_enabled = true;
552 if (regs.clear_flags.stencil) {
553 // Stencil affects the clear so fill it with the used masks
554 clear_state.stencil.front.test_func = GL_ALWAYS;
555 clear_state.stencil.front.test_mask = regs.stencil_front_func_mask;
556 clear_state.stencil.front.action_stencil_fail = GL_KEEP;
557 clear_state.stencil.front.action_depth_fail = GL_KEEP;
558 clear_state.stencil.front.action_depth_pass = GL_KEEP;
559 clear_state.stencil.front.write_mask = regs.stencil_front_mask;
560 if (regs.stencil_two_side_enable) {
561 clear_state.stencil.back.test_func = GL_ALWAYS;
562 clear_state.stencil.back.test_mask = regs.stencil_back_func_mask;
563 clear_state.stencil.back.action_stencil_fail = GL_KEEP;
564 clear_state.stencil.back.action_depth_fail = GL_KEEP;
565 clear_state.stencil.back.action_depth_pass = GL_KEEP;
566 clear_state.stencil.back.write_mask = regs.stencil_back_mask;
567 } else {
568 clear_state.stencil.back.test_func = GL_ALWAYS;
569 clear_state.stencil.back.test_mask = 0xFFFFFFFF;
570 clear_state.stencil.back.write_mask = 0xFFFFFFFF;
571 clear_state.stencil.back.action_stencil_fail = GL_KEEP;
572 clear_state.stencil.back.action_depth_fail = GL_KEEP;
573 clear_state.stencil.back.action_depth_pass = GL_KEEP;
574 }
575 }
559 } 576 }
560 577
561 if (!use_color && !use_depth && !use_stencil) { 578 if (!use_color && !use_depth && !use_stencil) {
@@ -567,6 +584,14 @@ void RasterizerOpenGL::Clear() {
567 584
568 ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false, 585 ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
569 regs.clear_buffers.RT.Value()); 586 regs.clear_buffers.RT.Value());
587 if (regs.clear_flags.scissor) {
588 SyncScissorTest(clear_state);
589 }
590
591 if (regs.clear_flags.viewport) {
592 clear_state.EmulateViewportWithScissor();
593 }
594
570 clear_state.Apply(); 595 clear_state.Apply();
571 596
572 if (use_color) { 597 if (use_color) {
@@ -594,18 +619,20 @@ void RasterizerOpenGL::DrawArrays() {
594 619
595 ConfigureFramebuffers(state); 620 ConfigureFramebuffers(state);
596 SyncColorMask(); 621 SyncColorMask();
622 SyncFragmentColorClampState();
623 SyncMultiSampleState();
597 SyncDepthTestState(); 624 SyncDepthTestState();
598 SyncStencilTestState(); 625 SyncStencilTestState();
599 SyncBlendState(); 626 SyncBlendState();
600 SyncLogicOpState(); 627 SyncLogicOpState();
601 SyncCullMode(); 628 SyncCullMode();
602 SyncPrimitiveRestart(); 629 SyncPrimitiveRestart();
603 SyncScissorTest(); 630 SyncScissorTest(state);
604 // Alpha Testing is synced on shaders. 631 // Alpha Testing is synced on shaders.
605 SyncTransformFeedback(); 632 SyncTransformFeedback();
606 SyncPointState(); 633 SyncPointState();
607 CheckAlphaTests(); 634 CheckAlphaTests();
608 635 SyncPolygonOffset();
609 // TODO(bunnei): Sync framebuffer_scale uniform here 636 // TODO(bunnei): Sync framebuffer_scale uniform here
610 // TODO(bunnei): Sync scissorbox uniform(s) here 637 // TODO(bunnei): Sync scissorbox uniform(s) here
611 638
@@ -658,7 +685,7 @@ void RasterizerOpenGL::DrawArrays() {
658 params.DispatchDraw(); 685 params.DispatchDraw();
659 686
660 // Disable scissor test 687 // Disable scissor test
661 state.scissor.enabled = false; 688 state.viewports[0].scissor.enabled = false;
662 689
663 accelerate_draw = AccelDraw::Disabled; 690 accelerate_draw = AccelDraw::Disabled;
664 691
@@ -749,9 +776,8 @@ void RasterizerOpenGL::SamplerInfo::Create() {
749 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); 776 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
750} 777}
751 778
752void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) { 779void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
753 const GLuint s = sampler.handle; 780 const GLuint s = sampler.handle;
754 const Tegra::Texture::TSCEntry& config = info.tsc;
755 if (mag_filter != config.mag_filter) { 781 if (mag_filter != config.mag_filter) {
756 mag_filter = config.mag_filter; 782 mag_filter = config.mag_filter;
757 glSamplerParameteri( 783 glSamplerParameteri(
@@ -793,30 +819,50 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTex
793 MaxwellToGL::DepthCompareFunc(depth_compare_func)); 819 MaxwellToGL::DepthCompareFunc(depth_compare_func));
794 } 820 }
795 821
796 if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border || 822 GLvec4 new_border_color;
797 wrap_p == Tegra::Texture::WrapMode::Border) { 823 if (config.srgb_conversion) {
798 const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, 824 new_border_color[0] = config.srgb_border_color_r / 255.0f;
799 config.border_color_b, config.border_color_a}}; 825 new_border_color[1] = config.srgb_border_color_g / 255.0f;
800 if (border_color != new_border_color) { 826 new_border_color[2] = config.srgb_border_color_g / 255.0f;
801 border_color = new_border_color; 827 } else {
802 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); 828 new_border_color[0] = config.border_color_r;
803 } 829 new_border_color[1] = config.border_color_g;
830 new_border_color[2] = config.border_color_b;
804 } 831 }
805 if (info.tic.use_header_opt_control == 0) { 832 new_border_color[3] = config.border_color_a;
833
834 if (border_color != new_border_color) {
835 border_color = new_border_color;
836 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
837 }
838
839 const float anisotropic_max = static_cast<float>(1 << config.max_anisotropy.Value());
840 if (anisotropic_max != max_anisotropic) {
841 max_anisotropic = anisotropic_max;
806 if (GLAD_GL_ARB_texture_filter_anisotropic) { 842 if (GLAD_GL_ARB_texture_filter_anisotropic) {
807 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, 843 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
808 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
809 } else if (GLAD_GL_EXT_texture_filter_anisotropic) { 844 } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
810 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, 845 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
811 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
812 } 846 }
813 glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, 847 }
814 static_cast<float>(info.tic.res_min_mip_level.Value())); 848 const float lod_min = static_cast<float>(config.min_lod_clamp.Value()) / 256.0f;
815 glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, 849 if (lod_min != min_lod) {
816 static_cast<float>(info.tic.res_max_mip_level.Value() == 0 850 min_lod = lod_min;
817 ? 16 851 glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, min_lod);
818 : info.tic.res_max_mip_level.Value())); 852 }
819 glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f); 853
854 const float lod_max = static_cast<float>(config.max_lod_clamp.Value()) / 256.0f;
855 if (lod_max != max_lod) {
856 max_lod = lod_max;
857 glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, max_lod);
858 }
859 const u32 bias = config.mip_lod_bias.Value();
860 // Sign extend the 13-bit value.
861 constexpr u32 mask = 1U << (13 - 1);
862 const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f;
863 if (lod_bias != bias_lod) {
864 lod_bias = bias_lod;
865 glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias);
820 } 866 }
821} 867}
822 868
@@ -915,7 +961,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
915 continue; 961 continue;
916 } 962 }
917 963
918 texture_samplers[current_bindpoint].SyncWithConfig(texture); 964 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
919 Surface surface = res_cache.GetTextureSurface(texture, entry); 965 Surface surface = res_cache.GetTextureSurface(texture, entry);
920 if (surface != nullptr) { 966 if (surface != nullptr) {
921 state.texture_units[current_bindpoint].texture = surface->Texture().handle; 967 state.texture_units[current_bindpoint].texture = surface->Texture().handle;
@@ -939,15 +985,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
939 985
940void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { 986void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
941 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 987 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
942 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 988 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
943 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; 989 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
944 auto& viewport = current_state.viewports[i]; 990 auto& viewport = current_state.viewports[i];
945 viewport.x = viewport_rect.left; 991 viewport.x = viewport_rect.left;
946 viewport.y = viewport_rect.bottom; 992 viewport.y = viewport_rect.bottom;
947 viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth()); 993 viewport.width = viewport_rect.GetWidth();
948 viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight()); 994 viewport.height = viewport_rect.GetHeight();
949 viewport.depth_range_far = regs.viewport[i].depth_range_far; 995 viewport.depth_range_far = regs.viewports[i].depth_range_far;
950 viewport.depth_range_near = regs.viewport[i].depth_range_near; 996 viewport.depth_range_near = regs.viewports[i].depth_range_near;
951 } 997 }
952} 998}
953 999
@@ -1038,7 +1084,9 @@ void RasterizerOpenGL::SyncStencilTestState() {
1038 1084
1039void RasterizerOpenGL::SyncColorMask() { 1085void RasterizerOpenGL::SyncColorMask() {
1040 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1086 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1041 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1087 const std::size_t count =
1088 regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
1089 for (std::size_t i = 0; i < count; i++) {
1042 const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i]; 1090 const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
1043 auto& dest = state.color_mask[i]; 1091 auto& dest = state.color_mask[i];
1044 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE; 1092 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
@@ -1048,6 +1096,17 @@ void RasterizerOpenGL::SyncColorMask() {
1048 } 1096 }
1049} 1097}
1050 1098
1099void RasterizerOpenGL::SyncMultiSampleState() {
1100 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1101 state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
1102 state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
1103}
1104
1105void RasterizerOpenGL::SyncFragmentColorClampState() {
1106 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1107 state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
1108}
1109
1051void RasterizerOpenGL::SyncBlendState() { 1110void RasterizerOpenGL::SyncBlendState() {
1052 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1111 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1053 1112
@@ -1059,43 +1118,40 @@ void RasterizerOpenGL::SyncBlendState() {
1059 state.independant_blend.enabled = regs.independent_blend_enable; 1118 state.independant_blend.enabled = regs.independent_blend_enable;
1060 if (!state.independant_blend.enabled) { 1119 if (!state.independant_blend.enabled) {
1061 auto& blend = state.blend[0]; 1120 auto& blend = state.blend[0];
1062 blend.enabled = regs.blend.enable[0] != 0; 1121 const auto& src = regs.blend;
1063 blend.separate_alpha = regs.blend.separate_alpha; 1122 blend.enabled = src.enable[0] != 0;
1064 blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb); 1123 if (blend.enabled) {
1065 blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb); 1124 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
1066 blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb); 1125 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
1067 if (blend.separate_alpha) { 1126 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
1068 blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a); 1127 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
1069 blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a); 1128 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
1070 blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a); 1129 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
1071 } 1130 }
1072 for (size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1131 for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1073 state.blend[i].enabled = false; 1132 state.blend[i].enabled = false;
1074 } 1133 }
1075 return; 1134 return;
1076 } 1135 }
1077 1136
1078 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1137 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1079 auto& blend = state.blend[i]; 1138 auto& blend = state.blend[i];
1139 const auto& src = regs.independent_blend[i];
1080 blend.enabled = regs.blend.enable[i] != 0; 1140 blend.enabled = regs.blend.enable[i] != 0;
1081 if (!blend.enabled) 1141 if (!blend.enabled)
1082 continue; 1142 continue;
1083 blend.separate_alpha = regs.independent_blend[i].separate_alpha; 1143 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
1084 blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_rgb); 1144 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
1085 blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_rgb); 1145 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
1086 blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_rgb); 1146 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
1087 if (blend.separate_alpha) { 1147 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
1088 blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_a); 1148 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
1089 blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_a);
1090 blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_a);
1091 }
1092 } 1149 }
1093} 1150}
1094 1151
1095void RasterizerOpenGL::SyncLogicOpState() { 1152void RasterizerOpenGL::SyncLogicOpState() {
1096 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1153 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1097 1154
1098 // TODO(Subv): Support more than just render target 0.
1099 state.logic_op.enabled = regs.logic_op.enable != 0; 1155 state.logic_op.enabled = regs.logic_op.enable != 0;
1100 1156
1101 if (!state.logic_op.enabled) 1157 if (!state.logic_op.enabled)
@@ -1107,20 +1163,22 @@ void RasterizerOpenGL::SyncLogicOpState() {
1107 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 1163 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
1108} 1164}
1109 1165
1110void RasterizerOpenGL::SyncScissorTest() { 1166void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
1111 // TODO: what is the correct behavior here, a single scissor for all targets
1112 // or scissor disabled for the rest of the targets?
1113 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1167 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1114 state.scissor.enabled = (regs.scissor_test.enable != 0); 1168 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
1115 if (regs.scissor_test.enable == 0) { 1169 const auto& src = regs.scissor_test[i];
1116 return; 1170 auto& dst = current_state.viewports[i].scissor;
1171 dst.enabled = (src.enable != 0);
1172 if (dst.enabled == 0) {
1173 return;
1174 }
1175 const u32 width = src.max_x - src.min_x;
1176 const u32 height = src.max_y - src.min_y;
1177 dst.x = src.min_x;
1178 dst.y = src.min_y;
1179 dst.width = width;
1180 dst.height = height;
1117 } 1181 }
1118 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
1119 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
1120 state.scissor.x = regs.scissor_test.min_x;
1121 state.scissor.y = regs.scissor_test.min_y;
1122 state.scissor.width = width;
1123 state.scissor.height = height;
1124} 1182}
1125 1183
1126void RasterizerOpenGL::SyncTransformFeedback() { 1184void RasterizerOpenGL::SyncTransformFeedback() {
@@ -1134,11 +1192,17 @@ void RasterizerOpenGL::SyncTransformFeedback() {
1134 1192
1135void RasterizerOpenGL::SyncPointState() { 1193void RasterizerOpenGL::SyncPointState() {
1136 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1194 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1195 state.point.size = regs.point_size;
1196}
1137 1197
1138 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a 1198void RasterizerOpenGL::SyncPolygonOffset() {
1139 // register carrying a default value. For now, if the point size is zero, assume it's 1199 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1140 // OpenGL's default (1). 1200 state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0;
1141 state.point.size = regs.point_size == 0 ? 1 : regs.point_size; 1201 state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0;
1202 state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0;
1203 state.polygon_offset.units = regs.polygon_offset_units;
1204 state.polygon_offset.factor = regs.polygon_offset_factor;
1205 state.polygon_offset.clamp = regs.polygon_offset_clamp;
1142} 1206}
1143 1207
1144void RasterizerOpenGL::CheckAlphaTests() { 1208void RasterizerOpenGL::CheckAlphaTests() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 8ef0f6c12..dfb4616f2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -88,18 +88,23 @@ private:
88 /// SamplerInfo struct. 88 /// SamplerInfo struct.
89 void Create(); 89 void Create();
90 /// Syncs the sampler object with the config, updating any necessary state. 90 /// Syncs the sampler object with the config, updating any necessary state.
91 void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info); 91 void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
92 92
93 private: 93 private:
94 Tegra::Texture::TextureFilter mag_filter; 94 Tegra::Texture::TextureFilter mag_filter = Tegra::Texture::TextureFilter::Nearest;
95 Tegra::Texture::TextureFilter min_filter; 95 Tegra::Texture::TextureFilter min_filter = Tegra::Texture::TextureFilter::Nearest;
96 Tegra::Texture::TextureMipmapFilter mip_filter; 96 Tegra::Texture::TextureMipmapFilter mip_filter = Tegra::Texture::TextureMipmapFilter::None;
97 Tegra::Texture::WrapMode wrap_u; 97 Tegra::Texture::WrapMode wrap_u = Tegra::Texture::WrapMode::ClampToEdge;
98 Tegra::Texture::WrapMode wrap_v; 98 Tegra::Texture::WrapMode wrap_v = Tegra::Texture::WrapMode::ClampToEdge;
99 Tegra::Texture::WrapMode wrap_p; 99 Tegra::Texture::WrapMode wrap_p = Tegra::Texture::WrapMode::ClampToEdge;
100 bool uses_depth_compare; 100 bool uses_depth_compare = false;
101 Tegra::Texture::DepthCompareFunc depth_compare_func; 101 Tegra::Texture::DepthCompareFunc depth_compare_func =
102 GLvec4 border_color; 102 Tegra::Texture::DepthCompareFunc::Always;
103 GLvec4 border_color = {};
104 float min_lod = 0.0f;
105 float max_lod = 16.0f;
106 float lod_bias = 0.0f;
107 float max_anisotropic = 1.0f;
103 }; 108 };
104 109
105 /** 110 /**
@@ -160,8 +165,14 @@ private:
160 /// Syncs the LogicOp state to match the guest state 165 /// Syncs the LogicOp state to match the guest state
161 void SyncLogicOpState(); 166 void SyncLogicOpState();
162 167
168 /// Syncs the the color clamp state
169 void SyncFragmentColorClampState();
170
171 /// Syncs the alpha coverage and alpha to one
172 void SyncMultiSampleState();
173
163 /// Syncs the scissor test state to match the guest state 174 /// Syncs the scissor test state to match the guest state
164 void SyncScissorTest(); 175 void SyncScissorTest(OpenGLState& current_state);
165 176
166 /// Syncs the transform feedback state to match the guest state 177 /// Syncs the transform feedback state to match the guest state
167 void SyncTransformFeedback(); 178 void SyncTransformFeedback();
@@ -172,13 +183,14 @@ private:
172 /// Syncs Color Mask 183 /// Syncs Color Mask
173 void SyncColorMask(); 184 void SyncColorMask();
174 185
186 /// Syncs the polygon offsets
187 void SyncPolygonOffset();
188
175 /// Check asserts for alpha testing. 189 /// Check asserts for alpha testing.
176 void CheckAlphaTests(); 190 void CheckAlphaTests();
177 191
178 bool has_ARB_direct_state_access = false; 192 bool has_ARB_direct_state_access = false;
179 bool has_ARB_multi_bind = false; 193 bool has_ARB_multi_bind = false;
180 bool has_ARB_separate_shader_objects = false;
181 bool has_ARB_vertex_attrib_binding = false;
182 194
183 OpenGLState state; 195 OpenGLState state;
184 196
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index b44ecfa1c..d458f77e4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -15,6 +15,7 @@
15#include "core/memory.h" 15#include "core/memory.h"
16#include "core/settings.h" 16#include "core/settings.h"
17#include "video_core/engines/maxwell_3d.h" 17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/morton.h"
18#include "video_core/renderer_opengl/gl_rasterizer.h" 19#include "video_core/renderer_opengl/gl_rasterizer.h"
19#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 20#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
20#include "video_core/renderer_opengl/gl_state.h" 21#include "video_core/renderer_opengl/gl_state.h"
@@ -22,10 +23,11 @@
22#include "video_core/surface.h" 23#include "video_core/surface.h"
23#include "video_core/textures/astc.h" 24#include "video_core/textures/astc.h"
24#include "video_core/textures/decoders.h" 25#include "video_core/textures/decoders.h"
25#include "video_core/utils.h"
26 26
27namespace OpenGL { 27namespace OpenGL {
28 28
29using VideoCore::MortonSwizzle;
30using VideoCore::MortonSwizzleMode;
29using VideoCore::Surface::ComponentTypeFromDepthFormat; 31using VideoCore::Surface::ComponentTypeFromDepthFormat;
30using VideoCore::Surface::ComponentTypeFromRenderTarget; 32using VideoCore::Surface::ComponentTypeFromRenderTarget;
31using VideoCore::Surface::ComponentTypeFromTexture; 33using VideoCore::Surface::ComponentTypeFromTexture;
@@ -265,11 +267,11 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
265 {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 267 {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
266 true}, // DXN2UNORM 268 true}, // DXN2UNORM
267 {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM 269 {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM
268 {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 270 {GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
269 true}, // BC7U 271 true}, // BC7U
270 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, 272 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
271 ComponentType::Float, true}, // BC6H_UF16 273 true}, // BC6H_UF16
272 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float, 274 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
273 true}, // BC6H_SF16 275 true}, // BC6H_SF16
274 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 276 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
275 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U 277 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U
@@ -306,8 +308,8 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
306 true}, // DXT23_SRGB 308 true}, // DXT23_SRGB
307 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 309 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
308 true}, // DXT45_SRGB 310 true}, // DXT45_SRGB
309 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 311 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
310 ComponentType::UNorm, true}, // BC7U_SRGB 312 true}, // BC7U_SRGB
311 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB 313 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB
312 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB 314 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
313 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB 315 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
@@ -346,7 +348,7 @@ static GLenum SurfaceTargetToGL(SurfaceTarget target) {
346 case SurfaceTarget::TextureCubemap: 348 case SurfaceTarget::TextureCubemap:
347 return GL_TEXTURE_CUBE_MAP; 349 return GL_TEXTURE_CUBE_MAP;
348 case SurfaceTarget::TextureCubeArray: 350 case SurfaceTarget::TextureCubeArray:
349 return GL_TEXTURE_CUBE_MAP_ARRAY_ARB; 351 return GL_TEXTURE_CUBE_MAP_ARRAY;
350 } 352 }
351 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); 353 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
352 UNREACHABLE(); 354 UNREACHABLE();
@@ -370,177 +372,7 @@ MathUtil::Rectangle<u32> SurfaceParams::GetRect(u32 mip_level) const {
370 return {0, actual_height, MipWidth(mip_level), 0}; 372 return {0, actual_height, MipWidth(mip_level), 0};
371} 373}
372 374
373template <bool morton_to_gl, PixelFormat format> 375void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
374void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
375 std::size_t gl_buffer_size, VAddr addr) {
376 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format);
377
378 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
379 // pixel values.
380 const u32 tile_size_x{GetDefaultBlockWidth(format)};
381 const u32 tile_size_y{GetDefaultBlockHeight(format)};
382
383 if (morton_to_gl) {
384 const std::vector<u8> data =
385 Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel,
386 stride, height, depth, block_height, block_depth);
387 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
388 memcpy(gl_buffer, data.data(), size_to_copy);
389 } else {
390 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
391 (height + tile_size_y - 1) / tile_size_y, depth,
392 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
393 gl_buffer, false, block_height, block_depth);
394 }
395}
396
397using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
398 VideoCore::Surface::MaxPixelFormat>;
399
400static constexpr GLConversionArray morton_to_gl_fns = {
401 // clang-format off
402 MortonCopy<true, PixelFormat::ABGR8U>,
403 MortonCopy<true, PixelFormat::ABGR8S>,
404 MortonCopy<true, PixelFormat::ABGR8UI>,
405 MortonCopy<true, PixelFormat::B5G6R5U>,
406 MortonCopy<true, PixelFormat::A2B10G10R10U>,
407 MortonCopy<true, PixelFormat::A1B5G5R5U>,
408 MortonCopy<true, PixelFormat::R8U>,
409 MortonCopy<true, PixelFormat::R8UI>,
410 MortonCopy<true, PixelFormat::RGBA16F>,
411 MortonCopy<true, PixelFormat::RGBA16U>,
412 MortonCopy<true, PixelFormat::RGBA16UI>,
413 MortonCopy<true, PixelFormat::R11FG11FB10F>,
414 MortonCopy<true, PixelFormat::RGBA32UI>,
415 MortonCopy<true, PixelFormat::DXT1>,
416 MortonCopy<true, PixelFormat::DXT23>,
417 MortonCopy<true, PixelFormat::DXT45>,
418 MortonCopy<true, PixelFormat::DXN1>,
419 MortonCopy<true, PixelFormat::DXN2UNORM>,
420 MortonCopy<true, PixelFormat::DXN2SNORM>,
421 MortonCopy<true, PixelFormat::BC7U>,
422 MortonCopy<true, PixelFormat::BC6H_UF16>,
423 MortonCopy<true, PixelFormat::BC6H_SF16>,
424 MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
425 MortonCopy<true, PixelFormat::G8R8U>,
426 MortonCopy<true, PixelFormat::G8R8S>,
427 MortonCopy<true, PixelFormat::BGRA8>,
428 MortonCopy<true, PixelFormat::RGBA32F>,
429 MortonCopy<true, PixelFormat::RG32F>,
430 MortonCopy<true, PixelFormat::R32F>,
431 MortonCopy<true, PixelFormat::R16F>,
432 MortonCopy<true, PixelFormat::R16U>,
433 MortonCopy<true, PixelFormat::R16S>,
434 MortonCopy<true, PixelFormat::R16UI>,
435 MortonCopy<true, PixelFormat::R16I>,
436 MortonCopy<true, PixelFormat::RG16>,
437 MortonCopy<true, PixelFormat::RG16F>,
438 MortonCopy<true, PixelFormat::RG16UI>,
439 MortonCopy<true, PixelFormat::RG16I>,
440 MortonCopy<true, PixelFormat::RG16S>,
441 MortonCopy<true, PixelFormat::RGB32F>,
442 MortonCopy<true, PixelFormat::RGBA8_SRGB>,
443 MortonCopy<true, PixelFormat::RG8U>,
444 MortonCopy<true, PixelFormat::RG8S>,
445 MortonCopy<true, PixelFormat::RG32UI>,
446 MortonCopy<true, PixelFormat::R32UI>,
447 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
448 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
449 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
450 MortonCopy<true, PixelFormat::BGRA8_SRGB>,
451 MortonCopy<true, PixelFormat::DXT1_SRGB>,
452 MortonCopy<true, PixelFormat::DXT23_SRGB>,
453 MortonCopy<true, PixelFormat::DXT45_SRGB>,
454 MortonCopy<true, PixelFormat::BC7U_SRGB>,
455 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>,
456 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
457 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
458 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
459 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
460 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
461 MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
462 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
463 MortonCopy<true, PixelFormat::Z32F>,
464 MortonCopy<true, PixelFormat::Z16>,
465 MortonCopy<true, PixelFormat::Z24S8>,
466 MortonCopy<true, PixelFormat::S8Z24>,
467 MortonCopy<true, PixelFormat::Z32FS8>,
468 // clang-format on
469};
470
471static constexpr GLConversionArray gl_to_morton_fns = {
472 // clang-format off
473 MortonCopy<false, PixelFormat::ABGR8U>,
474 MortonCopy<false, PixelFormat::ABGR8S>,
475 MortonCopy<false, PixelFormat::ABGR8UI>,
476 MortonCopy<false, PixelFormat::B5G6R5U>,
477 MortonCopy<false, PixelFormat::A2B10G10R10U>,
478 MortonCopy<false, PixelFormat::A1B5G5R5U>,
479 MortonCopy<false, PixelFormat::R8U>,
480 MortonCopy<false, PixelFormat::R8UI>,
481 MortonCopy<false, PixelFormat::RGBA16F>,
482 MortonCopy<false, PixelFormat::RGBA16U>,
483 MortonCopy<false, PixelFormat::RGBA16UI>,
484 MortonCopy<false, PixelFormat::R11FG11FB10F>,
485 MortonCopy<false, PixelFormat::RGBA32UI>,
486 MortonCopy<false, PixelFormat::DXT1>,
487 MortonCopy<false, PixelFormat::DXT23>,
488 MortonCopy<false, PixelFormat::DXT45>,
489 MortonCopy<false, PixelFormat::DXN1>,
490 MortonCopy<false, PixelFormat::DXN2UNORM>,
491 MortonCopy<false, PixelFormat::DXN2SNORM>,
492 MortonCopy<false, PixelFormat::BC7U>,
493 MortonCopy<false, PixelFormat::BC6H_UF16>,
494 MortonCopy<false, PixelFormat::BC6H_SF16>,
495 // TODO(Subv): Swizzling ASTC formats are not supported
496 nullptr,
497 MortonCopy<false, PixelFormat::G8R8U>,
498 MortonCopy<false, PixelFormat::G8R8S>,
499 MortonCopy<false, PixelFormat::BGRA8>,
500 MortonCopy<false, PixelFormat::RGBA32F>,
501 MortonCopy<false, PixelFormat::RG32F>,
502 MortonCopy<false, PixelFormat::R32F>,
503 MortonCopy<false, PixelFormat::R16F>,
504 MortonCopy<false, PixelFormat::R16U>,
505 MortonCopy<false, PixelFormat::R16S>,
506 MortonCopy<false, PixelFormat::R16UI>,
507 MortonCopy<false, PixelFormat::R16I>,
508 MortonCopy<false, PixelFormat::RG16>,
509 MortonCopy<false, PixelFormat::RG16F>,
510 MortonCopy<false, PixelFormat::RG16UI>,
511 MortonCopy<false, PixelFormat::RG16I>,
512 MortonCopy<false, PixelFormat::RG16S>,
513 MortonCopy<false, PixelFormat::RGB32F>,
514 MortonCopy<false, PixelFormat::RGBA8_SRGB>,
515 MortonCopy<false, PixelFormat::RG8U>,
516 MortonCopy<false, PixelFormat::RG8S>,
517 MortonCopy<false, PixelFormat::RG32UI>,
518 MortonCopy<false, PixelFormat::R32UI>,
519 nullptr,
520 nullptr,
521 nullptr,
522 MortonCopy<false, PixelFormat::BGRA8_SRGB>,
523 MortonCopy<false, PixelFormat::DXT1_SRGB>,
524 MortonCopy<false, PixelFormat::DXT23_SRGB>,
525 MortonCopy<false, PixelFormat::DXT45_SRGB>,
526 MortonCopy<false, PixelFormat::BC7U_SRGB>,
527 nullptr,
528 nullptr,
529 nullptr,
530 nullptr,
531 nullptr,
532 nullptr,
533 nullptr,
534 nullptr,
535 MortonCopy<false, PixelFormat::Z32F>,
536 MortonCopy<false, PixelFormat::Z16>,
537 MortonCopy<false, PixelFormat::Z24S8>,
538 MortonCopy<false, PixelFormat::S8Z24>,
539 MortonCopy<false, PixelFormat::Z32FS8>,
540 // clang-format on
541};
542
543void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params,
544 std::vector<u8>& gl_buffer, u32 mip_level) { 376 std::vector<u8>& gl_buffer, u32 mip_level) {
545 u32 depth = params.MipDepth(mip_level); 377 u32 depth = params.MipDepth(mip_level);
546 if (params.target == SurfaceTarget::Texture2D) { 378 if (params.target == SurfaceTarget::Texture2D) {
@@ -553,19 +385,19 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
553 const u64 layer_size = params.LayerMemorySize(); 385 const u64 layer_size = params.LayerMemorySize();
554 const u64 gl_size = params.LayerSizeGL(mip_level); 386 const u64 gl_size = params.LayerSizeGL(mip_level);
555 for (u32 i = 0; i < params.depth; i++) { 387 for (u32 i = 0; i < params.depth; i++) {
556 functions[static_cast<std::size_t>(params.pixel_format)]( 388 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
557 params.MipWidth(mip_level), params.MipBlockHeight(mip_level), 389 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
558 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), 1, 390 params.MipBlockDepth(mip_level), 1, gl_buffer.data() + offset_gl, gl_size,
559 gl_buffer.data() + offset_gl, gl_size, params.addr + offset); 391 params.addr + offset);
560 offset += layer_size; 392 offset += layer_size;
561 offset_gl += gl_size; 393 offset_gl += gl_size;
562 } 394 }
563 } else { 395 } else {
564 const u64 offset = params.GetMipmapLevelOffset(mip_level); 396 const u64 offset = params.GetMipmapLevelOffset(mip_level);
565 functions[static_cast<std::size_t>(params.pixel_format)]( 397 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
566 params.MipWidth(mip_level), params.MipBlockHeight(mip_level), 398 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
567 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(), 399 params.MipBlockDepth(mip_level), depth, gl_buffer.data(), gl_buffer.size(),
568 gl_buffer.size(), params.addr + offset); 400 params.addr + offset);
569 } 401 }
570} 402}
571 403
@@ -729,7 +561,7 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
729 const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes); 561 const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
730 562
731 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 563 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
732 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 564 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW);
733 if (source_format.compressed) { 565 if (source_format.compressed) {
734 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, 566 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
735 static_cast<GLsizei>(src_params.size_in_bytes), nullptr); 567 static_cast<GLsizei>(src_params.size_in_bytes), nullptr);
@@ -999,7 +831,7 @@ void CachedSurface::LoadGLBuffer() {
999 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 831 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
1000 params.block_width, static_cast<u32>(params.target)); 832 params.block_width, static_cast<u32>(params.target));
1001 for (u32 i = 0; i < params.max_mip_level; i++) 833 for (u32 i = 0; i < params.max_mip_level; i++)
1002 SwizzleFunc(morton_to_gl_fns, params, gl_buffer[i], i); 834 SwizzleFunc(MortonSwizzleMode::MortonToLinear, params, gl_buffer[i], i);
1003 } else { 835 } else {
1004 const auto texture_src_data{Memory::GetPointer(params.addr)}; 836 const auto texture_src_data{Memory::GetPointer(params.addr)};
1005 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; 837 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
@@ -1038,7 +870,7 @@ void CachedSurface::FlushGLBuffer() {
1038 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 870 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
1039 params.block_width, static_cast<u32>(params.target)); 871 params.block_width, static_cast<u32>(params.target));
1040 872
1041 SwizzleFunc(gl_to_morton_fns, params, gl_buffer[0], 0); 873 SwizzleFunc(MortonSwizzleMode::LinearToMorton, params, gl_buffer[0], 0);
1042 } else { 874 } else {
1043 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer[0].data(), GetSizeInBytes()); 875 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer[0].data(), GetSizeInBytes());
1044 } 876 }
@@ -1278,6 +1110,31 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) {
1278 return surface; 1110 return surface;
1279} 1111}
1280 1112
1113void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
1114 const Surface& dst_surface) {
1115 const auto& init_params{src_surface->GetSurfaceParams()};
1116 const auto& dst_params{dst_surface->GetSurfaceParams()};
1117 VAddr address = init_params.addr;
1118 const std::size_t layer_size = dst_params.LayerMemorySize();
1119 for (u32 layer = 0; layer < dst_params.depth; layer++) {
1120 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
1121 const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap);
1122 const Surface& copy = TryGet(sub_address);
1123 if (!copy)
1124 continue;
1125 const auto& src_params{copy->GetSurfaceParams()};
1126 const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))};
1127 const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))};
1128
1129 glCopyImageSubData(copy->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0,
1130 0, 0, dst_surface->Texture().handle,
1131 SurfaceTargetToGL(dst_params.target), mipmap, 0, 0, layer, width,
1132 height, 1);
1133 }
1134 address += layer_size;
1135 }
1136}
1137
1281void RasterizerCacheOpenGL::FermiCopySurface( 1138void RasterizerCacheOpenGL::FermiCopySurface(
1282 const Tegra::Engines::Fermi2D::Regs::Surface& src_config, 1139 const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
1283 const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) { 1140 const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) {
@@ -1343,11 +1200,13 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1343 CopySurface(old_surface, new_surface, copy_pbo.handle); 1200 CopySurface(old_surface, new_surface, copy_pbo.handle);
1344 } 1201 }
1345 break; 1202 break;
1346 case SurfaceTarget::TextureCubemap:
1347 case SurfaceTarget::Texture3D: 1203 case SurfaceTarget::Texture3D:
1204 AccurateCopySurface(old_surface, new_surface);
1205 break;
1206 case SurfaceTarget::TextureCubemap:
1348 case SurfaceTarget::Texture2DArray: 1207 case SurfaceTarget::Texture2DArray:
1349 case SurfaceTarget::TextureCubeArray: 1208 case SurfaceTarget::TextureCubeArray:
1350 AccurateCopySurface(old_surface, new_surface); 1209 FastLayeredCopySurface(old_surface, new_surface);
1351 break; 1210 break;
1352 default: 1211 default:
1353 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1212 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 494f6b903..9ac79c5a4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -350,6 +350,7 @@ private:
350 350
351 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data 351 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
352 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface); 352 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
353 void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface);
353 354
354 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have 355 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
355 /// previously been used. This is to prevent surfaces from being constantly created and 356 /// previously been used. This is to prevent surfaces from being constantly created and
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index a85a7c0c5..038b25c75 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -84,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
84 } 84 }
85 85
86 entries = program_result.second; 86 entries = program_result.second;
87 shader_length = entries.shader_length;
87 88
88 if (program_type != Maxwell::ShaderProgram::Geometry) { 89 if (program_type != Maxwell::ShaderProgram::Geometry) {
89 OGLShader shader; 90 OGLShader shader;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index ffbf21831..08f470de3 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -30,7 +30,7 @@ public:
30 } 30 }
31 31
32 std::size_t GetSizeInBytes() const override { 32 std::size_t GetSizeInBytes() const override {
33 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); 33 return shader_length;
34 } 34 }
35 35
36 // We do not have to flush this cache as things in it are never modified by us. 36 // We do not have to flush this cache as things in it are never modified by us.
@@ -82,6 +82,7 @@ private:
82 u32 max_vertices, const std::string& debug_name); 82 u32 max_vertices, const std::string& debug_name);
83 83
84 VAddr addr; 84 VAddr addr;
85 std::size_t shader_length;
85 Maxwell::ShaderProgram program_type; 86 Maxwell::ShaderProgram program_type;
86 GLShader::ShaderSetup setup; 87 GLShader::ShaderSetup setup;
87 GLShader::ShaderEntries entries; 88 GLShader::ShaderEntries entries;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 5fde22ad4..7c0935a4e 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -34,6 +34,17 @@ constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
34constexpr u32 MAX_GEOMETRY_BUFFERS = 6; 34constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
35constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested 35constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
36 36
37static const char* INTERNAL_FLAG_NAMES[] = {"zero_flag", "sign_flag", "carry_flag",
38 "overflow_flag"};
39
40enum class InternalFlag : u64 {
41 ZeroFlag = 0,
42 SignFlag = 1,
43 CarryFlag = 2,
44 OverflowFlag = 3,
45 Amount
46};
47
37class DecompileFail : public std::runtime_error { 48class DecompileFail : public std::runtime_error {
38public: 49public:
39 using std::runtime_error::runtime_error; 50 using std::runtime_error::runtime_error;
@@ -49,8 +60,7 @@ static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
49 case Tegra::Shader::OutputTopology::TriangleStrip: 60 case Tegra::Shader::OutputTopology::TriangleStrip:
50 return "triangle_strip"; 61 return "triangle_strip";
51 default: 62 default:
52 LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology)); 63 UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology));
53 UNREACHABLE();
54 return "points"; 64 return "points";
55 } 65 }
56} 66}
@@ -85,7 +95,8 @@ struct Subroutine {
85class ControlFlowAnalyzer { 95class ControlFlowAnalyzer {
86public: 96public:
87 ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix) 97 ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix)
88 : program_code(program_code) { 98 : program_code(program_code), shader_coverage_begin(main_offset),
99 shader_coverage_end(main_offset + 1) {
89 100
90 // Recursively finds all subroutines. 101 // Recursively finds all subroutines.
91 const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix); 102 const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix);
@@ -97,10 +108,16 @@ public:
97 return std::move(subroutines); 108 return std::move(subroutines);
98 } 109 }
99 110
111 std::size_t GetShaderLength() const {
112 return shader_coverage_end * sizeof(u64);
113 }
114
100private: 115private:
101 const ProgramCode& program_code; 116 const ProgramCode& program_code;
102 std::set<Subroutine> subroutines; 117 std::set<Subroutine> subroutines;
103 std::map<std::pair<u32, u32>, ExitMethod> exit_method_map; 118 std::map<std::pair<u32, u32>, ExitMethod> exit_method_map;
119 u32 shader_coverage_begin;
120 u32 shader_coverage_end;
104 121
105 /// Adds and analyzes a new subroutine if it is not added yet. 122 /// Adds and analyzes a new subroutine if it is not added yet.
106 const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) { 123 const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) {
@@ -142,6 +159,9 @@ private:
142 return exit_method; 159 return exit_method;
143 160
144 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { 161 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
162 shader_coverage_begin = std::min(shader_coverage_begin, offset);
163 shader_coverage_end = std::max(shader_coverage_end, offset + 1);
164
145 const Instruction instr = {program_code[offset]}; 165 const Instruction instr = {program_code[offset]};
146 if (const auto opcode = OpCode::Decode(instr)) { 166 if (const auto opcode = OpCode::Decode(instr)) {
147 switch (opcode->get().GetId()) { 167 switch (opcode->get().GetId()) {
@@ -167,8 +187,8 @@ private:
167 case OpCode::Id::SSY: 187 case OpCode::Id::SSY:
168 case OpCode::Id::PBK: { 188 case OpCode::Id::PBK: {
169 // The SSY and PBK use a similar encoding as the BRA instruction. 189 // The SSY and PBK use a similar encoding as the BRA instruction.
170 ASSERT_MSG(instr.bra.constant_buffer == 0, 190 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
171 "Constant buffer branching is not supported"); 191 "Constant buffer branching is not supported");
172 const u32 target = offset + instr.bra.GetBranchTarget(); 192 const u32 target = offset + instr.bra.GetBranchTarget();
173 labels.insert(target); 193 labels.insert(target);
174 // Continue scanning for an exit method. 194 // Continue scanning for an exit method.
@@ -258,14 +278,6 @@ private:
258 const std::string& suffix; 278 const std::string& suffix;
259}; 279};
260 280
261enum class InternalFlag : u64 {
262 ZeroFlag = 0,
263 CarryFlag = 1,
264 OverflowFlag = 2,
265 NaNFlag = 3,
266 Amount
267};
268
269/** 281/**
270 * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state 282 * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state
271 * of all registers (e.g. whether they are currently being used as Floats or Integers), and 283 * of all registers (e.g. whether they are currently being used as Floats or Integers), and
@@ -299,8 +311,7 @@ public:
299 // Default - do nothing 311 // Default - do nothing
300 return value; 312 return value;
301 default: 313 default:
302 LOG_CRITICAL(HW_GPU, "Unimplemented conversion size {}", static_cast<u32>(size)); 314 UNIMPLEMENTED_MSG("Unimplemented conversion size: {}", static_cast<u32>(size));
303 UNREACHABLE();
304 } 315 }
305 } 316 }
306 317
@@ -363,7 +374,7 @@ public:
363 u64 value_num_components, bool is_saturated = false, 374 u64 value_num_components, bool is_saturated = false,
364 u64 dest_elem = 0, Register::Size size = Register::Size::Word, 375 u64 dest_elem = 0, Register::Size size = Register::Size::Word,
365 bool sets_cc = false) { 376 bool sets_cc = false) {
366 ASSERT_MSG(!is_saturated, "Unimplemented"); 377 UNIMPLEMENTED_IF(is_saturated);
367 378
368 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; 379 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
369 380
@@ -373,7 +384,7 @@ public:
373 if (sets_cc) { 384 if (sets_cc) {
374 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; 385 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
375 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); 386 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
376 LOG_WARNING(HW_GPU, "Control Codes Imcomplete."); 387 LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete.");
377 } 388 }
378 } 389 }
379 390
@@ -392,7 +403,7 @@ public:
392 Tegra::Shader::HalfMerge merge, u64 dest_num_components, 403 Tegra::Shader::HalfMerge merge, u64 dest_num_components,
393 u64 value_num_components, bool is_saturated = false, 404 u64 value_num_components, bool is_saturated = false,
394 u64 dest_elem = 0) { 405 u64 dest_elem = 0) {
395 ASSERT_MSG(!is_saturated, "Unimplemented"); 406 UNIMPLEMENTED_IF(is_saturated);
396 407
397 const std::string result = [&]() { 408 const std::string result = [&]() {
398 switch (merge) { 409 switch (merge) {
@@ -456,24 +467,25 @@ public:
456 shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");"); 467 shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");");
457 } 468 }
458 469
459 std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { 470 std::string GetConditionCode(const Tegra::Shader::ConditionCode cc) const {
460 switch (cc) { 471 switch (cc) {
461 case Tegra::Shader::ControlCode::NEU: 472 case Tegra::Shader::ConditionCode::NEU:
462 return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')'; 473 return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')';
463 default: 474 default:
464 LOG_CRITICAL(HW_GPU, "Unimplemented Control Code {}", static_cast<u32>(cc)); 475 UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc));
465 UNREACHABLE();
466 return "false"; 476 return "false";
467 } 477 }
468 } 478 }
469 479
470 std::string GetInternalFlag(const InternalFlag ii) const { 480 std::string GetInternalFlag(const InternalFlag flag) const {
471 const u32 code = static_cast<u32>(ii); 481 const auto index = static_cast<u32>(flag);
472 return "internalFlag_" + std::to_string(code) + suffix; 482 ASSERT(index < static_cast<u32>(InternalFlag::Amount));
483
484 return std::string(INTERNAL_FLAG_NAMES[index]) + '_' + suffix;
473 } 485 }
474 486
475 void SetInternalFlag(const InternalFlag ii, const std::string& value) const { 487 void SetInternalFlag(const InternalFlag flag, const std::string& value) const {
476 shader.AddLine(GetInternalFlag(ii) + " = " + value + ';'); 488 shader.AddLine(GetInternalFlag(flag) + " = " + value + ';');
477 } 489 }
478 490
479 /** 491 /**
@@ -488,27 +500,42 @@ public:
488 const Register& buf_reg) { 500 const Register& buf_reg) {
489 const std::string dest = GetOutputAttribute(attribute); 501 const std::string dest = GetOutputAttribute(attribute);
490 const std::string src = GetRegisterAsFloat(val_reg); 502 const std::string src = GetRegisterAsFloat(val_reg);
503 if (dest.empty())
504 return;
491 505
492 if (!dest.empty()) { 506 // Can happen with unknown/unimplemented output attributes, in which case we ignore the
493 // Can happen with unknown/unimplemented output attributes, in which case we ignore the 507 // instruction for now.
494 // instruction for now. 508 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
495 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { 509 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
496 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry 510 // shader. These instructions use a dirty register as buffer index, to avoid some
497 // shader. These instructions use a dirty register as buffer index, to avoid some 511 // drivers from complaining about out of boundary writes, guard them.
498 // drivers from complaining about out of boundary writes, guard them. 512 const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " +
499 const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " + 513 std::to_string(MAX_GEOMETRY_BUFFERS) + ')'};
500 std::to_string(MAX_GEOMETRY_BUFFERS) + ')'}; 514 shader.AddLine("amem[" + buf_index + "][" +
501 shader.AddLine("amem[" + buf_index + "][" + 515 std::to_string(static_cast<u32>(attribute)) + ']' + GetSwizzle(elem) +
502 std::to_string(static_cast<u32>(attribute)) + ']' + 516 " = " + src + ';');
503 GetSwizzle(elem) + " = " + src + ';'); 517 return;
504 } else { 518 }
505 if (attribute == Attribute::Index::PointSize) { 519
506 fixed_pipeline_output_attributes_used.insert(attribute); 520 switch (attribute) {
507 shader.AddLine(dest + " = " + src + ';'); 521 case Attribute::Index::ClipDistances0123:
508 } else { 522 case Attribute::Index::ClipDistances4567: {
509 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); 523 const u64 index = attribute == Attribute::Index::ClipDistances4567 ? 4 : 0 + elem;
510 } 524 UNIMPLEMENTED_IF_MSG(
511 } 525 ((header.vtg.clip_distances >> index) & 1) == 0,
526 "Shader is setting gl_ClipDistance{} without enabling it in the header", index);
527
528 fixed_pipeline_output_attributes_used.insert(attribute);
529 shader.AddLine(dest + '[' + std::to_string(index) + "] = " + src + ';');
530 break;
531 }
532 case Attribute::Index::PointSize:
533 fixed_pipeline_output_attributes_used.insert(attribute);
534 shader.AddLine(dest + " = " + src + ';');
535 break;
536 default:
537 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
538 break;
512 } 539 }
513 } 540 }
514 541
@@ -624,8 +651,8 @@ private:
624 651
625 /// Generates declarations for internal flags. 652 /// Generates declarations for internal flags.
626 void GenerateInternalFlags() { 653 void GenerateInternalFlags() {
627 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { 654 for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
628 const InternalFlag code = static_cast<InternalFlag>(ii); 655 const InternalFlag code = static_cast<InternalFlag>(flag);
629 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); 656 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
630 } 657 }
631 declarations.AddNewLine(); 658 declarations.AddNewLine();
@@ -728,12 +755,19 @@ private:
728 void GenerateVertex() { 755 void GenerateVertex() {
729 if (stage != Maxwell3D::Regs::ShaderStage::Vertex) 756 if (stage != Maxwell3D::Regs::ShaderStage::Vertex)
730 return; 757 return;
758 bool clip_distances_declared = false;
759
731 declarations.AddLine("out gl_PerVertex {"); 760 declarations.AddLine("out gl_PerVertex {");
732 ++declarations.scope; 761 ++declarations.scope;
733 declarations.AddLine("vec4 gl_Position;"); 762 declarations.AddLine("vec4 gl_Position;");
734 for (auto& o : fixed_pipeline_output_attributes_used) { 763 for (auto& o : fixed_pipeline_output_attributes_used) {
735 if (o == Attribute::Index::PointSize) 764 if (o == Attribute::Index::PointSize)
736 declarations.AddLine("float gl_PointSize;"); 765 declarations.AddLine("float gl_PointSize;");
766 if (!clip_distances_declared && (o == Attribute::Index::ClipDistances0123 ||
767 o == Attribute::Index::ClipDistances4567)) {
768 declarations.AddLine("float gl_ClipDistance[];");
769 clip_distances_declared = true;
770 }
737 } 771 }
738 --declarations.scope; 772 --declarations.scope;
739 declarations.AddLine("};"); 773 declarations.AddLine("};");
@@ -761,8 +795,7 @@ private:
761 u64 dest_num_components, u64 value_num_components, u64 dest_elem, 795 u64 dest_num_components, u64 value_num_components, u64 dest_elem,
762 bool precise) { 796 bool precise) {
763 if (reg == Register::ZeroIndex) { 797 if (reg == Register::ZeroIndex) {
764 LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex"); 798 // Setting RZ is a nop in hardware.
765 UNREACHABLE();
766 return; 799 return;
767 } 800 }
768 801
@@ -834,7 +867,8 @@ private:
834 // vertex shader, and what's the value of the fourth element when inside a Tess Eval 867 // vertex shader, and what's the value of the fourth element when inside a Tess Eval
835 // shader. 868 // shader.
836 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex); 869 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex);
837 return "vec4(0, 0, uintBitsToFloat(instance_id.x), uintBitsToFloat(gl_VertexID))"; 870 // Config pack's first value is instance_id.
871 return "vec4(0, 0, uintBitsToFloat(config_pack[0]), uintBitsToFloat(gl_VertexID))";
838 case Attribute::Index::FrontFacing: 872 case Attribute::Index::FrontFacing:
839 // TODO(Subv): Find out what the values are for the other elements. 873 // TODO(Subv): Find out what the values are for the other elements.
840 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); 874 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
@@ -847,16 +881,13 @@ private:
847 if (declr_input_attribute.count(attribute) == 0) { 881 if (declr_input_attribute.count(attribute) == 0) {
848 declr_input_attribute[attribute] = input_mode; 882 declr_input_attribute[attribute] = input_mode;
849 } else { 883 } else {
850 if (declr_input_attribute[attribute] != input_mode) { 884 UNIMPLEMENTED_IF_MSG(declr_input_attribute[attribute] != input_mode,
851 LOG_CRITICAL(HW_GPU, "Same Input multiple input modes"); 885 "Multiple input modes for the same attribute");
852 UNREACHABLE();
853 }
854 } 886 }
855 return GeometryPass("input_attribute_" + std::to_string(index)); 887 return GeometryPass("input_attribute_" + std::to_string(index));
856 } 888 }
857 889
858 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); 890 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
859 UNREACHABLE();
860 } 891 }
861 892
862 return "vec4(0, 0, 0, 0)"; 893 return "vec4(0, 0, 0, 0)";
@@ -882,24 +913,20 @@ private:
882 break; 913 break;
883 } 914 }
884 default: { 915 default: {
885 LOG_CRITICAL(HW_GPU, "Unhandled Ipa InterpMode: {}", static_cast<u32>(interp_mode)); 916 UNIMPLEMENTED_MSG("Unhandled IPA interp mode: {}", static_cast<u32>(interp_mode));
886 UNREACHABLE();
887 } 917 }
888 } 918 }
889 switch (sample_mode) { 919 switch (sample_mode) {
890 case Tegra::Shader::IpaSampleMode::Centroid: { 920 case Tegra::Shader::IpaSampleMode::Centroid:
891 // Note not implemented, it can be implemented with the "centroid " keyword in glsl; 921 // It can be implemented with the "centroid " keyword in glsl
892 LOG_CRITICAL(HW_GPU, "Ipa Sampler Mode: centroid, not implemented"); 922 UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode centroid");
893 UNREACHABLE();
894 break; 923 break;
895 } 924 case Tegra::Shader::IpaSampleMode::Default:
896 case Tegra::Shader::IpaSampleMode::Default: {
897 // Default, n/a 925 // Default, n/a
898 break; 926 break;
899 }
900 default: { 927 default: {
901 LOG_CRITICAL(HW_GPU, "Unhandled Ipa SampleMode: {}", static_cast<u32>(sample_mode)); 928 UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode: {}", static_cast<u32>(sample_mode));
902 UNREACHABLE(); 929 break;
903 } 930 }
904 } 931 }
905 return out; 932 return out;
@@ -912,6 +939,10 @@ private:
912 return "gl_PointSize"; 939 return "gl_PointSize";
913 case Attribute::Index::Position: 940 case Attribute::Index::Position:
914 return "position"; 941 return "position";
942 case Attribute::Index::ClipDistances0123:
943 case Attribute::Index::ClipDistances4567: {
944 return "gl_ClipDistance";
945 }
915 default: 946 default:
916 const u32 index{static_cast<u32>(attribute) - 947 const u32 index{static_cast<u32>(attribute) -
917 static_cast<u32>(Attribute::Index::Attribute_0)}; 948 static_cast<u32>(Attribute::Index::Attribute_0)};
@@ -920,8 +951,7 @@ private:
920 return "output_attribute_" + std::to_string(index); 951 return "output_attribute_" + std::to_string(index);
921 } 952 }
922 953
923 LOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); 954 UNIMPLEMENTED_MSG("Unhandled output attribute={}", index);
924 UNREACHABLE();
925 return {}; 955 return {};
926 } 956 }
927 } 957 }
@@ -951,9 +981,10 @@ private:
951class GLSLGenerator { 981class GLSLGenerator {
952public: 982public:
953 GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code, 983 GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code,
954 u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) 984 u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix,
985 std::size_t shader_length)
955 : subroutines(subroutines), program_code(program_code), main_offset(main_offset), 986 : subroutines(subroutines), program_code(program_code), main_offset(main_offset),
956 stage(stage), suffix(suffix) { 987 stage(stage), suffix(suffix), shader_length(shader_length) {
957 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); 988 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
958 local_memory_size = header.GetLocalMemorySize(); 989 local_memory_size = header.GetLocalMemorySize();
959 regs.SetLocalMemory(local_memory_size); 990 regs.SetLocalMemory(local_memory_size);
@@ -966,7 +997,7 @@ public:
966 997
967 /// Returns entries in the shader that are useful for external functions 998 /// Returns entries in the shader that are useful for external functions
968 ShaderEntries GetEntries() const { 999 ShaderEntries GetEntries() const {
969 return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()}; 1000 return {regs.GetConstBuffersDeclarations(), regs.GetSamplers(), shader_length};
970 } 1001 }
971 1002
972private: 1003private:
@@ -1071,19 +1102,26 @@ private:
1071 const std::string& op_a, const std::string& op_b) const { 1102 const std::string& op_a, const std::string& op_b) const {
1072 using Tegra::Shader::PredCondition; 1103 using Tegra::Shader::PredCondition;
1073 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { 1104 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = {
1074 {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, 1105 {PredCondition::LessThan, "<"},
1075 {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, 1106 {PredCondition::Equal, "=="},
1076 {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, 1107 {PredCondition::LessEqual, "<="},
1077 {PredCondition::LessThanWithNan, "<"}, {PredCondition::NotEqualWithNan, "!="}, 1108 {PredCondition::GreaterThan, ">"},
1078 {PredCondition::GreaterThanWithNan, ">"}, {PredCondition::GreaterEqualWithNan, ">="}}; 1109 {PredCondition::NotEqual, "!="},
1110 {PredCondition::GreaterEqual, ">="},
1111 {PredCondition::LessThanWithNan, "<"},
1112 {PredCondition::NotEqualWithNan, "!="},
1113 {PredCondition::LessEqualWithNan, "<="},
1114 {PredCondition::GreaterThanWithNan, ">"},
1115 {PredCondition::GreaterEqualWithNan, ">="}};
1079 1116
1080 const auto& comparison{PredicateComparisonStrings.find(condition)}; 1117 const auto& comparison{PredicateComparisonStrings.find(condition)};
1081 ASSERT_MSG(comparison != PredicateComparisonStrings.end(), 1118 UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonStrings.end(),
1082 "Unknown predicate comparison operation"); 1119 "Unknown predicate comparison operation");
1083 1120
1084 std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; 1121 std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'};
1085 if (condition == PredCondition::LessThanWithNan || 1122 if (condition == PredCondition::LessThanWithNan ||
1086 condition == PredCondition::NotEqualWithNan || 1123 condition == PredCondition::NotEqualWithNan ||
1124 condition == PredCondition::LessEqualWithNan ||
1087 condition == PredCondition::GreaterThanWithNan || 1125 condition == PredCondition::GreaterThanWithNan ||
1088 condition == PredCondition::GreaterEqualWithNan) { 1126 condition == PredCondition::GreaterEqualWithNan) {
1089 predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; 1127 predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')';
@@ -1107,7 +1145,7 @@ private:
1107 }; 1145 };
1108 1146
1109 auto op = PredicateOperationStrings.find(operation); 1147 auto op = PredicateOperationStrings.find(operation);
1110 ASSERT_MSG(op != PredicateOperationStrings.end(), "Unknown predicate operation"); 1148 UNIMPLEMENTED_IF_MSG(op == PredicateOperationStrings.end(), "Unknown predicate operation");
1111 return op->second; 1149 return op->second;
1112 } 1150 }
1113 1151
@@ -1205,8 +1243,7 @@ private:
1205 break; 1243 break;
1206 } 1244 }
1207 default: 1245 default:
1208 LOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op)); 1246 UNIMPLEMENTED_MSG("Unimplemented logic operation={}", static_cast<u32>(logic_op));
1209 UNREACHABLE();
1210 } 1247 }
1211 1248
1212 if (dest != Tegra::Shader::Register::ZeroIndex) { 1249 if (dest != Tegra::Shader::Register::ZeroIndex) {
@@ -1224,9 +1261,8 @@ private:
1224 SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0"); 1261 SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0");
1225 break; 1262 break;
1226 default: 1263 default:
1227 LOG_CRITICAL(HW_GPU, "Unimplemented predicate result mode: {}", 1264 UNIMPLEMENTED_MSG("Unimplemented predicate result mode: {}",
1228 static_cast<u32>(predicate_mode)); 1265 static_cast<u32>(predicate_mode));
1229 UNREACHABLE();
1230 } 1266 }
1231 } 1267 }
1232 1268
@@ -1264,6 +1300,7 @@ private:
1264 shader.AddLine('{'); 1300 shader.AddLine('{');
1265 ++shader.scope; 1301 ++shader.scope;
1266 shader.AddLine(coord); 1302 shader.AddLine(coord);
1303 shader.AddLine("vec4 texture_tmp = " + texture + ';');
1267 1304
1268 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle 1305 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle
1269 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 1306 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
@@ -1276,37 +1313,32 @@ private:
1276 1313
1277 if (written_components < 2) { 1314 if (written_components < 2) {
1278 // Write the first two swizzle components to gpr0 and gpr0+1 1315 // Write the first two swizzle components to gpr0 and gpr0+1
1279 regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false, 1316 regs.SetRegisterToFloat(instr.gpr0, component, "texture_tmp", 1, 4, false,
1280 written_components % 2); 1317 written_components % 2);
1281 } else { 1318 } else {
1282 ASSERT(instr.texs.HasTwoDestinations()); 1319 ASSERT(instr.texs.HasTwoDestinations());
1283 // Write the rest of the swizzle components to gpr28 and gpr28+1 1320 // Write the rest of the swizzle components to gpr28 and gpr28+1
1284 regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false, 1321 regs.SetRegisterToFloat(instr.gpr28, component, "texture_tmp", 1, 4, false,
1285 written_components % 2); 1322 written_components % 2);
1286 } 1323 }
1287 1324
1288 ++written_components; 1325 ++written_components;
1289 } 1326 }
1290
1291 --shader.scope; 1327 --shader.scope;
1292 shader.AddLine('}'); 1328 shader.AddLine('}');
1293 } 1329 }
1294 1330
1295 static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { 1331 static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) {
1296 switch (texture_type) { 1332 switch (texture_type) {
1297 case Tegra::Shader::TextureType::Texture1D: { 1333 case Tegra::Shader::TextureType::Texture1D:
1298 return 1; 1334 return 1;
1299 } 1335 case Tegra::Shader::TextureType::Texture2D:
1300 case Tegra::Shader::TextureType::Texture2D: {
1301 return 2; 1336 return 2;
1302 }
1303 case Tegra::Shader::TextureType::Texture3D: 1337 case Tegra::Shader::TextureType::Texture3D:
1304 case Tegra::Shader::TextureType::TextureCube: { 1338 case Tegra::Shader::TextureType::TextureCube:
1305 return 3; 1339 return 3;
1306 }
1307 default: 1340 default:
1308 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", static_cast<u32>(texture_type)); 1341 UNIMPLEMENTED_MSG("Unhandled texture type: {}", static_cast<u32>(texture_type));
1309 UNREACHABLE();
1310 return 0; 1342 return 0;
1311 } 1343 }
1312 } 1344 }
@@ -1342,7 +1374,7 @@ private:
1342 void EmitFragmentOutputsWrite() { 1374 void EmitFragmentOutputsWrite() {
1343 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); 1375 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
1344 1376
1345 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); 1377 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Samplemask write is unimplemented");
1346 1378
1347 shader.AddLine("if (alpha_test[0] != 0) {"); 1379 shader.AddLine("if (alpha_test[0] != 0) {");
1348 ++shader.scope; 1380 ++shader.scope;
@@ -1408,7 +1440,7 @@ private:
1408 case Tegra::Shader::VideoType::Size32: 1440 case Tegra::Shader::VideoType::Size32:
1409 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when 1441 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
1410 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better 1442 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
1411 // explanation is found: assert. 1443 // explanation is found: abort.
1412 UNIMPLEMENTED(); 1444 UNIMPLEMENTED();
1413 return zero; 1445 return zero;
1414 case Tegra::Shader::VideoType::Invalid: 1446 case Tegra::Shader::VideoType::Invalid:
@@ -1464,8 +1496,7 @@ private:
1464 1496
1465 // Decoding failure 1497 // Decoding failure
1466 if (!opcode) { 1498 if (!opcode) {
1467 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); 1499 UNIMPLEMENTED_MSG("Unhandled instruction: {0:x}", instr.value);
1468 UNREACHABLE();
1469 return offset + 1; 1500 return offset + 1;
1470 } 1501 }
1471 1502
@@ -1473,8 +1504,8 @@ private:
1473 fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value)); 1504 fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value));
1474 1505
1475 using Tegra::Shader::Pred; 1506 using Tegra::Shader::Pred;
1476 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, 1507 UNIMPLEMENTED_IF_MSG(instr.pred.full_pred == Pred::NeverExecute,
1477 "NeverExecute predicate not implemented"); 1508 "NeverExecute predicate not implemented");
1478 1509
1479 // Some instructions (like SSY) don't have a predicate field, they are always 1510 // Some instructions (like SSY) don't have a predicate field, they are always
1480 // unconditionally executed. 1511 // unconditionally executed.
@@ -1517,37 +1548,36 @@ private:
1517 case OpCode::Id::FMUL_R: 1548 case OpCode::Id::FMUL_R:
1518 case OpCode::Id::FMUL_IMM: { 1549 case OpCode::Id::FMUL_IMM: {
1519 // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. 1550 // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit.
1520 ASSERT_MSG(instr.fmul.tab5cb8_2 == 0, "FMUL tab5cb8_2({}) is not implemented", 1551 UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0,
1521 instr.fmul.tab5cb8_2.Value()); 1552 "FMUL tab5cb8_2({}) is not implemented",
1522 ASSERT_MSG(instr.fmul.tab5c68_1 == 0, "FMUL tab5cb8_1({}) is not implemented", 1553 instr.fmul.tab5cb8_2.Value());
1523 instr.fmul.tab5c68_1.Value()); 1554 UNIMPLEMENTED_IF_MSG(instr.fmul.tab5c68_1 != 0,
1524 ASSERT_MSG(instr.fmul.tab5c68_0 == 1, "FMUL tab5cb8_0({}) is not implemented", 1555 "FMUL tab5cb8_1({}) is not implemented",
1525 instr.fmul.tab5c68_0 1556 instr.fmul.tab5c68_1.Value());
1526 .Value()); // SMO typical sends 1 here which seems to be the default 1557 UNIMPLEMENTED_IF_MSG(
1527 ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented"); 1558 instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented",
1559 instr.fmul.tab5c68_0
1560 .Value()); // SMO typical sends 1 here which seems to be the default
1561 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1562 "Condition codes generation in FMUL is not implemented");
1528 1563
1529 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); 1564 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
1530 1565
1531 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, 1566 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
1532 instr.alu.saturate_d, 0, true); 1567 instr.alu.saturate_d, 0, true);
1533 if (instr.generates_cc) {
1534 LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code");
1535 UNREACHABLE();
1536 }
1537 break; 1568 break;
1538 } 1569 }
1539 case OpCode::Id::FADD_C: 1570 case OpCode::Id::FADD_C:
1540 case OpCode::Id::FADD_R: 1571 case OpCode::Id::FADD_R:
1541 case OpCode::Id::FADD_IMM: { 1572 case OpCode::Id::FADD_IMM: {
1573 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1574 "Condition codes generation in FADD is not implemented");
1575
1542 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); 1576 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
1543 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); 1577 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
1544 1578
1545 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, 1579 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
1546 instr.alu.saturate_d, 0, true); 1580 instr.alu.saturate_d, 0, true);
1547 if (instr.generates_cc) {
1548 LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code");
1549 UNREACHABLE();
1550 }
1551 break; 1581 break;
1552 } 1582 }
1553 case OpCode::Id::MUFU: { 1583 case OpCode::Id::MUFU: {
@@ -1582,15 +1612,17 @@ private:
1582 instr.alu.saturate_d, 0, true); 1612 instr.alu.saturate_d, 0, true);
1583 break; 1613 break;
1584 default: 1614 default:
1585 LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", 1615 UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}",
1586 static_cast<unsigned>(instr.sub_op.Value())); 1616 static_cast<unsigned>(instr.sub_op.Value()));
1587 UNREACHABLE();
1588 } 1617 }
1589 break; 1618 break;
1590 } 1619 }
1591 case OpCode::Id::FMNMX_C: 1620 case OpCode::Id::FMNMX_C:
1592 case OpCode::Id::FMNMX_R: 1621 case OpCode::Id::FMNMX_R:
1593 case OpCode::Id::FMNMX_IMM: { 1622 case OpCode::Id::FMNMX_IMM: {
1623 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1624 "Condition codes generation in FMNMX is not implemented");
1625
1594 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); 1626 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
1595 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); 1627 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
1596 1628
@@ -1601,10 +1633,6 @@ private:
1601 '(' + condition + ") ? min(" + parameters + ") : max(" + 1633 '(' + condition + ") ? min(" + parameters + ") : max(" +
1602 parameters + ')', 1634 parameters + ')',
1603 1, 1, false, 0, true); 1635 1, 1, false, 0, true);
1604 if (instr.generates_cc) {
1605 LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code");
1606 UNREACHABLE();
1607 }
1608 break; 1636 break;
1609 } 1637 }
1610 case OpCode::Id::RRO_C: 1638 case OpCode::Id::RRO_C:
@@ -1617,9 +1645,7 @@ private:
1617 break; 1645 break;
1618 } 1646 }
1619 default: { 1647 default: {
1620 LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", 1648 UNIMPLEMENTED_MSG("Unhandled arithmetic instruction: {}", opcode->get().GetName());
1621 opcode->get().GetName());
1622 UNREACHABLE();
1623 } 1649 }
1624 } 1650 }
1625 break; 1651 break;
@@ -1631,17 +1657,19 @@ private:
1631 break; 1657 break;
1632 } 1658 }
1633 case OpCode::Id::FMUL32_IMM: { 1659 case OpCode::Id::FMUL32_IMM: {
1660 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1661 "Condition codes generation in FMUL32 is not implemented");
1662
1634 regs.SetRegisterToFloat(instr.gpr0, 0, 1663 regs.SetRegisterToFloat(instr.gpr0, 0,
1635 regs.GetRegisterAsFloat(instr.gpr8) + " * " + 1664 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1636 GetImmediate32(instr), 1665 GetImmediate32(instr),
1637 1, 1, instr.fmul32.saturate, 0, true); 1666 1, 1, instr.fmul32.saturate, 0, true);
1638 if (instr.op_32.generates_cc) {
1639 LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code");
1640 UNREACHABLE();
1641 }
1642 break; 1667 break;
1643 } 1668 }
1644 case OpCode::Id::FADD32I: { 1669 case OpCode::Id::FADD32I: {
1670 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1671 "Condition codes generation in FADD32I is not implemented");
1672
1645 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1673 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1646 std::string op_b = GetImmediate32(instr); 1674 std::string op_b = GetImmediate32(instr);
1647 1675
@@ -1662,23 +1690,22 @@ private:
1662 } 1690 }
1663 1691
1664 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); 1692 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true);
1665 if (instr.op_32.generates_cc) {
1666 LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code");
1667 UNREACHABLE();
1668 }
1669 break; 1693 break;
1670 } 1694 }
1671 } 1695 }
1672 break; 1696 break;
1673 } 1697 }
1674 case OpCode::Type::Bfe: { 1698 case OpCode::Type::Bfe: {
1675 ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented"); 1699 UNIMPLEMENTED_IF(instr.bfe.negate_b);
1676 1700
1677 std::string op_a = instr.bfe.negate_a ? "-" : ""; 1701 std::string op_a = instr.bfe.negate_a ? "-" : "";
1678 op_a += regs.GetRegisterAsInteger(instr.gpr8); 1702 op_a += regs.GetRegisterAsInteger(instr.gpr8);
1679 1703
1680 switch (opcode->get().GetId()) { 1704 switch (opcode->get().GetId()) {
1681 case OpCode::Id::BFE_IMM: { 1705 case OpCode::Id::BFE_IMM: {
1706 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1707 "Condition codes generation in BFE is not implemented");
1708
1682 std::string inner_shift = 1709 std::string inner_shift =
1683 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; 1710 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
1684 std::string outer_shift = 1711 std::string outer_shift =
@@ -1686,20 +1713,35 @@ private:
1686 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; 1713 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
1687 1714
1688 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); 1715 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1);
1689 if (instr.generates_cc) {
1690 LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code");
1691 UNREACHABLE();
1692 }
1693 break; 1716 break;
1694 } 1717 }
1695 default: { 1718 default: {
1696 LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->get().GetName()); 1719 UNIMPLEMENTED_MSG("Unhandled BFE instruction: {}", opcode->get().GetName());
1697 UNREACHABLE();
1698 } 1720 }
1699 } 1721 }
1700 1722
1701 break; 1723 break;
1702 } 1724 }
1725 case OpCode::Type::Bfi: {
1726 UNIMPLEMENTED_IF(instr.generates_cc);
1727
1728 const auto [base, packed_shift] = [&]() -> std::tuple<std::string, std::string> {
1729 switch (opcode->get().GetId()) {
1730 case OpCode::Id::BFI_IMM_R:
1731 return {regs.GetRegisterAsInteger(instr.gpr39, 0, false),
1732 std::to_string(instr.alu.GetSignedImm20_20())};
1733 default:
1734 UNREACHABLE();
1735 }
1736 }();
1737 const std::string offset = '(' + packed_shift + " & 0xff)";
1738 const std::string bits = "((" + packed_shift + " >> 8) & 0xff)";
1739 const std::string insert = regs.GetRegisterAsInteger(instr.gpr8, 0, false);
1740 regs.SetRegisterToInteger(
1741 instr.gpr0, false, 0,
1742 "bitfieldInsert(" + base + ", " + insert + ", " + offset + ", " + bits + ')', 1, 1);
1743 break;
1744 }
1703 case OpCode::Type::Shift: { 1745 case OpCode::Type::Shift: {
1704 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); 1746 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
1705 std::string op_b; 1747 std::string op_b;
@@ -1719,6 +1761,9 @@ private:
1719 case OpCode::Id::SHR_C: 1761 case OpCode::Id::SHR_C:
1720 case OpCode::Id::SHR_R: 1762 case OpCode::Id::SHR_R:
1721 case OpCode::Id::SHR_IMM: { 1763 case OpCode::Id::SHR_IMM: {
1764 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1765 "Condition codes generation in SHR is not implemented");
1766
1722 if (!instr.shift.is_signed) { 1767 if (!instr.shift.is_signed) {
1723 // Logical shift right 1768 // Logical shift right
1724 op_a = "uint(" + op_a + ')'; 1769 op_a = "uint(" + op_a + ')';
@@ -1727,24 +1772,17 @@ private:
1727 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift 1772 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift
1728 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', 1773 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
1729 1, 1); 1774 1, 1);
1730 if (instr.generates_cc) {
1731 LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code");
1732 UNREACHABLE();
1733 }
1734 break; 1775 break;
1735 } 1776 }
1736 case OpCode::Id::SHL_C: 1777 case OpCode::Id::SHL_C:
1737 case OpCode::Id::SHL_R: 1778 case OpCode::Id::SHL_R:
1738 case OpCode::Id::SHL_IMM: 1779 case OpCode::Id::SHL_IMM:
1780 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1781 "Condition codes generation in SHL is not implemented");
1739 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); 1782 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
1740 if (instr.generates_cc) {
1741 LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code");
1742 UNREACHABLE();
1743 }
1744 break; 1783 break;
1745 default: { 1784 default: {
1746 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName()); 1785 UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName());
1747 UNREACHABLE();
1748 } 1786 }
1749 } 1787 }
1750 break; 1788 break;
@@ -1755,17 +1793,19 @@ private:
1755 1793
1756 switch (opcode->get().GetId()) { 1794 switch (opcode->get().GetId()) {
1757 case OpCode::Id::IADD32I: 1795 case OpCode::Id::IADD32I:
1796 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1797 "Condition codes generation in IADD32I is not implemented");
1798
1758 if (instr.iadd32i.negate_a) 1799 if (instr.iadd32i.negate_a)
1759 op_a = "-(" + op_a + ')'; 1800 op_a = "-(" + op_a + ')';
1760 1801
1761 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1802 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1762 instr.iadd32i.saturate != 0); 1803 instr.iadd32i.saturate != 0);
1763 if (instr.op_32.generates_cc) {
1764 LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code");
1765 UNREACHABLE();
1766 }
1767 break; 1804 break;
1768 case OpCode::Id::LOP32I: { 1805 case OpCode::Id::LOP32I: {
1806 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1807 "Condition codes generation in LOP32I is not implemented");
1808
1769 if (instr.alu.lop32i.invert_a) 1809 if (instr.alu.lop32i.invert_a)
1770 op_a = "~(" + op_a + ')'; 1810 op_a = "~(" + op_a + ')';
1771 1811
@@ -1775,16 +1815,11 @@ private:
1775 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, 1815 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
1776 Tegra::Shader::PredicateResultMode::None, 1816 Tegra::Shader::PredicateResultMode::None,
1777 Tegra::Shader::Pred::UnusedIndex); 1817 Tegra::Shader::Pred::UnusedIndex);
1778 if (instr.op_32.generates_cc) {
1779 LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code");
1780 UNREACHABLE();
1781 }
1782 break; 1818 break;
1783 } 1819 }
1784 default: { 1820 default: {
1785 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}", 1821 UNIMPLEMENTED_MSG("Unhandled ArithmeticIntegerImmediate instruction: {}",
1786 opcode->get().GetName()); 1822 opcode->get().GetName());
1787 UNREACHABLE();
1788 } 1823 }
1789 } 1824 }
1790 break; 1825 break;
@@ -1807,6 +1842,9 @@ private:
1807 case OpCode::Id::IADD_C: 1842 case OpCode::Id::IADD_C:
1808 case OpCode::Id::IADD_R: 1843 case OpCode::Id::IADD_R:
1809 case OpCode::Id::IADD_IMM: { 1844 case OpCode::Id::IADD_IMM: {
1845 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1846 "Condition codes generation in IADD is not implemented");
1847
1810 if (instr.alu_integer.negate_a) 1848 if (instr.alu_integer.negate_a)
1811 op_a = "-(" + op_a + ')'; 1849 op_a = "-(" + op_a + ')';
1812 1850
@@ -1815,15 +1853,14 @@ private:
1815 1853
1816 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1854 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1817 instr.alu.saturate_d); 1855 instr.alu.saturate_d);
1818 if (instr.generates_cc) {
1819 LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code");
1820 UNREACHABLE();
1821 }
1822 break; 1856 break;
1823 } 1857 }
1824 case OpCode::Id::IADD3_C: 1858 case OpCode::Id::IADD3_C:
1825 case OpCode::Id::IADD3_R: 1859 case OpCode::Id::IADD3_R:
1826 case OpCode::Id::IADD3_IMM: { 1860 case OpCode::Id::IADD3_IMM: {
1861 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1862 "Condition codes generation in IADD3 is not implemented");
1863
1827 std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); 1864 std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
1828 1865
1829 auto apply_height = [](auto height, auto& oprand) { 1866 auto apply_height = [](auto height, auto& oprand) {
@@ -1837,9 +1874,8 @@ private:
1837 oprand = "((" + oprand + ") >> 16)"; 1874 oprand = "((" + oprand + ") >> 16)";
1838 break; 1875 break;
1839 default: 1876 default:
1840 LOG_CRITICAL(HW_GPU, "Unhandled IADD3 height: {}", 1877 UNIMPLEMENTED_MSG("Unhandled IADD3 height: {}",
1841 static_cast<u32>(height.Value())); 1878 static_cast<u32>(height.Value()));
1842 UNREACHABLE();
1843 } 1879 }
1844 }; 1880 };
1845 1881
@@ -1880,16 +1916,14 @@ private:
1880 } 1916 }
1881 1917
1882 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); 1918 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1);
1883
1884 if (instr.generates_cc) {
1885 LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code");
1886 UNREACHABLE();
1887 }
1888 break; 1919 break;
1889 } 1920 }
1890 case OpCode::Id::ISCADD_C: 1921 case OpCode::Id::ISCADD_C:
1891 case OpCode::Id::ISCADD_R: 1922 case OpCode::Id::ISCADD_R:
1892 case OpCode::Id::ISCADD_IMM: { 1923 case OpCode::Id::ISCADD_IMM: {
1924 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1925 "Condition codes generation in ISCADD is not implemented");
1926
1893 if (instr.alu_integer.negate_a) 1927 if (instr.alu_integer.negate_a)
1894 op_a = "-(" + op_a + ')'; 1928 op_a = "-(" + op_a + ')';
1895 1929
@@ -1900,10 +1934,6 @@ private:
1900 1934
1901 regs.SetRegisterToInteger(instr.gpr0, true, 0, 1935 regs.SetRegisterToInteger(instr.gpr0, true, 0,
1902 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); 1936 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
1903 if (instr.generates_cc) {
1904 LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code");
1905 UNREACHABLE();
1906 }
1907 break; 1937 break;
1908 } 1938 }
1909 case OpCode::Id::POPC_C: 1939 case OpCode::Id::POPC_C:
@@ -1927,6 +1957,9 @@ private:
1927 case OpCode::Id::LOP_C: 1957 case OpCode::Id::LOP_C:
1928 case OpCode::Id::LOP_R: 1958 case OpCode::Id::LOP_R:
1929 case OpCode::Id::LOP_IMM: { 1959 case OpCode::Id::LOP_IMM: {
1960 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1961 "Condition codes generation in LOP is not implemented");
1962
1930 if (instr.alu.lop.invert_a) 1963 if (instr.alu.lop.invert_a)
1931 op_a = "~(" + op_a + ')'; 1964 op_a = "~(" + op_a + ')';
1932 1965
@@ -1935,15 +1968,14 @@ private:
1935 1968
1936 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, 1969 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
1937 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); 1970 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
1938 if (instr.generates_cc) {
1939 LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code");
1940 UNREACHABLE();
1941 }
1942 break; 1971 break;
1943 } 1972 }
1944 case OpCode::Id::LOP3_C: 1973 case OpCode::Id::LOP3_C:
1945 case OpCode::Id::LOP3_R: 1974 case OpCode::Id::LOP3_R:
1946 case OpCode::Id::LOP3_IMM: { 1975 case OpCode::Id::LOP3_IMM: {
1976 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1977 "Condition codes generation in LOP3 is not implemented");
1978
1947 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); 1979 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
1948 std::string lut; 1980 std::string lut;
1949 1981
@@ -1954,17 +1986,15 @@ private:
1954 } 1986 }
1955 1987
1956 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); 1988 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut);
1957 if (instr.generates_cc) {
1958 LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code");
1959 UNREACHABLE();
1960 }
1961 break; 1989 break;
1962 } 1990 }
1963 case OpCode::Id::IMNMX_C: 1991 case OpCode::Id::IMNMX_C:
1964 case OpCode::Id::IMNMX_R: 1992 case OpCode::Id::IMNMX_R:
1965 case OpCode::Id::IMNMX_IMM: { 1993 case OpCode::Id::IMNMX_IMM: {
1966 ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, 1994 UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None);
1967 "Unimplemented"); 1995 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1996 "Condition codes generation in IMNMX is not implemented");
1997
1968 const std::string condition = 1998 const std::string condition =
1969 GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); 1999 GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
1970 const std::string parameters = op_a + ',' + op_b; 2000 const std::string parameters = op_a + ',' + op_b;
@@ -1972,10 +2002,6 @@ private:
1972 '(' + condition + ") ? min(" + parameters + ") : max(" + 2002 '(' + condition + ") ? min(" + parameters + ") : max(" +
1973 parameters + ')', 2003 parameters + ')',
1974 1, 1); 2004 1, 1);
1975 if (instr.generates_cc) {
1976 LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code");
1977 UNREACHABLE();
1978 }
1979 break; 2005 break;
1980 } 2006 }
1981 case OpCode::Id::LEA_R2: 2007 case OpCode::Id::LEA_R2:
@@ -2030,24 +2056,19 @@ private:
2030 op_b = regs.GetRegisterAsInteger(instr.gpr8); 2056 op_b = regs.GetRegisterAsInteger(instr.gpr8);
2031 op_a = std::to_string(instr.lea.imm.entry_a); 2057 op_a = std::to_string(instr.lea.imm.entry_a);
2032 op_c = std::to_string(instr.lea.imm.entry_b); 2058 op_c = std::to_string(instr.lea.imm.entry_b);
2033 LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", 2059 UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName());
2034 opcode->get().GetName());
2035 UNREACHABLE();
2036 } 2060 }
2037 } 2061 }
2038 if (instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex)) { 2062 UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
2039 LOG_ERROR(HW_GPU, "Unhandled LEA Predicate"); 2063 "Unhandled LEA Predicate");
2040 UNREACHABLE();
2041 }
2042 const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))"; 2064 const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))";
2043 regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1); 2065 regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1);
2044 2066
2045 break; 2067 break;
2046 } 2068 }
2047 default: { 2069 default: {
2048 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", 2070 UNIMPLEMENTED_MSG("Unhandled ArithmeticInteger instruction: {}",
2049 opcode->get().GetName()); 2071 opcode->get().GetName());
2050 UNREACHABLE();
2051 } 2072 }
2052 } 2073 }
2053 2074
@@ -2056,7 +2077,7 @@ private:
2056 case OpCode::Type::ArithmeticHalf: { 2077 case OpCode::Type::ArithmeticHalf: {
2057 if (opcode->get().GetId() == OpCode::Id::HADD2_C || 2078 if (opcode->get().GetId() == OpCode::Id::HADD2_C ||
2058 opcode->get().GetId() == OpCode::Id::HADD2_R) { 2079 opcode->get().GetId() == OpCode::Id::HADD2_R) {
2059 ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented"); 2080 UNIMPLEMENTED_IF(instr.alu_half.ftz != 0);
2060 } 2081 }
2061 const bool negate_a = 2082 const bool negate_a =
2062 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; 2083 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
@@ -2094,9 +2115,8 @@ private:
2094 case OpCode::Id::HMUL2_R: 2115 case OpCode::Id::HMUL2_R:
2095 return '(' + op_a + " * " + op_b + ')'; 2116 return '(' + op_a + " * " + op_b + ')';
2096 default: 2117 default:
2097 LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", 2118 UNIMPLEMENTED_MSG("Unhandled half float instruction: {}",
2098 opcode->get().GetName()); 2119 opcode->get().GetName());
2099 UNREACHABLE();
2100 return std::string("0"); 2120 return std::string("0");
2101 } 2121 }
2102 }(); 2122 }();
@@ -2107,10 +2127,10 @@ private:
2107 } 2127 }
2108 case OpCode::Type::ArithmeticHalfImmediate: { 2128 case OpCode::Type::ArithmeticHalfImmediate: {
2109 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { 2129 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) {
2110 ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented"); 2130 UNIMPLEMENTED_IF(instr.alu_half_imm.ftz != 0);
2111 } else { 2131 } else {
2112 ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None, 2132 UNIMPLEMENTED_IF(instr.alu_half_imm.precision !=
2113 "Unimplemented"); 2133 Tegra::Shader::HalfPrecision::None);
2114 } 2134 }
2115 2135
2116 const std::string op_a = GetHalfFloat( 2136 const std::string op_a = GetHalfFloat(
@@ -2140,11 +2160,14 @@ private:
2140 std::string op_b = instr.ffma.negate_b ? "-" : ""; 2160 std::string op_b = instr.ffma.negate_b ? "-" : "";
2141 std::string op_c = instr.ffma.negate_c ? "-" : ""; 2161 std::string op_c = instr.ffma.negate_c ? "-" : "";
2142 2162
2143 ASSERT_MSG(instr.ffma.cc == 0, "FFMA cc not implemented"); 2163 UNIMPLEMENTED_IF_MSG(instr.ffma.cc != 0, "FFMA cc not implemented");
2144 ASSERT_MSG(instr.ffma.tab5980_0 == 1, "FFMA tab5980_0({}) not implemented", 2164 UNIMPLEMENTED_IF_MSG(
2145 instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO 2165 instr.ffma.tab5980_0 != 1, "FFMA tab5980_0({}) not implemented",
2146 ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented", 2166 instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO
2147 instr.ffma.tab5980_1.Value()); 2167 UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented",
2168 instr.ffma.tab5980_1.Value());
2169 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2170 "Condition codes generation in FFMA is not implemented");
2148 2171
2149 switch (opcode->get().GetId()) { 2172 switch (opcode->get().GetId()) {
2150 case OpCode::Id::FFMA_CR: { 2173 case OpCode::Id::FFMA_CR: {
@@ -2170,27 +2193,19 @@ private:
2170 break; 2193 break;
2171 } 2194 }
2172 default: { 2195 default: {
2173 LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->get().GetName()); 2196 UNIMPLEMENTED_MSG("Unhandled FFMA instruction: {}", opcode->get().GetName());
2174 UNREACHABLE();
2175 } 2197 }
2176 } 2198 }
2177 2199
2178 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', 2200 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
2179 1, 1, instr.alu.saturate_d, 0, true); 2201 1, 1, instr.alu.saturate_d, 0, true);
2180 if (instr.generates_cc) {
2181 LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code");
2182 UNREACHABLE();
2183 }
2184
2185 break; 2202 break;
2186 } 2203 }
2187 case OpCode::Type::Hfma2: { 2204 case OpCode::Type::Hfma2: {
2188 if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) { 2205 if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) {
2189 ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None, 2206 UNIMPLEMENTED_IF(instr.hfma2.rr.precision != Tegra::Shader::HalfPrecision::None);
2190 "Unimplemented");
2191 } else { 2207 } else {
2192 ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None, 2208 UNIMPLEMENTED_IF(instr.hfma2.precision != Tegra::Shader::HalfPrecision::None);
2193 "Unimplemented");
2194 } 2209 }
2195 const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR 2210 const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR
2196 ? instr.hfma2.rr.saturate != 0 2211 ? instr.hfma2.rr.saturate != 0
@@ -2240,7 +2255,7 @@ private:
2240 case OpCode::Type::Conversion: { 2255 case OpCode::Type::Conversion: {
2241 switch (opcode->get().GetId()) { 2256 switch (opcode->get().GetId()) {
2242 case OpCode::Id::I2I_R: { 2257 case OpCode::Id::I2I_R: {
2243 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 2258 UNIMPLEMENTED_IF(instr.conversion.selector);
2244 2259
2245 std::string op_a = regs.GetRegisterAsInteger( 2260 std::string op_a = regs.GetRegisterAsInteger(
2246 instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size); 2261 instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size);
@@ -2260,8 +2275,10 @@ private:
2260 } 2275 }
2261 case OpCode::Id::I2F_R: 2276 case OpCode::Id::I2F_R:
2262 case OpCode::Id::I2F_C: { 2277 case OpCode::Id::I2F_C: {
2263 ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); 2278 UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
2264 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 2279 UNIMPLEMENTED_IF(instr.conversion.selector);
2280 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2281 "Condition codes generation in I2F is not implemented");
2265 2282
2266 std::string op_a{}; 2283 std::string op_a{};
2267 2284
@@ -2286,16 +2303,13 @@ private:
2286 } 2303 }
2287 2304
2288 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 2305 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2289
2290 if (instr.generates_cc) {
2291 LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code");
2292 UNREACHABLE();
2293 }
2294 break; 2306 break;
2295 } 2307 }
2296 case OpCode::Id::F2F_R: { 2308 case OpCode::Id::F2F_R: {
2297 ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); 2309 UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
2298 ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); 2310 UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
2311 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2312 "Condition codes generation in F2F is not implemented");
2299 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); 2313 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
2300 2314
2301 if (instr.conversion.abs_a) { 2315 if (instr.conversion.abs_a) {
@@ -2322,23 +2336,19 @@ private:
2322 op_a = "trunc(" + op_a + ')'; 2336 op_a = "trunc(" + op_a + ')';
2323 break; 2337 break;
2324 default: 2338 default:
2325 LOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}", 2339 UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}",
2326 static_cast<u32>(instr.conversion.f2f.rounding.Value())); 2340 static_cast<u32>(instr.conversion.f2f.rounding.Value()));
2327 UNREACHABLE();
2328 break; 2341 break;
2329 } 2342 }
2330 2343
2331 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); 2344 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
2332
2333 if (instr.generates_cc) {
2334 LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code");
2335 UNREACHABLE();
2336 }
2337 break; 2345 break;
2338 } 2346 }
2339 case OpCode::Id::F2I_R: 2347 case OpCode::Id::F2I_R:
2340 case OpCode::Id::F2I_C: { 2348 case OpCode::Id::F2I_C: {
2341 ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); 2349 UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
2350 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2351 "Condition codes generation in F2I is not implemented");
2342 std::string op_a{}; 2352 std::string op_a{};
2343 2353
2344 if (instr.is_b_gpr) { 2354 if (instr.is_b_gpr) {
@@ -2369,9 +2379,8 @@ private:
2369 op_a = "trunc(" + op_a + ')'; 2379 op_a = "trunc(" + op_a + ')';
2370 break; 2380 break;
2371 default: 2381 default:
2372 LOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", 2382 UNIMPLEMENTED_MSG("Unimplemented F2I rounding mode {}",
2373 static_cast<u32>(instr.conversion.f2i.rounding.Value())); 2383 static_cast<u32>(instr.conversion.f2i.rounding.Value()));
2374 UNREACHABLE();
2375 break; 2384 break;
2376 } 2385 }
2377 2386
@@ -2383,16 +2392,10 @@ private:
2383 2392
2384 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 2393 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
2385 1, false, 0, instr.conversion.dest_size); 2394 1, false, 0, instr.conversion.dest_size);
2386 if (instr.generates_cc) {
2387 LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code");
2388 UNREACHABLE();
2389 }
2390 break; 2395 break;
2391 } 2396 }
2392 default: { 2397 default: {
2393 LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", 2398 UNIMPLEMENTED_MSG("Unhandled conversion instruction: {}", opcode->get().GetName());
2394 opcode->get().GetName());
2395 UNREACHABLE();
2396 } 2399 }
2397 } 2400 }
2398 break; 2401 break;
@@ -2401,10 +2404,10 @@ private:
2401 switch (opcode->get().GetId()) { 2404 switch (opcode->get().GetId()) {
2402 case OpCode::Id::LD_A: { 2405 case OpCode::Id::LD_A: {
2403 // Note: Shouldn't this be interp mode flat? As in no interpolation made. 2406 // Note: Shouldn't this be interp mode flat? As in no interpolation made.
2404 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2407 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
2405 "Indirect attribute loads are not supported"); 2408 "Indirect attribute loads are not supported");
2406 ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, 2409 UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
2407 "Unaligned attribute loads are not supported"); 2410 "Unaligned attribute loads are not supported");
2408 2411
2409 Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, 2412 Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective,
2410 Tegra::Shader::IpaSampleMode::Default}; 2413 Tegra::Shader::IpaSampleMode::Default};
@@ -2431,7 +2434,7 @@ private:
2431 break; 2434 break;
2432 } 2435 }
2433 case OpCode::Id::LD_C: { 2436 case OpCode::Id::LD_C: {
2434 ASSERT_MSG(instr.ld_c.unknown == 0, "Unimplemented"); 2437 UNIMPLEMENTED_IF(instr.ld_c.unknown != 0);
2435 2438
2436 // Add an extra scope and declare the index register inside to prevent 2439 // Add an extra scope and declare the index register inside to prevent
2437 // overwriting it in case it is used as an output of the LD instruction. 2440 // overwriting it in case it is used as an output of the LD instruction.
@@ -2459,9 +2462,8 @@ private:
2459 break; 2462 break;
2460 } 2463 }
2461 default: 2464 default:
2462 LOG_CRITICAL(HW_GPU, "Unhandled type: {}", 2465 UNIMPLEMENTED_MSG("Unhandled type: {}",
2463 static_cast<unsigned>(instr.ld_c.type.Value())); 2466 static_cast<unsigned>(instr.ld_c.type.Value()));
2464 UNREACHABLE();
2465 } 2467 }
2466 2468
2467 --shader.scope; 2469 --shader.scope;
@@ -2469,6 +2471,9 @@ private:
2469 break; 2471 break;
2470 } 2472 }
2471 case OpCode::Id::LD_L: { 2473 case OpCode::Id::LD_L: {
2474 UNIMPLEMENTED_IF_MSG(instr.ld_l.unknown == 1, "LD_L Unhandled mode: {}",
2475 static_cast<unsigned>(instr.ld_l.unknown.Value()));
2476
2472 // Add an extra scope and declare the index register inside to prevent 2477 // Add an extra scope and declare the index register inside to prevent
2473 // overwriting it in case it is used as an output of the LD instruction. 2478 // overwriting it in case it is used as an output of the LD instruction.
2474 shader.AddLine('{'); 2479 shader.AddLine('{');
@@ -2481,20 +2486,13 @@ private:
2481 2486
2482 const std::string op_a = regs.GetLocalMemoryAsFloat("index"); 2487 const std::string op_a = regs.GetLocalMemoryAsFloat("index");
2483 2488
2484 if (instr.ld_l.unknown != 1) {
2485 LOG_CRITICAL(HW_GPU, "LD_L Unhandled mode: {}",
2486 static_cast<unsigned>(instr.ld_l.unknown.Value()));
2487 UNREACHABLE();
2488 }
2489
2490 switch (instr.ldst_sl.type.Value()) { 2489 switch (instr.ldst_sl.type.Value()) {
2491 case Tegra::Shader::StoreType::Bytes32: 2490 case Tegra::Shader::StoreType::Bytes32:
2492 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 2491 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2493 break; 2492 break;
2494 default: 2493 default:
2495 LOG_CRITICAL(HW_GPU, "LD_L Unhandled type: {}", 2494 UNIMPLEMENTED_MSG("LD_L Unhandled type: {}",
2496 static_cast<unsigned>(instr.ldst_sl.type.Value())); 2495 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2497 UNREACHABLE();
2498 } 2496 }
2499 2497
2500 --shader.scope; 2498 --shader.scope;
@@ -2502,10 +2500,10 @@ private:
2502 break; 2500 break;
2503 } 2501 }
2504 case OpCode::Id::ST_A: { 2502 case OpCode::Id::ST_A: {
2505 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2503 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
2506 "Indirect attribute loads are not supported"); 2504 "Indirect attribute loads are not supported");
2507 ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, 2505 UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
2508 "Unaligned attribute loads are not supported"); 2506 "Unaligned attribute loads are not supported");
2509 2507
2510 u64 next_element = instr.attribute.fmt20.element; 2508 u64 next_element = instr.attribute.fmt20.element;
2511 u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); 2509 u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
@@ -2530,6 +2528,9 @@ private:
2530 break; 2528 break;
2531 } 2529 }
2532 case OpCode::Id::ST_L: { 2530 case OpCode::Id::ST_L: {
2531 UNIMPLEMENTED_IF_MSG(instr.st_l.unknown == 0, "ST_L Unhandled mode: {}",
2532 static_cast<unsigned>(instr.st_l.unknown.Value()));
2533
2533 // Add an extra scope and declare the index register inside to prevent 2534 // Add an extra scope and declare the index register inside to prevent
2534 // overwriting it in case it is used as an output of the LD instruction. 2535 // overwriting it in case it is used as an output of the LD instruction.
2535 shader.AddLine('{'); 2536 shader.AddLine('{');
@@ -2540,20 +2541,13 @@ private:
2540 2541
2541 shader.AddLine("uint index = (" + op + " / 4);"); 2542 shader.AddLine("uint index = (" + op + " / 4);");
2542 2543
2543 if (instr.st_l.unknown != 0) {
2544 LOG_CRITICAL(HW_GPU, "ST_L Unhandled mode: {}",
2545 static_cast<unsigned>(instr.st_l.unknown.Value()));
2546 UNREACHABLE();
2547 }
2548
2549 switch (instr.ldst_sl.type.Value()) { 2544 switch (instr.ldst_sl.type.Value()) {
2550 case Tegra::Shader::StoreType::Bytes32: 2545 case Tegra::Shader::StoreType::Bytes32:
2551 regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0)); 2546 regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0));
2552 break; 2547 break;
2553 default: 2548 default:
2554 LOG_CRITICAL(HW_GPU, "ST_L Unhandled type: {}", 2549 UNIMPLEMENTED_MSG("ST_L Unhandled type: {}",
2555 static_cast<unsigned>(instr.ldst_sl.type.Value())); 2550 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2556 UNREACHABLE();
2557 } 2551 }
2558 2552
2559 --shader.scope; 2553 --shader.scope;
@@ -2565,78 +2559,99 @@ private:
2565 std::string coord; 2559 std::string coord;
2566 const bool is_array = instr.tex.array != 0; 2560 const bool is_array = instr.tex.array != 0;
2567 2561
2568 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2562 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2569 "NODEP is not implemented"); 2563 "NODEP is not implemented");
2570 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2564 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2571 "AOFFI is not implemented"); 2565 "AOFFI is not implemented");
2572 2566
2573 const bool depth_compare = 2567 const bool depth_compare =
2574 instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2568 instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2575 u32 num_coordinates = TextureCoordinates(texture_type); 2569 u32 num_coordinates = TextureCoordinates(texture_type);
2576 if (depth_compare) 2570 u32 start_index = 0;
2577 num_coordinates += 1; 2571 std::string array_elem;
2572 if (is_array) {
2573 array_elem = regs.GetRegisterAsInteger(instr.gpr8);
2574 start_index = 1;
2575 }
2576 const auto process_mode = instr.tex.GetTextureProcessMode();
2577 u32 start_index_b = 0;
2578 std::string lod_value;
2579 if (process_mode != Tegra::Shader::TextureProcessMode::LZ &&
2580 process_mode != Tegra::Shader::TextureProcessMode::None) {
2581 start_index_b = 1;
2582 lod_value = regs.GetRegisterAsFloat(instr.gpr20);
2583 }
2584
2585 std::string depth_value;
2586 if (depth_compare) {
2587 depth_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + start_index_b);
2588 }
2589
2590 bool depth_compare_extra = false;
2578 2591
2579 switch (num_coordinates) { 2592 switch (num_coordinates) {
2580 case 1: { 2593 case 1: {
2594 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index);
2581 if (is_array) { 2595 if (is_array) {
2582 const std::string index = regs.GetRegisterAsInteger(instr.gpr8); 2596 if (depth_compare) {
2583 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2597 coord = "vec3 coords = vec3(" + x + ", " + depth_value + ", " +
2584 coord = "vec2 coords = vec2(" + x + ", " + index + ");"; 2598 array_elem + ");";
2599 } else {
2600 coord = "vec2 coords = vec2(" + x + ", " + array_elem + ");";
2601 }
2585 } else { 2602 } else {
2586 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2603 if (depth_compare) {
2587 coord = "float coords = " + x + ';'; 2604 coord = "vec2 coords = vec2(" + x + ", " + depth_value + ");";
2605 } else {
2606 coord = "float coords = " + x + ';';
2607 }
2588 } 2608 }
2589 break; 2609 break;
2590 } 2610 }
2591 case 2: { 2611 case 2: {
2612 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index);
2613 const std::string y =
2614 regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1);
2592 if (is_array) { 2615 if (is_array) {
2593 const std::string index = regs.GetRegisterAsInteger(instr.gpr8); 2616 if (depth_compare) {
2594 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2617 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + depth_value +
2595 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); 2618 ", " + array_elem + ");";
2596 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; 2619 } else {
2620 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + array_elem + ");";
2621 }
2597 } else { 2622 } else {
2598 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2623 if (depth_compare) {
2599 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2624 coord =
2600 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2625 "vec3 coords = vec3(" + x + ", " + y + ", " + depth_value + ");";
2626 } else {
2627 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2628 }
2601 } 2629 }
2602 break; 2630 break;
2603 } 2631 }
2604 case 3: { 2632 case 3: {
2605 if (depth_compare) { 2633 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index);
2606 if (is_array) { 2634 const std::string y =
2607 const std::string index = regs.GetRegisterAsInteger(instr.gpr8); 2635 regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1);
2608 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2636 const std::string z =
2609 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2637 regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 2);
2610 const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2638 if (is_array) {
2611 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + 2639 depth_compare_extra = depth_compare;
2612 ");"; 2640 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " +
2613 } else { 2641 array_elem + ");";
2614 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2615 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2616 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2617 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2618 }
2619 } else { 2642 } else {
2620 if (is_array) { 2643 if (depth_compare) {
2621 const std::string index = regs.GetRegisterAsInteger(instr.gpr8); 2644 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " +
2622 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2645 depth_value + ");";
2623 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2624 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3);
2625 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2626 ");";
2627 } else { 2646 } else {
2628 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2629 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2630 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2631 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2647 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2632 } 2648 }
2633 } 2649 }
2634 break; 2650 break;
2635 } 2651 }
2636 default: 2652 default:
2637 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", 2653 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2638 static_cast<u32>(num_coordinates)); 2654 static_cast<u32>(num_coordinates));
2639 UNREACHABLE();
2640 2655
2641 // Fallback to interpreting as a 2D texture for now 2656 // Fallback to interpreting as a 2D texture for now
2642 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2657 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
@@ -2644,126 +2659,172 @@ private:
2644 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2659 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2645 texture_type = Tegra::Shader::TextureType::Texture2D; 2660 texture_type = Tegra::Shader::TextureType::Texture2D;
2646 } 2661 }
2647 // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias
2648 // or lod.
2649 std::string op_c;
2650 2662
2651 const std::string sampler = 2663 const std::string sampler =
2652 GetSampler(instr.sampler, texture_type, is_array, depth_compare); 2664 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
2653 // Add an extra scope and declare the texture coords inside to prevent 2665 // Add an extra scope and declare the texture coords inside to prevent
2654 // overwriting them in case they are used as outputs of the texs instruction. 2666 // overwriting them in case they are used as outputs of the texs instruction.
2655 2667
2656 shader.AddLine("{"); 2668 shader.AddLine('{');
2657 ++shader.scope; 2669 ++shader.scope;
2658 shader.AddLine(coord); 2670 shader.AddLine(coord);
2659 std::string texture; 2671 std::string texture;
2660 2672
2661 switch (instr.tex.GetTextureProcessMode()) { 2673 switch (instr.tex.GetTextureProcessMode()) {
2662 case Tegra::Shader::TextureProcessMode::None: { 2674 case Tegra::Shader::TextureProcessMode::None: {
2663 texture = "texture(" + sampler + ", coords)"; 2675 if (!depth_compare_extra) {
2676 texture = "texture(" + sampler + ", coords)";
2677 } else {
2678 texture = "texture(" + sampler + ", coords, " + depth_value + ')';
2679 }
2664 break; 2680 break;
2665 } 2681 }
2666 case Tegra::Shader::TextureProcessMode::LZ: { 2682 case Tegra::Shader::TextureProcessMode::LZ: {
2667 texture = "textureLod(" + sampler + ", coords, 0.0)"; 2683 if (!depth_compare_extra) {
2684 texture = "textureLod(" + sampler + ", coords, 0.0)";
2685 } else {
2686 texture = "texture(" + sampler + ", coords, " + depth_value + ')';
2687 }
2668 break; 2688 break;
2669 } 2689 }
2670 case Tegra::Shader::TextureProcessMode::LB: 2690 case Tegra::Shader::TextureProcessMode::LB:
2671 case Tegra::Shader::TextureProcessMode::LBA: { 2691 case Tegra::Shader::TextureProcessMode::LBA: {
2672 if (depth_compare) { 2692 // TODO: Figure if A suffix changes the equation at all.
2673 if (is_array) 2693 if (!depth_compare_extra) {
2674 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); 2694 texture = "texture(" + sampler + ", coords, " + lod_value + ')';
2675 else
2676 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2677 } else { 2695 } else {
2678 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2696 texture = "texture(" + sampler + ", coords, " + depth_value + ')';
2697 LOG_WARNING(HW_GPU,
2698 "OpenGL Limitation: can't set bias value along depth compare");
2679 } 2699 }
2680 // TODO: Figure if A suffix changes the equation at all.
2681 texture = "texture(" + sampler + ", coords, " + op_c + ')';
2682 break; 2700 break;
2683 } 2701 }
2684 case Tegra::Shader::TextureProcessMode::LL: 2702 case Tegra::Shader::TextureProcessMode::LL:
2685 case Tegra::Shader::TextureProcessMode::LLA: { 2703 case Tegra::Shader::TextureProcessMode::LLA: {
2686 if (num_coordinates <= 2) { 2704 // TODO: Figure if A suffix changes the equation at all.
2687 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2705 if (!depth_compare_extra) {
2706 texture = "textureLod(" + sampler + ", coords, " + lod_value + ')';
2688 } else { 2707 } else {
2689 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2708 texture = "texture(" + sampler + ", coords, " + depth_value + ')';
2709 LOG_WARNING(HW_GPU,
2710 "OpenGL Limitation: can't set lod value along depth compare");
2690 } 2711 }
2691 // TODO: Figure if A suffix changes the equation at all.
2692 texture = "textureLod(" + sampler + ", coords, " + op_c + ')';
2693 break; 2712 break;
2694 } 2713 }
2695 default: { 2714 default: {
2696 texture = "texture(" + sampler + ", coords)"; 2715 if (!depth_compare_extra) {
2697 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2716 texture = "texture(" + sampler + ", coords)";
2698 static_cast<u32>(instr.tex.GetTextureProcessMode())); 2717 } else {
2699 UNREACHABLE(); 2718 texture = "texture(" + sampler + ", coords, " + depth_value + ')';
2719 }
2720 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2721 static_cast<u32>(instr.tex.GetTextureProcessMode()));
2700 } 2722 }
2701 } 2723 }
2702 if (!depth_compare) { 2724 if (!depth_compare) {
2725 shader.AddLine("vec4 texture_tmp = " + texture + ';');
2703 std::size_t dest_elem{}; 2726 std::size_t dest_elem{};
2704 for (std::size_t elem = 0; elem < 4; ++elem) { 2727 for (std::size_t elem = 0; elem < 4; ++elem) {
2705 if (!instr.tex.IsComponentEnabled(elem)) { 2728 if (!instr.tex.IsComponentEnabled(elem)) {
2706 // Skip disabled components 2729 // Skip disabled components
2707 continue; 2730 continue;
2708 } 2731 }
2709 regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); 2732 regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false,
2733 dest_elem);
2710 ++dest_elem; 2734 ++dest_elem;
2711 } 2735 }
2712 } else { 2736 } else {
2713 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); 2737 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
2714 } 2738 }
2715 --shader.scope; 2739 --shader.scope;
2716 shader.AddLine("}"); 2740 shader.AddLine('}');
2717 break; 2741 break;
2718 } 2742 }
2719 case OpCode::Id::TEXS: { 2743 case OpCode::Id::TEXS: {
2720 std::string coord;
2721 Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; 2744 Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
2722 bool is_array{instr.texs.IsArrayTexture()}; 2745 bool is_array{instr.texs.IsArrayTexture()};
2723 2746
2724 ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2747 UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2725 "NODEP is not implemented"); 2748 "NODEP is not implemented");
2726 2749
2727 const bool depth_compare = 2750 const bool depth_compare =
2728 instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2751 instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2729 u32 num_coordinates = TextureCoordinates(texture_type); 2752 u32 num_coordinates = TextureCoordinates(texture_type);
2730 if (depth_compare) 2753 const auto process_mode = instr.texs.GetTextureProcessMode();
2731 num_coordinates += 1; 2754 std::string lod_value;
2755 std::string coord;
2756 u32 lod_offset = 0;
2757 if (process_mode == Tegra::Shader::TextureProcessMode::LL) {
2758 if (num_coordinates > 2) {
2759 lod_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2760 lod_offset = 2;
2761 } else {
2762 lod_value = regs.GetRegisterAsFloat(instr.gpr20);
2763 lod_offset = 1;
2764 }
2765 }
2732 2766
2733 switch (num_coordinates) { 2767 switch (num_coordinates) {
2768 case 1: {
2769 coord = "float coords = " + regs.GetRegisterAsFloat(instr.gpr8) + ';';
2770 break;
2771 }
2734 case 2: { 2772 case 2: {
2735 if (is_array) { 2773 if (is_array) {
2736 const std::string index = regs.GetRegisterAsInteger(instr.gpr8); 2774 if (depth_compare) {
2737 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2775 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2738 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2776 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2739 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; 2777 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2778 const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2779 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2780 ");";
2781 } else {
2782 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2783 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2784 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2785 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
2786 }
2740 } else { 2787 } else {
2741 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2788 if (lod_offset != 0) {
2742 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2789 if (depth_compare) {
2743 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2790 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2791 const std::string y =
2792 regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2793 const std::string z =
2794 regs.GetRegisterAsFloat(instr.gpr20.Value() + lod_offset);
2795 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2796 } else {
2797 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2798 const std::string y =
2799 regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2800 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2801 }
2802 } else {
2803 if (depth_compare) {
2804 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2805 const std::string y =
2806 regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2807 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2808 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2809 } else {
2810 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2811 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2812 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2813 }
2814 }
2744 } 2815 }
2745 break; 2816 break;
2746 } 2817 }
2747 case 3: { 2818 case 3: {
2748 if (is_array) { 2819 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2749 const std::string index = regs.GetRegisterAsInteger(instr.gpr8); 2820 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2750 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2821 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2751 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); 2822 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2752 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2753 coord =
2754 "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");";
2755 } else {
2756 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2757 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2758 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2759 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2760 }
2761 break; 2823 break;
2762 } 2824 }
2763 default: 2825 default:
2764 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", 2826 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2765 static_cast<u32>(num_coordinates)); 2827 static_cast<u32>(num_coordinates));
2766 UNREACHABLE();
2767 2828
2768 // Fallback to interpreting as a 2D texture for now 2829 // Fallback to interpreting as a 2D texture for now
2769 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2830 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
@@ -2775,7 +2836,7 @@ private:
2775 const std::string sampler = 2836 const std::string sampler =
2776 GetSampler(instr.sampler, texture_type, is_array, depth_compare); 2837 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
2777 std::string texture; 2838 std::string texture;
2778 switch (instr.texs.GetTextureProcessMode()) { 2839 switch (process_mode) {
2779 case Tegra::Shader::TextureProcessMode::None: { 2840 case Tegra::Shader::TextureProcessMode::None: {
2780 texture = "texture(" + sampler + ", coords)"; 2841 texture = "texture(" + sampler + ", coords)";
2781 break; 2842 break;
@@ -2789,15 +2850,13 @@ private:
2789 break; 2850 break;
2790 } 2851 }
2791 case Tegra::Shader::TextureProcessMode::LL: { 2852 case Tegra::Shader::TextureProcessMode::LL: {
2792 const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2853 texture = "textureLod(" + sampler + ", coords, " + lod_value + ')';
2793 texture = "textureLod(" + sampler + ", coords, " + op_c + ')';
2794 break; 2854 break;
2795 } 2855 }
2796 default: { 2856 default: {
2797 texture = "texture(" + sampler + ", coords)"; 2857 texture = "texture(" + sampler + ", coords)";
2798 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2858 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2799 static_cast<u32>(instr.texs.GetTextureProcessMode())); 2859 static_cast<u32>(instr.texs.GetTextureProcessMode()));
2800 UNREACHABLE();
2801 } 2860 }
2802 } 2861 }
2803 if (!depth_compare) { 2862 if (!depth_compare) {
@@ -2805,47 +2864,48 @@ private:
2805 } else { 2864 } else {
2806 WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); 2865 WriteTexsInstruction(instr, coord, "vec4(" + texture + ')');
2807 } 2866 }
2867
2808 break; 2868 break;
2809 } 2869 }
2810 case OpCode::Id::TLDS: { 2870 case OpCode::Id::TLDS: {
2811 std::string coord;
2812 const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; 2871 const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()};
2813 const bool is_array{instr.tlds.IsArrayTexture()}; 2872 const bool is_array{instr.tlds.IsArrayTexture()};
2814 2873
2815 ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D); 2874 ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D);
2816 ASSERT(is_array == false); 2875 ASSERT(is_array == false);
2817 2876
2818 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2877 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2819 "NODEP is not implemented"); 2878 "NODEP is not implemented");
2820 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2879 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2821 "AOFFI is not implemented"); 2880 "AOFFI is not implemented");
2822 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), 2881 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2823 "MZ is not implemented"); 2882 "MZ is not implemented");
2824 2883
2825 u32 op_c_offset = 0; 2884 u32 extra_op_offset = 0;
2885
2886 // Scope to avoid variable name overlaps.
2887 shader.AddLine('{');
2888 ++shader.scope;
2889 std::string coords;
2826 2890
2827 switch (texture_type) { 2891 switch (texture_type) {
2828 case Tegra::Shader::TextureType::Texture1D: { 2892 case Tegra::Shader::TextureType::Texture1D: {
2829 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2893 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2830 coord = "int coords = " + x + ';'; 2894 coords = "float coords = " + x + ';';
2831 break; 2895 break;
2832 } 2896 }
2833 case Tegra::Shader::TextureType::Texture2D: { 2897 case Tegra::Shader::TextureType::Texture2D: {
2834 if (is_array) { 2898 UNIMPLEMENTED_IF_MSG(is_array, "Unhandled 2d array texture");
2835 LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture"); 2899
2836 UNREACHABLE(); 2900 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2837 } else { 2901 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2838 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2902 // shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");");
2839 const std::string y = regs.GetRegisterAsInteger(instr.gpr20); 2903 coords = "ivec2 coords = ivec2(" + x + ", " + y + ");";
2840 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; 2904 extra_op_offset = 1;
2841 op_c_offset = 1;
2842 }
2843 break; 2905 break;
2844 } 2906 }
2845 default: 2907 default:
2846 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", 2908 UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type));
2847 static_cast<u32>(texture_type));
2848 UNREACHABLE();
2849 } 2909 }
2850 const std::string sampler = 2910 const std::string sampler =
2851 GetSampler(instr.sampler, texture_type, is_array, false); 2911 GetSampler(instr.sampler, texture_type, is_array, false);
@@ -2856,19 +2916,22 @@ private:
2856 break; 2916 break;
2857 } 2917 }
2858 case Tegra::Shader::TextureProcessMode::LL: { 2918 case Tegra::Shader::TextureProcessMode::LL: {
2859 const std::string op_c = 2919 shader.AddLine(
2860 regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset); 2920 "float lod = " +
2861 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; 2921 regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';');
2922 texture = "texelFetch(" + sampler + ", coords, lod)";
2862 break; 2923 break;
2863 } 2924 }
2864 default: { 2925 default: {
2865 texture = "texelFetch(" + sampler + ", coords, 0)"; 2926 texture = "texelFetch(" + sampler + ", coords, 0)";
2866 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2927 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2867 static_cast<u32>(instr.tlds.GetTextureProcessMode())); 2928 static_cast<u32>(instr.tlds.GetTextureProcessMode()));
2868 UNREACHABLE();
2869 } 2929 }
2870 } 2930 }
2871 WriteTexsInstruction(instr, coord, texture); 2931 WriteTexsInstruction(instr, coords, texture);
2932
2933 --shader.scope;
2934 shader.AddLine('}');
2872 break; 2935 break;
2873 } 2936 }
2874 case OpCode::Id::TLD4: { 2937 case OpCode::Id::TLD4: {
@@ -2876,14 +2939,14 @@ private:
2876 ASSERT(instr.tld4.array == 0); 2939 ASSERT(instr.tld4.array == 0);
2877 std::string coord; 2940 std::string coord;
2878 2941
2879 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2942 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2880 "NODEP is not implemented"); 2943 "NODEP is not implemented");
2881 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2944 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2882 "AOFFI is not implemented"); 2945 "AOFFI is not implemented");
2883 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 2946 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
2884 "NDV is not implemented"); 2947 "NDV is not implemented");
2885 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), 2948 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP),
2886 "PTP is not implemented"); 2949 "PTP is not implemented");
2887 const bool depth_compare = 2950 const bool depth_compare =
2888 instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2951 instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2889 auto texture_type = instr.tld4.texture_type.Value(); 2952 auto texture_type = instr.tld4.texture_type.Value();
@@ -2891,61 +2954,71 @@ private:
2891 if (depth_compare) 2954 if (depth_compare)
2892 num_coordinates += 1; 2955 num_coordinates += 1;
2893 2956
2957 // Add an extra scope and declare the texture coords inside to prevent
2958 // overwriting them in case they are used as outputs of the texs instruction.
2959 shader.AddLine('{');
2960 ++shader.scope;
2961
2894 switch (num_coordinates) { 2962 switch (num_coordinates) {
2895 case 2: { 2963 case 2: {
2896 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2964 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2897 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2965 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2898 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2966 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2899 break; 2967 break;
2900 } 2968 }
2901 case 3: { 2969 case 3: {
2902 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2970 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2903 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2971 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2904 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); 2972 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2905 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2973 shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");");
2906 break; 2974 break;
2907 } 2975 }
2908 default: 2976 default:
2909 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", 2977 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2910 static_cast<u32>(num_coordinates)); 2978 static_cast<u32>(num_coordinates));
2911 UNREACHABLE();
2912 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2979 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2913 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2980 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2914 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2981 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2915 texture_type = Tegra::Shader::TextureType::Texture2D; 2982 texture_type = Tegra::Shader::TextureType::Texture2D;
2916 } 2983 }
2917 2984
2918 const std::string sampler = 2985 const std::string sampler =
2919 GetSampler(instr.sampler, texture_type, false, depth_compare); 2986 GetSampler(instr.sampler, texture_type, false, depth_compare);
2920 // Add an extra scope and declare the texture coords inside to prevent 2987
2921 // overwriting them in case they are used as outputs of the texs instruction.
2922 shader.AddLine("{");
2923 ++shader.scope;
2924 shader.AddLine(coord);
2925 const std::string texture = "textureGather(" + sampler + ", coords, " + 2988 const std::string texture = "textureGather(" + sampler + ", coords, " +
2926 std::to_string(instr.tld4.component) + ')'; 2989 std::to_string(instr.tld4.component) + ')';
2990
2927 if (!depth_compare) { 2991 if (!depth_compare) {
2992 shader.AddLine("vec4 texture_tmp = " + texture + ';');
2928 std::size_t dest_elem{}; 2993 std::size_t dest_elem{};
2929 for (std::size_t elem = 0; elem < 4; ++elem) { 2994 for (std::size_t elem = 0; elem < 4; ++elem) {
2930 if (!instr.tex.IsComponentEnabled(elem)) { 2995 if (!instr.tex.IsComponentEnabled(elem)) {
2931 // Skip disabled components 2996 // Skip disabled components
2932 continue; 2997 continue;
2933 } 2998 }
2934 regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); 2999 regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false,
3000 dest_elem);
2935 ++dest_elem; 3001 ++dest_elem;
2936 } 3002 }
2937 } else { 3003 } else {
2938 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); 3004 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
2939 } 3005 }
2940 --shader.scope; 3006 --shader.scope;
2941 shader.AddLine("}"); 3007 shader.AddLine('}');
2942 break; 3008 break;
2943 } 3009 }
2944 case OpCode::Id::TLD4S: { 3010 case OpCode::Id::TLD4S: {
2945 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 3011 UNIMPLEMENTED_IF_MSG(
2946 "NODEP is not implemented"); 3012 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2947 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 3013 "NODEP is not implemented");
2948 "AOFFI is not implemented"); 3014 UNIMPLEMENTED_IF_MSG(
3015 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
3016 "AOFFI is not implemented");
3017
3018 // Scope to avoid variable name overlaps.
3019 shader.AddLine('{');
3020 ++shader.scope;
3021 std::string coords;
2949 3022
2950 const bool depth_compare = 3023 const bool depth_compare =
2951 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 3024 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
@@ -2954,28 +3027,32 @@ private:
2954 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. 3027 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
2955 const std::string sampler = GetSampler( 3028 const std::string sampler = GetSampler(
2956 instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); 3029 instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare);
2957 std::string coord;
2958 if (!depth_compare) { 3030 if (!depth_compare) {
2959 coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; 3031 coords = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
2960 } else { 3032 } else {
2961 // Note: TLD4S coordinate encoding works just like TEXS's 3033 // Note: TLD4S coordinate encoding works just like TEXS's
2962 const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 3034 const std::string op_y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2963 coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");"; 3035 coords = "vec3 coords = vec3(" + op_a + ", " + op_y + ", " + op_b + ");";
2964 } 3036 }
2965 const std::string texture = "textureGather(" + sampler + ", coords, " + 3037 const std::string texture = "textureGather(" + sampler + ", coords, " +
2966 std::to_string(instr.tld4s.component) + ')'; 3038 std::to_string(instr.tld4s.component) + ')';
2967 3039
2968 if (!depth_compare) { 3040 if (!depth_compare) {
2969 WriteTexsInstruction(instr, coord, texture); 3041 WriteTexsInstruction(instr, coords, texture);
2970 } else { 3042 } else {
2971 WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); 3043 WriteTexsInstruction(instr, coords, "vec4(" + texture + ')');
2972 } 3044 }
3045
3046 --shader.scope;
3047 shader.AddLine('}');
2973 break; 3048 break;
2974 } 3049 }
2975 case OpCode::Id::TXQ: { 3050 case OpCode::Id::TXQ: {
2976 ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 3051 UNIMPLEMENTED_IF_MSG(instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2977 "NODEP is not implemented"); 3052 "NODEP is not implemented");
2978 3053
3054 ++shader.scope;
3055 shader.AddLine('{');
2979 // TODO: the new commits on the texture refactor, change the way samplers work. 3056 // TODO: the new commits on the texture refactor, change the way samplers work.
2980 // Sadly, not all texture instructions specify the type of texture their sampler 3057 // Sadly, not all texture instructions specify the type of texture their sampler
2981 // uses. This must be fixed at a later instance. 3058 // uses. This must be fixed at a later instance.
@@ -2983,23 +3060,30 @@ private:
2983 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); 3060 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false);
2984 switch (instr.txq.query_type) { 3061 switch (instr.txq.query_type) {
2985 case Tegra::Shader::TextureQueryType::Dimension: { 3062 case Tegra::Shader::TextureQueryType::Dimension: {
2986 const std::string texture = "textureQueryLevels(" + sampler + ')'; 3063 const std::string texture = "textureSize(" + sampler + ", " +
2987 regs.SetRegisterToInteger(instr.gpr0, true, 0, texture, 1, 1); 3064 regs.GetRegisterAsInteger(instr.gpr8) + ')';
3065 const std::string mip_level = "textureQueryLevels(" + sampler + ')';
3066 shader.AddLine("ivec2 sizes = " + texture + ';');
3067 regs.SetRegisterToInteger(instr.gpr0, true, 0, "sizes.x", 1, 1);
3068 regs.SetRegisterToInteger(instr.gpr0.Value() + 1, true, 0, "sizes.y", 1, 1);
3069 regs.SetRegisterToInteger(instr.gpr0.Value() + 2, true, 0, "0", 1, 1);
3070 regs.SetRegisterToInteger(instr.gpr0.Value() + 3, true, 0, mip_level, 1, 1);
2988 break; 3071 break;
2989 } 3072 }
2990 default: { 3073 default: {
2991 LOG_CRITICAL(HW_GPU, "Unhandled texture query type: {}", 3074 UNIMPLEMENTED_MSG("Unhandled texture query type: {}",
2992 static_cast<u32>(instr.txq.query_type.Value())); 3075 static_cast<u32>(instr.txq.query_type.Value()));
2993 UNREACHABLE();
2994 } 3076 }
2995 } 3077 }
3078 --shader.scope;
3079 shader.AddLine('}');
2996 break; 3080 break;
2997 } 3081 }
2998 case OpCode::Id::TMML: { 3082 case OpCode::Id::TMML: {
2999 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 3083 UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
3000 "NODEP is not implemented"); 3084 "NODEP is not implemented");
3001 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 3085 UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
3002 "NDV is not implemented"); 3086 "NDV is not implemented");
3003 3087
3004 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 3088 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
3005 const bool is_array = instr.tmml.array != 0; 3089 const bool is_array = instr.tmml.array != 0;
@@ -3021,9 +3105,7 @@ private:
3021 break; 3105 break;
3022 } 3106 }
3023 default: 3107 default:
3024 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", 3108 UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type));
3025 static_cast<u32>(texture_type));
3026 UNREACHABLE();
3027 3109
3028 // Fallback to interpreting as a 2D texture for now 3110 // Fallback to interpreting as a 2D texture for now
3029 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 3111 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
@@ -3046,8 +3128,7 @@ private:
3046 break; 3128 break;
3047 } 3129 }
3048 default: { 3130 default: {
3049 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->get().GetName()); 3131 UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
3050 UNREACHABLE();
3051 } 3132 }
3052 } 3133 }
3053 break; 3134 break;
@@ -3133,7 +3214,7 @@ private:
3133 break; 3214 break;
3134 } 3215 }
3135 case OpCode::Type::HalfSetPredicate: { 3216 case OpCode::Type::HalfSetPredicate: {
3136 ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented"); 3217 UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0);
3137 3218
3138 const std::string op_a = 3219 const std::string op_a =
3139 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a, 3220 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a,
@@ -3178,6 +3259,9 @@ private:
3178 break; 3259 break;
3179 } 3260 }
3180 case OpCode::Type::PredicateSetRegister: { 3261 case OpCode::Type::PredicateSetRegister: {
3262 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
3263 "Condition codes generation in PSET is not implemented");
3264
3181 const std::string op_a = 3265 const std::string op_a =
3182 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); 3266 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
3183 const std::string op_b = 3267 const std::string op_b =
@@ -3198,12 +3282,6 @@ private:
3198 const std::string value = '(' + result + ") ? 1.0 : 0.0"; 3282 const std::string value = '(' + result + ") ? 1.0 : 0.0";
3199 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); 3283 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
3200 } 3284 }
3201
3202 if (instr.generates_cc) {
3203 LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code");
3204 UNREACHABLE();
3205 }
3206
3207 break; 3285 break;
3208 } 3286 }
3209 case OpCode::Type::PredicateSetPredicate: { 3287 case OpCode::Type::PredicateSetPredicate: {
@@ -3241,25 +3319,51 @@ private:
3241 const std::string pred = 3319 const std::string pred =
3242 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 3320 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
3243 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 3321 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
3244 const std::string control_code = regs.GetControlCode(instr.csetp.cc); 3322 const std::string condition_code = regs.GetConditionCode(instr.csetp.cc);
3245 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 3323 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
3246 SetPredicate(instr.csetp.pred3, 3324 SetPredicate(instr.csetp.pred3,
3247 '(' + control_code + ") " + combiner + " (" + pred + ')'); 3325 '(' + condition_code + ") " + combiner + " (" + pred + ')');
3248 } 3326 }
3249 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 3327 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
3250 SetPredicate(instr.csetp.pred0, 3328 SetPredicate(instr.csetp.pred0,
3251 "!(" + control_code + ") " + combiner + " (" + pred + ')'); 3329 "!(" + condition_code + ") " + combiner + " (" + pred + ')');
3252 } 3330 }
3253 break; 3331 break;
3254 } 3332 }
3255 default: { 3333 default: {
3256 LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", 3334 UNIMPLEMENTED_MSG("Unhandled predicate instruction: {}", opcode->get().GetName());
3257 opcode->get().GetName());
3258 UNREACHABLE();
3259 } 3335 }
3260 } 3336 }
3261 break; 3337 break;
3262 } 3338 }
3339 case OpCode::Type::RegisterSetPredicate: {
3340 UNIMPLEMENTED_IF(instr.r2p.mode != Tegra::Shader::R2pMode::Pr);
3341
3342 const std::string apply_mask = [&]() {
3343 switch (opcode->get().GetId()) {
3344 case OpCode::Id::R2P_IMM:
3345 return std::to_string(instr.r2p.immediate_mask);
3346 default:
3347 UNREACHABLE();
3348 }
3349 }();
3350 const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) +
3351 " >> " + std::to_string(instr.r2p.byte) + ')';
3352
3353 constexpr u64 programmable_preds = 7;
3354 for (u64 pred = 0; pred < programmable_preds; ++pred) {
3355 const auto shift = std::to_string(1 << pred);
3356
3357 shader.AddLine("if ((" + apply_mask + " & " + shift + ") != 0) {");
3358 ++shader.scope;
3359
3360 SetPredicate(pred, '(' + mask + " & " + shift + ") != 0");
3361
3362 --shader.scope;
3363 shader.AddLine('}');
3364 }
3365 break;
3366 }
3263 case OpCode::Type::FloatSet: { 3367 case OpCode::Type::FloatSet: {
3264 const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), 3368 const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8),
3265 instr.fset.abs_a != 0, instr.fset.neg_a != 0); 3369 instr.fset.abs_a != 0, instr.fset.neg_a != 0);
@@ -3297,6 +3401,10 @@ private:
3297 regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1, 3401 regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
3298 1); 3402 1);
3299 } 3403 }
3404 if (instr.generates_cc.Value() != 0) {
3405 regs.SetInternalFlag(InternalFlag::ZeroFlag, predicate);
3406 LOG_WARNING(HW_GPU, "FSET Condition Code is incomplete");
3407 }
3300 break; 3408 break;
3301 } 3409 }
3302 case OpCode::Type::IntegerSet: { 3410 case OpCode::Type::IntegerSet: {
@@ -3335,7 +3443,7 @@ private:
3335 break; 3443 break;
3336 } 3444 }
3337 case OpCode::Type::HalfSet: { 3445 case OpCode::Type::HalfSet: {
3338 ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented"); 3446 UNIMPLEMENTED_IF(instr.hset2.ftz != 0);
3339 3447
3340 const std::string op_a = 3448 const std::string op_a =
3341 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a, 3449 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a,
@@ -3379,15 +3487,17 @@ private:
3379 break; 3487 break;
3380 } 3488 }
3381 case OpCode::Type::Xmad: { 3489 case OpCode::Type::Xmad: {
3382 ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); 3490 UNIMPLEMENTED_IF(instr.xmad.sign_a);
3383 ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); 3491 UNIMPLEMENTED_IF(instr.xmad.sign_b);
3492 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
3493 "Condition codes generation in XMAD is not implemented");
3384 3494
3385 std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; 3495 std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)};
3386 std::string op_b; 3496 std::string op_b;
3387 std::string op_c; 3497 std::string op_c;
3388 3498
3389 // TODO(bunnei): Needs to be fixed once op_a or op_b is signed 3499 // TODO(bunnei): Needs to be fixed once op_a or op_b is signed
3390 ASSERT_MSG(instr.xmad.sign_a == instr.xmad.sign_b, "Unimplemented"); 3500 UNIMPLEMENTED_IF(instr.xmad.sign_a != instr.xmad.sign_b);
3391 const bool is_signed{instr.xmad.sign_a == 1}; 3501 const bool is_signed{instr.xmad.sign_a == 1};
3392 3502
3393 bool is_merge{}; 3503 bool is_merge{};
@@ -3420,8 +3530,7 @@ private:
3420 break; 3530 break;
3421 } 3531 }
3422 default: { 3532 default: {
3423 LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->get().GetName()); 3533 UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName());
3424 UNREACHABLE();
3425 } 3534 }
3426 } 3535 }
3427 3536
@@ -3457,9 +3566,8 @@ private:
3457 op_c = "((" + op_c + ") + (" + src2 + "<< 16))"; 3566 op_c = "((" + op_c + ") + (" + src2 + "<< 16))";
3458 break; 3567 break;
3459 default: { 3568 default: {
3460 LOG_CRITICAL(HW_GPU, "Unhandled XMAD mode: {}", 3569 UNIMPLEMENTED_MSG("Unhandled XMAD mode: {}",
3461 static_cast<u32>(instr.xmad.mode.Value())); 3570 static_cast<u32>(instr.xmad.mode.Value()));
3462 UNREACHABLE();
3463 } 3571 }
3464 } 3572 }
3465 3573
@@ -3469,25 +3577,19 @@ private:
3469 } 3577 }
3470 3578
3471 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); 3579 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1);
3472 if (instr.generates_cc) {
3473 LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code");
3474 UNREACHABLE();
3475 }
3476 break; 3580 break;
3477 } 3581 }
3478 default: { 3582 default: {
3479 switch (opcode->get().GetId()) { 3583 switch (opcode->get().GetId()) {
3480 case OpCode::Id::EXIT: { 3584 case OpCode::Id::EXIT: {
3585 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3586 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3587 "EXIT condition code used: {}", static_cast<u32>(cc));
3588
3481 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { 3589 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
3482 EmitFragmentOutputsWrite(); 3590 EmitFragmentOutputsWrite();
3483 } 3591 }
3484 3592
3485 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3486 if (cc != Tegra::Shader::ControlCode::T) {
3487 LOG_CRITICAL(HW_GPU, "EXIT Control Code used: {}", static_cast<u32>(cc));
3488 UNREACHABLE();
3489 }
3490
3491 switch (instr.flow.cond) { 3593 switch (instr.flow.cond) {
3492 case Tegra::Shader::FlowCondition::Always: 3594 case Tegra::Shader::FlowCondition::Always:
3493 shader.AddLine("return true;"); 3595 shader.AddLine("return true;");
@@ -3502,26 +3604,24 @@ private:
3502 case Tegra::Shader::FlowCondition::Fcsm_Tr: 3604 case Tegra::Shader::FlowCondition::Fcsm_Tr:
3503 // TODO(bunnei): What is this used for? If we assume this conditon is not 3605 // TODO(bunnei): What is this used for? If we assume this conditon is not
3504 // satisifed, dual vertex shaders in Farming Simulator make more sense 3606 // satisifed, dual vertex shaders in Farming Simulator make more sense
3505 LOG_CRITICAL(HW_GPU, "Skipping unknown FlowCondition::Fcsm_Tr"); 3607 UNIMPLEMENTED_MSG("Skipping unknown FlowCondition::Fcsm_Tr");
3506 break; 3608 break;
3507 3609
3508 default: 3610 default:
3509 LOG_CRITICAL(HW_GPU, "Unhandled flow condition: {}", 3611 UNIMPLEMENTED_MSG("Unhandled flow condition: {}",
3510 static_cast<u32>(instr.flow.cond.Value())); 3612 static_cast<u32>(instr.flow.cond.Value()));
3511 UNREACHABLE();
3512 } 3613 }
3513 break; 3614 break;
3514 } 3615 }
3515 case OpCode::Id::KIL: { 3616 case OpCode::Id::KIL: {
3516 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3617 UNIMPLEMENTED_IF(instr.flow.cond != Tegra::Shader::FlowCondition::Always);
3618
3619 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3620 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3621 "KIL condition code used: {}", static_cast<u32>(cc));
3517 3622
3518 // Enclose "discard" in a conditional, so that GLSL compilation does not complain 3623 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
3519 // about unexecuted instructions that may follow this. 3624 // about unexecuted instructions that may follow this.
3520 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3521 if (cc != Tegra::Shader::ControlCode::T) {
3522 LOG_CRITICAL(HW_GPU, "KIL Control Code used: {}", static_cast<u32>(cc));
3523 UNREACHABLE();
3524 }
3525 shader.AddLine("if (true) {"); 3625 shader.AddLine("if (true) {");
3526 ++shader.scope; 3626 ++shader.scope;
3527 shader.AddLine("discard;"); 3627 shader.AddLine("discard;");
@@ -3531,7 +3631,8 @@ private:
3531 break; 3631 break;
3532 } 3632 }
3533 case OpCode::Id::OUT_R: { 3633 case OpCode::Id::OUT_R: {
3534 ASSERT(instr.gpr20.Value() == Register::ZeroIndex); 3634 UNIMPLEMENTED_IF_MSG(instr.gpr20.Value() != Register::ZeroIndex,
3635 "Stream buffer is not supported");
3535 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, 3636 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
3536 "OUT is expected to be used in a geometry shader."); 3637 "OUT is expected to be used in a geometry shader.");
3537 3638
@@ -3557,19 +3658,23 @@ private:
3557 regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1); 3658 regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1);
3558 break; 3659 break;
3559 } 3660 }
3661 case Tegra::Shader::SystemVariable::Ydirection: {
3662 // Config pack's third value is Y_NEGATE's state.
3663 regs.SetRegisterToFloat(instr.gpr0, 0, "uintBitsToFloat(config_pack[2])", 1, 1);
3664 break;
3665 }
3560 default: { 3666 default: {
3561 LOG_CRITICAL(HW_GPU, "Unhandled system move: {}", 3667 UNIMPLEMENTED_MSG("Unhandled system move: {}",
3562 static_cast<u32>(instr.sys20.Value())); 3668 static_cast<u32>(instr.sys20.Value()));
3563 UNREACHABLE();
3564 } 3669 }
3565 } 3670 }
3566 break; 3671 break;
3567 } 3672 }
3568 case OpCode::Id::ISBERD: { 3673 case OpCode::Id::ISBERD: {
3569 ASSERT(instr.isberd.o == 0); 3674 UNIMPLEMENTED_IF(instr.isberd.o != 0);
3570 ASSERT(instr.isberd.skew == 0); 3675 UNIMPLEMENTED_IF(instr.isberd.skew != 0);
3571 ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None); 3676 UNIMPLEMENTED_IF(instr.isberd.shift != Tegra::Shader::IsberdShift::None);
3572 ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None); 3677 UNIMPLEMENTED_IF(instr.isberd.mode != Tegra::Shader::IsberdMode::None);
3573 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, 3678 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
3574 "ISBERD is expected to be used in a geometry shader."); 3679 "ISBERD is expected to be used in a geometry shader.");
3575 LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete"); 3680 LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete");
@@ -3577,15 +3682,21 @@ private:
3577 break; 3682 break;
3578 } 3683 }
3579 case OpCode::Id::BRA: { 3684 case OpCode::Id::BRA: {
3580 ASSERT_MSG(instr.bra.constant_buffer == 0, 3685 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
3581 "BRA with constant buffers are not implemented"); 3686 "BRA with constant buffers are not implemented");
3582 const Tegra::Shader::ControlCode cc = instr.flow_control_code; 3687
3583 if (cc != Tegra::Shader::ControlCode::T) { 3688 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3584 LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc));
3585 UNREACHABLE();
3586 }
3587 const u32 target = offset + instr.bra.GetBranchTarget(); 3689 const u32 target = offset + instr.bra.GetBranchTarget();
3588 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); 3690 if (cc != Tegra::Shader::ConditionCode::T) {
3691 const std::string condition_code = regs.GetConditionCode(cc);
3692 shader.AddLine("if (" + condition_code + "){");
3693 shader.scope++;
3694 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
3695 shader.scope--;
3696 shader.AddLine('}');
3697 } else {
3698 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
3699 }
3589 break; 3700 break;
3590 } 3701 }
3591 case OpCode::Id::IPA: { 3702 case OpCode::Id::IPA: {
@@ -3606,7 +3717,8 @@ private:
3606 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it 3717 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it
3607 // sets the target of the jump that the SYNC instruction will make. The SSY opcode 3718 // sets the target of the jump that the SYNC instruction will make. The SSY opcode
3608 // has a similar structure to the BRA opcode. 3719 // has a similar structure to the BRA opcode.
3609 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported"); 3720 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
3721 "Constant buffer flow is not supported");
3610 3722
3611 const u32 target = offset + instr.bra.GetBranchTarget(); 3723 const u32 target = offset + instr.bra.GetBranchTarget();
3612 EmitPushToFlowStack(target); 3724 EmitPushToFlowStack(target);
@@ -3616,29 +3728,28 @@ private:
3616 // PBK pushes to a stack the address where BRK will jump to. This shares stack with 3728 // PBK pushes to a stack the address where BRK will jump to. This shares stack with
3617 // SSY but using SYNC on a PBK address will kill the shader execution. We don't 3729 // SSY but using SYNC on a PBK address will kill the shader execution. We don't
3618 // emulate this because it's very unlikely a driver will emit such invalid shader. 3730 // emulate this because it's very unlikely a driver will emit such invalid shader.
3619 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported"); 3731 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
3732 "Constant buffer PBK is not supported");
3620 3733
3621 const u32 target = offset + instr.bra.GetBranchTarget(); 3734 const u32 target = offset + instr.bra.GetBranchTarget();
3622 EmitPushToFlowStack(target); 3735 EmitPushToFlowStack(target);
3623 break; 3736 break;
3624 } 3737 }
3625 case OpCode::Id::SYNC: { 3738 case OpCode::Id::SYNC: {
3739 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3740 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3741 "SYNC condition code used: {}", static_cast<u32>(cc));
3742
3626 // The SYNC opcode jumps to the address previously set by the SSY opcode 3743 // The SYNC opcode jumps to the address previously set by the SSY opcode
3627 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3628 if (cc != Tegra::Shader::ControlCode::T) {
3629 LOG_CRITICAL(HW_GPU, "SYNC Control Code used: {}", static_cast<u32>(cc));
3630 UNREACHABLE();
3631 }
3632 EmitPopFromFlowStack(); 3744 EmitPopFromFlowStack();
3633 break; 3745 break;
3634 } 3746 }
3635 case OpCode::Id::BRK: { 3747 case OpCode::Id::BRK: {
3636 // The BRK opcode jumps to the address previously set by the PBK opcode 3748 // The BRK opcode jumps to the address previously set by the PBK opcode
3637 const Tegra::Shader::ControlCode cc = instr.flow_control_code; 3749 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3638 if (cc != Tegra::Shader::ControlCode::T) { 3750 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3639 LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc)); 3751 "BRK condition code used: {}", static_cast<u32>(cc));
3640 UNREACHABLE(); 3752
3641 }
3642 EmitPopFromFlowStack(); 3753 EmitPopFromFlowStack();
3643 break; 3754 break;
3644 } 3755 }
@@ -3649,6 +3760,9 @@ private:
3649 break; 3760 break;
3650 } 3761 }
3651 case OpCode::Id::VMAD: { 3762 case OpCode::Id::VMAD: {
3763 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
3764 "Condition codes generation in VMAD is not implemented");
3765
3652 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1; 3766 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
3653 const std::string op_a = GetVideoOperandA(instr); 3767 const std::string op_a = GetVideoOperandA(instr);
3654 const std::string op_b = GetVideoOperandB(instr); 3768 const std::string op_b = GetVideoOperandB(instr);
@@ -3668,11 +3782,6 @@ private:
3668 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, 3782 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3669 instr.vmad.saturate == 1, 0, Register::Size::Word, 3783 instr.vmad.saturate == 1, 0, Register::Size::Word,
3670 instr.vmad.cc); 3784 instr.vmad.cc);
3671 if (instr.generates_cc) {
3672 LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code");
3673 UNREACHABLE();
3674 }
3675
3676 break; 3785 break;
3677 } 3786 }
3678 case OpCode::Id::VSETP: { 3787 case OpCode::Id::VSETP: {
@@ -3699,10 +3808,7 @@ private:
3699 } 3808 }
3700 break; 3809 break;
3701 } 3810 }
3702 default: { 3811 default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); }
3703 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->get().GetName());
3704 UNREACHABLE();
3705 }
3706 } 3812 }
3707 3813
3708 break; 3814 break;
@@ -3827,6 +3933,7 @@ private:
3827 Maxwell3D::Regs::ShaderStage stage; 3933 Maxwell3D::Regs::ShaderStage stage;
3828 const std::string& suffix; 3934 const std::string& suffix;
3829 u64 local_memory_size; 3935 u64 local_memory_size;
3936 std::size_t shader_length;
3830 3937
3831 ShaderWriter shader; 3938 ShaderWriter shader;
3832 ShaderWriter declarations; 3939 ShaderWriter declarations;
@@ -3845,9 +3952,10 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u
3845 Maxwell3D::Regs::ShaderStage stage, 3952 Maxwell3D::Regs::ShaderStage stage,
3846 const std::string& suffix) { 3953 const std::string& suffix) {
3847 try { 3954 try {
3848 const auto subroutines = 3955 ControlFlowAnalyzer analyzer(program_code, main_offset, suffix);
3849 ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); 3956 const auto subroutines = analyzer.GetSubroutines();
3850 GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix); 3957 GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix,
3958 analyzer.GetShaderLength());
3851 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; 3959 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
3852 } catch (const DecompileFail& exception) { 3960 } catch (const DecompileFail& exception) {
3853 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); 3961 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what());
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index eea090e52..23ed91e27 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -24,8 +24,7 @@ layout (location = 0) out vec4 position;
24 24
25layout(std140) uniform vs_config { 25layout(std140) uniform vs_config {
26 vec4 viewport_flip; 26 vec4 viewport_flip;
27 uvec4 instance_id; 27 uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
28 uvec4 flip_stage;
29 uvec4 alpha_test; 28 uvec4 alpha_test;
30}; 29};
31)"; 30)";
@@ -63,7 +62,8 @@ void main() {
63 out += R"( 62 out += R"(
64 63
65 // Check if the flip stage is VertexB 64 // Check if the flip stage is VertexB
66 if (flip_stage[0] == 1) { 65 // Config pack's second value is flip_stage
66 if (config_pack[1] == 1) {
67 // Viewport can be flipped, which is unsupported by glViewport 67 // Viewport can be flipped, which is unsupported by glViewport
68 position.xy *= viewport_flip.xy; 68 position.xy *= viewport_flip.xy;
69 } 69 }
@@ -71,7 +71,7 @@ void main() {
71 71
72 // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 72 // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
73 // For now, this is here to bring order in lieu of proper emulation 73 // For now, this is here to bring order in lieu of proper emulation
74 if (flip_stage[0] == 1) { 74 if (config_pack[1] == 1) {
75 position.w = 1.0; 75 position.w = 1.0;
76 } 76 }
77} 77}
@@ -101,8 +101,7 @@ layout (location = 0) out vec4 position;
101 101
102layout (std140) uniform gs_config { 102layout (std140) uniform gs_config {
103 vec4 viewport_flip; 103 vec4 viewport_flip;
104 uvec4 instance_id; 104 uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
105 uvec4 flip_stage;
106 uvec4 alpha_test; 105 uvec4 alpha_test;
107}; 106};
108 107
@@ -139,8 +138,7 @@ layout (location = 0) in vec4 position;
139 138
140layout (std140) uniform fs_config { 139layout (std140) uniform fs_config {
141 vec4 viewport_flip; 140 vec4 viewport_flip;
142 uvec4 instance_id; 141 uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
143 uvec4 flip_stage;
144 uvec4 alpha_test; 142 uvec4 alpha_test;
145}; 143};
146 144
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 520b9d4e3..b425d98ae 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -163,6 +163,7 @@ private:
163struct ShaderEntries { 163struct ShaderEntries {
164 std::vector<ConstBufferEntry> const_buffer_entries; 164 std::vector<ConstBufferEntry> const_buffer_entries;
165 std::vector<SamplerEntry> texture_samplers; 165 std::vector<SamplerEntry> texture_samplers;
166 std::size_t shader_length;
166}; 167};
167 168
168using ProgramResult = std::pair<std::string, ShaderEntries>; 169using ProgramResult = std::pair<std::string, ShaderEntries>;
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 8b8869ecb..6a30c28d2 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -27,16 +27,18 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
27 alpha_test.func = func; 27 alpha_test.func = func;
28 alpha_test.ref = regs.alpha_test_ref; 28 alpha_test.ref = regs.alpha_test_ref;
29 29
30 // We only assign the instance to the first component of the vector, the rest is just padding. 30 instance_id = state.current_instance;
31 instance_id[0] = state.current_instance;
32 31
33 // Assign in which stage the position has to be flipped 32 // Assign in which stage the position has to be flipped
34 // (the last stage before the fragment shader). 33 // (the last stage before the fragment shader).
35 if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) { 34 if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) {
36 flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); 35 flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
37 } else { 36 } else {
38 flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); 37 flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
39 } 38 }
39
40 // Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value.
41 y_direction = regs.screen_y_control.y_negate == 0 ? 1.f : -1.f;
40} 42}
41 43
42} // namespace OpenGL::GLShader 44} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 2a069cdd8..b757f5f44 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -21,8 +21,11 @@ using Tegra::Engines::Maxwell3D;
21struct MaxwellUniformData { 21struct MaxwellUniformData {
22 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); 22 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
23 alignas(16) GLvec4 viewport_flip; 23 alignas(16) GLvec4 viewport_flip;
24 alignas(16) GLuvec4 instance_id; 24 struct alignas(16) {
25 alignas(16) GLuvec4 flip_stage; 25 GLuint instance_id;
26 GLuint flip_stage;
27 GLfloat y_direction;
28 };
26 struct alignas(16) { 29 struct alignas(16) {
27 GLuint enabled; 30 GLuint enabled;
28 GLuint func; 31 GLuint func;
@@ -30,7 +33,7 @@ struct MaxwellUniformData {
30 GLuint padding; 33 GLuint padding;
31 } alpha_test; 34 } alpha_test;
32}; 35};
33static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect"); 36static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect");
34static_assert(sizeof(MaxwellUniformData) < 16384, 37static_assert(sizeof(MaxwellUniformData) < 16384,
35 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); 38 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
36 39
@@ -67,6 +70,7 @@ public:
67 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); 70 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
68 state.draw.shader_program = 0; 71 state.draw.shader_program = 0;
69 state.draw.program_pipeline = pipeline.handle; 72 state.draw.program_pipeline = pipeline.handle;
73 state.geometry_shaders.enabled = (gs != 0);
70 } 74 }
71 75
72private: 76private:
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 98622a058..b3bfad6a0 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -14,7 +14,10 @@ OpenGLState OpenGLState::cur_state;
14bool OpenGLState::s_rgb_used; 14bool OpenGLState::s_rgb_used;
15OpenGLState::OpenGLState() { 15OpenGLState::OpenGLState() {
16 // These all match default OpenGL values 16 // These all match default OpenGL values
17 geometry_shaders.enabled = false;
17 framebuffer_srgb.enabled = false; 18 framebuffer_srgb.enabled = false;
19 multisample_control.alpha_to_coverage = false;
20 multisample_control.alpha_to_one = false;
18 cull.enabled = false; 21 cull.enabled = false;
19 cull.mode = GL_BACK; 22 cull.mode = GL_BACK;
20 cull.front_face = GL_CCW; 23 cull.front_face = GL_CCW;
@@ -50,12 +53,12 @@ OpenGLState::OpenGLState() {
50 item.height = 0; 53 item.height = 0;
51 item.depth_range_near = 0.0f; 54 item.depth_range_near = 0.0f;
52 item.depth_range_far = 1.0f; 55 item.depth_range_far = 1.0f;
56 item.scissor.enabled = false;
57 item.scissor.x = 0;
58 item.scissor.y = 0;
59 item.scissor.width = 0;
60 item.scissor.height = 0;
53 } 61 }
54 scissor.enabled = false;
55 scissor.x = 0;
56 scissor.y = 0;
57 scissor.width = 0;
58 scissor.height = 0;
59 for (auto& item : blend) { 62 for (auto& item : blend) {
60 item.enabled = true; 63 item.enabled = true;
61 item.rgb_equation = GL_FUNC_ADD; 64 item.rgb_equation = GL_FUNC_ADD;
@@ -88,6 +91,14 @@ OpenGLState::OpenGLState() {
88 clip_distance = {}; 91 clip_distance = {};
89 92
90 point.size = 1; 93 point.size = 1;
94 fragment_color_clamp.enabled = false;
95
96 polygon_offset.fill_enable = false;
97 polygon_offset.line_enable = false;
98 polygon_offset.point_enable = false;
99 polygon_offset.factor = 0.0f;
100 polygon_offset.units = 0.0f;
101 polygon_offset.clamp = 0.0f;
91} 102}
92 103
93void OpenGLState::ApplyDefaultState() { 104void OpenGLState::ApplyDefaultState() {
@@ -136,7 +147,7 @@ void OpenGLState::ApplyCulling() const {
136} 147}
137 148
138void OpenGLState::ApplyColorMask() const { 149void OpenGLState::ApplyColorMask() const {
139 if (GLAD_GL_ARB_viewport_array) { 150 if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) {
140 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 151 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
141 const auto& updated = color_mask[i]; 152 const auto& updated = color_mask[i];
142 const auto& current = cur_state.color_mask[i]; 153 const auto& current = cur_state.color_mask[i];
@@ -229,50 +240,88 @@ void OpenGLState::ApplyStencilTest() const {
229 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); 240 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
230 } 241 }
231} 242}
232 243// Viewport does not affects glClearBuffer so emulate viewport using scissor test
233void OpenGLState::ApplyScissor() const { 244void OpenGLState::EmulateViewportWithScissor() {
234 const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled; 245 auto& current = viewports[0];
235 if (scissor_changed) { 246 if (current.scissor.enabled) {
236 if (scissor.enabled) { 247 const GLint left = std::max(current.x, current.scissor.x);
237 glEnable(GL_SCISSOR_TEST); 248 const GLint right =
238 } else { 249 std::max(current.x + current.width, current.scissor.x + current.scissor.width);
239 glDisable(GL_SCISSOR_TEST); 250 const GLint bottom = std::max(current.y, current.scissor.y);
240 } 251 const GLint top =
241 } 252 std::max(current.y + current.height, current.scissor.y + current.scissor.height);
242 if (scissor.enabled && 253 current.scissor.x = std::max(left, 0);
243 (scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y || 254 current.scissor.y = std::max(bottom, 0);
244 scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) { 255 current.scissor.width = std::max(right - left, 0);
245 glScissor(scissor.x, scissor.y, scissor.width, scissor.height); 256 current.scissor.height = std::max(top - bottom, 0);
257 } else {
258 current.scissor.enabled = true;
259 current.scissor.x = current.x;
260 current.scissor.y = current.y;
261 current.scissor.width = current.width;
262 current.scissor.height = current.height;
246 } 263 }
247} 264}
248 265
249void OpenGLState::ApplyViewport() const { 266void OpenGLState::ApplyViewport() const {
250 if (GLAD_GL_ARB_viewport_array) { 267 if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
251 for (GLuint i = 0; 268 for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports);
252 i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); i++) { 269 i++) {
253 const auto& current = cur_state.viewports[i]; 270 const auto& current = cur_state.viewports[i];
254 const auto& updated = viewports[i]; 271 const auto& updated = viewports[i];
255 if (updated.x != current.x || updated.y != current.y || 272 if (updated.x != current.x || updated.y != current.y ||
256 updated.width != current.width || updated.height != current.height) { 273 updated.width != current.width || updated.height != current.height) {
257 glViewportIndexedf(i, updated.x, updated.y, updated.width, updated.height); 274 glViewportIndexedf(
275 i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y),
276 static_cast<GLfloat>(updated.width), static_cast<GLfloat>(updated.height));
258 } 277 }
259 if (updated.depth_range_near != current.depth_range_near || 278 if (updated.depth_range_near != current.depth_range_near ||
260 updated.depth_range_far != current.depth_range_far) { 279 updated.depth_range_far != current.depth_range_far) {
261 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far); 280 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
262 } 281 }
282 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
283 if (scissor_changed) {
284 if (updated.scissor.enabled) {
285 glEnablei(GL_SCISSOR_TEST, i);
286 } else {
287 glDisablei(GL_SCISSOR_TEST, i);
288 }
289 }
290 if (updated.scissor.enabled &&
291 (scissor_changed || updated.scissor.x != current.scissor.x ||
292 updated.scissor.y != current.scissor.y ||
293 updated.scissor.width != current.scissor.width ||
294 updated.scissor.height != current.scissor.height)) {
295 glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
296 updated.scissor.height);
297 }
263 } 298 }
264 } else { 299 } else {
265 const auto& current = cur_state.viewports[0]; 300 const auto& current = cur_state.viewports[0];
266 const auto& updated = viewports[0]; 301 const auto& updated = viewports[0];
267 if (updated.x != current.x || updated.y != current.y || updated.width != current.width || 302 if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
268 updated.height != current.height) { 303 updated.height != current.height) {
269 glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y), 304 glViewport(updated.x, updated.y, updated.width, updated.height);
270 static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height));
271 } 305 }
272 if (updated.depth_range_near != current.depth_range_near || 306 if (updated.depth_range_near != current.depth_range_near ||
273 updated.depth_range_far != current.depth_range_far) { 307 updated.depth_range_far != current.depth_range_far) {
274 glDepthRange(updated.depth_range_near, updated.depth_range_far); 308 glDepthRange(updated.depth_range_near, updated.depth_range_far);
275 } 309 }
310 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
311 if (scissor_changed) {
312 if (updated.scissor.enabled) {
313 glEnable(GL_SCISSOR_TEST);
314 } else {
315 glDisable(GL_SCISSOR_TEST);
316 }
317 }
318 if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x ||
319 updated.scissor.y != current.scissor.y ||
320 updated.scissor.width != current.scissor.width ||
321 updated.scissor.height != current.scissor.height)) {
322 glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width,
323 updated.scissor.height);
324 }
276 } 325 }
277} 326}
278 327
@@ -290,27 +339,16 @@ void OpenGLState::ApplyGlobalBlending() const {
290 if (!updated.enabled) { 339 if (!updated.enabled) {
291 return; 340 return;
292 } 341 }
293 if (updated.separate_alpha) { 342 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
294 if (blend_changed || updated.src_rgb_func != current.src_rgb_func || 343 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
295 updated.dst_rgb_func != current.dst_rgb_func || 344 updated.dst_a_func != current.dst_a_func) {
296 updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { 345 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
297 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, 346 updated.dst_a_func);
298 updated.dst_a_func); 347 }
299 }
300
301 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
302 updated.a_equation != current.a_equation) {
303 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
304 }
305 } else {
306 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
307 updated.dst_rgb_func != current.dst_rgb_func) {
308 glBlendFunc(updated.src_rgb_func, updated.dst_rgb_func);
309 }
310 348
311 if (blend_changed || updated.rgb_equation != current.rgb_equation) { 349 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
312 glBlendEquation(updated.rgb_equation); 350 updated.a_equation != current.a_equation) {
313 } 351 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
314 } 352 }
315} 353}
316 354
@@ -328,29 +366,17 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
328 if (!updated.enabled) { 366 if (!updated.enabled) {
329 return; 367 return;
330 } 368 }
331 if (updated.separate_alpha) { 369 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
332 if (blend_changed || updated.src_rgb_func != current.src_rgb_func || 370 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
333 updated.dst_rgb_func != current.dst_rgb_func || 371 updated.dst_a_func != current.dst_a_func) {
334 updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { 372 glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func,
335 glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func, 373 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
336 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); 374 }
337 }
338
339 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
340 updated.a_equation != current.a_equation) {
341 glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
342 updated.a_equation);
343 }
344 } else {
345 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
346 updated.dst_rgb_func != current.dst_rgb_func) {
347 glBlendFunciARB(static_cast<GLuint>(target), updated.src_rgb_func,
348 updated.dst_rgb_func);
349 }
350 375
351 if (blend_changed || updated.rgb_equation != current.rgb_equation) { 376 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
352 glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation); 377 updated.a_equation != current.a_equation) {
353 } 378 glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation,
379 updated.a_equation);
354 } 380 }
355} 381}
356 382
@@ -387,6 +413,55 @@ void OpenGLState::ApplyLogicOp() const {
387 } 413 }
388} 414}
389 415
416void OpenGLState::ApplyPolygonOffset() const {
417
418 const bool fill_enable_changed =
419 polygon_offset.fill_enable != cur_state.polygon_offset.fill_enable;
420 const bool line_enable_changed =
421 polygon_offset.line_enable != cur_state.polygon_offset.line_enable;
422 const bool point_enable_changed =
423 polygon_offset.point_enable != cur_state.polygon_offset.point_enable;
424 const bool factor_changed = polygon_offset.factor != cur_state.polygon_offset.factor;
425 const bool units_changed = polygon_offset.units != cur_state.polygon_offset.units;
426 const bool clamp_changed = polygon_offset.clamp != cur_state.polygon_offset.clamp;
427
428 if (fill_enable_changed) {
429 if (polygon_offset.fill_enable) {
430 glEnable(GL_POLYGON_OFFSET_FILL);
431 } else {
432 glDisable(GL_POLYGON_OFFSET_FILL);
433 }
434 }
435
436 if (line_enable_changed) {
437 if (polygon_offset.line_enable) {
438 glEnable(GL_POLYGON_OFFSET_LINE);
439 } else {
440 glDisable(GL_POLYGON_OFFSET_LINE);
441 }
442 }
443
444 if (point_enable_changed) {
445 if (polygon_offset.point_enable) {
446 glEnable(GL_POLYGON_OFFSET_POINT);
447 } else {
448 glDisable(GL_POLYGON_OFFSET_POINT);
449 }
450 }
451
452 if ((polygon_offset.fill_enable || polygon_offset.line_enable || polygon_offset.point_enable) &&
453 (factor_changed || units_changed || clamp_changed)) {
454
455 if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) {
456 glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp);
457 } else {
458 glPolygonOffset(polygon_offset.factor, polygon_offset.units);
459 UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0,
460 "Unimplemented Depth polygon offset clamp.");
461 }
462 }
463}
464
390void OpenGLState::ApplyTextures() const { 465void OpenGLState::ApplyTextures() const {
391 for (std::size_t i = 0; i < std::size(texture_units); ++i) { 466 for (std::size_t i = 0; i < std::size(texture_units); ++i) {
392 const auto& texture_unit = texture_units[i]; 467 const auto& texture_unit = texture_units[i];
@@ -481,9 +556,29 @@ void OpenGLState::Apply() const {
481 if (point.size != cur_state.point.size) { 556 if (point.size != cur_state.point.size) {
482 glPointSize(point.size); 557 glPointSize(point.size);
483 } 558 }
559 if (GLAD_GL_ARB_color_buffer_float) {
560 if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) {
561 glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
562 fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
563 }
564 }
565 if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) {
566 if (multisample_control.alpha_to_coverage) {
567 glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
568 } else {
569 glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
570 }
571 }
572 if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) {
573 if (multisample_control.alpha_to_one) {
574 glEnable(GL_SAMPLE_ALPHA_TO_ONE);
575 } else {
576 glDisable(GL_SAMPLE_ALPHA_TO_ONE);
577 }
578 }
579
484 ApplyColorMask(); 580 ApplyColorMask();
485 ApplyViewport(); 581 ApplyViewport();
486 ApplyScissor();
487 ApplyStencilTest(); 582 ApplyStencilTest();
488 ApplySRgb(); 583 ApplySRgb();
489 ApplyCulling(); 584 ApplyCulling();
@@ -493,6 +588,7 @@ void OpenGLState::Apply() const {
493 ApplyLogicOp(); 588 ApplyLogicOp();
494 ApplyTextures(); 589 ApplyTextures();
495 ApplySamplers(); 590 ApplySamplers();
591 ApplyPolygonOffset();
496 cur_state = *this; 592 cur_state = *this;
497} 593}
498 594
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index e5d1baae6..0bf19ed07 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -40,6 +40,19 @@ public:
40 } framebuffer_srgb; 40 } framebuffer_srgb;
41 41
42 struct { 42 struct {
43 bool alpha_to_coverage; // GL_ALPHA_TO_COVERAGE
44 bool alpha_to_one; // GL_ALPHA_TO_ONE
45 } multisample_control;
46
47 struct {
48 bool enabled; // GL_CLAMP_FRAGMENT_COLOR_ARB
49 } fragment_color_clamp;
50
51 struct {
52 bool enabled; // viewports arrays are only supported when geometry shaders are enabled.
53 } geometry_shaders;
54
55 struct {
43 bool enabled; // GL_CULL_FACE 56 bool enabled; // GL_CULL_FACE
44 GLenum mode; // GL_CULL_FACE_MODE 57 GLenum mode; // GL_CULL_FACE_MODE
45 GLenum front_face; // GL_FRONT_FACE 58 GLenum front_face; // GL_FRONT_FACE
@@ -79,7 +92,6 @@ public:
79 92
80 struct Blend { 93 struct Blend {
81 bool enabled; // GL_BLEND 94 bool enabled; // GL_BLEND
82 bool separate_alpha; // Independent blend enabled
83 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB 95 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
84 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA 96 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
85 GLenum src_rgb_func; // GL_BLEND_SRC_RGB 97 GLenum src_rgb_func; // GL_BLEND_SRC_RGB
@@ -144,27 +156,35 @@ public:
144 } draw; 156 } draw;
145 157
146 struct viewport { 158 struct viewport {
147 GLfloat x; 159 GLint x;
148 GLfloat y; 160 GLint y;
149 GLfloat width; 161 GLint width;
150 GLfloat height; 162 GLint height;
151 GLfloat depth_range_near; // GL_DEPTH_RANGE 163 GLfloat depth_range_near; // GL_DEPTH_RANGE
152 GLfloat depth_range_far; // GL_DEPTH_RANGE 164 GLfloat depth_range_far; // GL_DEPTH_RANGE
165 struct {
166 bool enabled; // GL_SCISSOR_TEST
167 GLint x;
168 GLint y;
169 GLsizei width;
170 GLsizei height;
171 } scissor;
153 }; 172 };
154 std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> viewports; 173 std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
155
156 struct {
157 bool enabled; // GL_SCISSOR_TEST
158 GLint x;
159 GLint y;
160 GLsizei width;
161 GLsizei height;
162 } scissor;
163 174
164 struct { 175 struct {
165 float size; // GL_POINT_SIZE 176 float size; // GL_POINT_SIZE
166 } point; 177 } point;
167 178
179 struct {
180 bool point_enable;
181 bool line_enable;
182 bool fill_enable;
183 GLfloat units;
184 GLfloat factor;
185 GLfloat clamp;
186 } polygon_offset;
187
168 std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE 188 std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
169 189
170 OpenGLState(); 190 OpenGLState();
@@ -195,6 +215,7 @@ public:
195 OpenGLState& ResetBuffer(GLuint handle); 215 OpenGLState& ResetBuffer(GLuint handle);
196 OpenGLState& ResetVertexArray(GLuint handle); 216 OpenGLState& ResetVertexArray(GLuint handle);
197 OpenGLState& ResetFramebuffer(GLuint handle); 217 OpenGLState& ResetFramebuffer(GLuint handle);
218 void EmulateViewportWithScissor();
198 219
199private: 220private:
200 static OpenGLState cur_state; 221 static OpenGLState cur_state;
@@ -214,7 +235,7 @@ private:
214 void ApplyLogicOp() const; 235 void ApplyLogicOp() const;
215 void ApplyTextures() const; 236 void ApplyTextures() const;
216 void ApplySamplers() const; 237 void ApplySamplers() const;
217 void ApplyScissor() const; 238 void ApplyPolygonOffset() const;
218}; 239};
219 240
220} // namespace OpenGL 241} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 3ce2cc6d2..a8833c06e 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -180,6 +180,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
180 return GL_CLAMP_TO_BORDER; 180 return GL_CLAMP_TO_BORDER;
181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: 181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
182 return GL_MIRROR_CLAMP_TO_EDGE; 182 return GL_MIRROR_CLAMP_TO_EDGE;
183 case Tegra::Texture::WrapMode::MirrorOnceBorder:
184 if (GL_EXT_texture_mirror_clamp) {
185 return GL_MIRROR_CLAMP_TO_BORDER_EXT;
186 } else {
187 return GL_MIRROR_CLAMP_TO_EDGE;
188 }
183 } 189 }
184 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); 190 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
185 return GL_REPEAT; 191 return GL_REPEAT;
@@ -212,14 +218,19 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
212inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { 218inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
213 switch (equation) { 219 switch (equation) {
214 case Maxwell::Blend::Equation::Add: 220 case Maxwell::Blend::Equation::Add:
221 case Maxwell::Blend::Equation::AddGL:
215 return GL_FUNC_ADD; 222 return GL_FUNC_ADD;
216 case Maxwell::Blend::Equation::Subtract: 223 case Maxwell::Blend::Equation::Subtract:
224 case Maxwell::Blend::Equation::SubtractGL:
217 return GL_FUNC_SUBTRACT; 225 return GL_FUNC_SUBTRACT;
218 case Maxwell::Blend::Equation::ReverseSubtract: 226 case Maxwell::Blend::Equation::ReverseSubtract:
227 case Maxwell::Blend::Equation::ReverseSubtractGL:
219 return GL_FUNC_REVERSE_SUBTRACT; 228 return GL_FUNC_REVERSE_SUBTRACT;
220 case Maxwell::Blend::Equation::Min: 229 case Maxwell::Blend::Equation::Min:
230 case Maxwell::Blend::Equation::MinGL:
221 return GL_MIN; 231 return GL_MIN;
222 case Maxwell::Blend::Equation::Max: 232 case Maxwell::Blend::Equation::Max:
233 case Maxwell::Blend::Equation::MaxGL:
223 return GL_MAX; 234 return GL_MAX;
224 } 235 }
225 LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); 236 LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index ea38da932..4fd0d66c5 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -19,9 +19,9 @@
19#include "core/settings.h" 19#include "core/settings.h"
20#include "core/telemetry_session.h" 20#include "core/telemetry_session.h"
21#include "core/tracer/recorder.h" 21#include "core/tracer/recorder.h"
22#include "video_core/morton.h"
22#include "video_core/renderer_opengl/gl_rasterizer.h" 23#include "video_core/renderer_opengl/gl_rasterizer.h"
23#include "video_core/renderer_opengl/renderer_opengl.h" 24#include "video_core/renderer_opengl/renderer_opengl.h"
24#include "video_core/utils.h"
25 25
26namespace OpenGL { 26namespace OpenGL {
27 27
@@ -304,6 +304,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
304 gl_framebuffer_data.resize(texture.width * texture.height * 4); 304 gl_framebuffer_data.resize(texture.width * texture.height * 4);
305 break; 305 break;
306 default: 306 default:
307 internal_format = GL_RGBA;
308 texture.gl_format = GL_RGBA;
309 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
310 gl_framebuffer_data.resize(texture.width * texture.height * 4);
311 LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer pixel format: {}",
312 static_cast<u32>(framebuffer.pixel_format));
307 UNREACHABLE(); 313 UNREACHABLE();
308 } 314 }
309 315
@@ -484,7 +490,7 @@ bool RendererOpenGL::Init() {
484 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model); 490 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model);
485 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version); 491 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version);
486 492
487 if (!GLAD_GL_VERSION_3_3) { 493 if (!GLAD_GL_VERSION_4_3) {
488 return false; 494 return false;
489 } 495 }
490 496
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 19f30b1b5..7eabd34f1 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -37,8 +37,14 @@ struct alignas(64) SwizzleTable {
37 std::array<std::array<u16, M>, N> values{}; 37 std::array<std::array<u16, M>, N> values{};
38}; 38};
39 39
40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); 40constexpr u32 gob_size_x = 64;
41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); 41constexpr u32 gob_size_y = 8;
42constexpr u32 gob_size_z = 1;
43constexpr u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
44constexpr u32 fast_swizzle_align = 16;
45
46constexpr auto legacy_swizzle_table = SwizzleTable<gob_size_y, gob_size_x, gob_size_z>();
47constexpr auto fast_swizzle_table = SwizzleTable<gob_size_y, 4, fast_swizzle_align>();
42 48
43/** 49/**
44 * This function manages ALL the GOBs(Group of Bytes) Inside a single block. 50 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
@@ -52,10 +58,7 @@ void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, con
52 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) { 58 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
53 std::array<u8*, 2> data_ptrs; 59 std::array<u8*, 2> data_ptrs;
54 u32 z_address = tile_offset; 60 u32 z_address = tile_offset;
55 const u32 gob_size_x = 64; 61
56 const u32 gob_size_y = 8;
57 const u32 gob_size_z = 1;
58 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
59 for (u32 z = z_start; z < z_end; z++) { 62 for (u32 z = z_start; z < z_end; z++) {
60 u32 y_address = z_address; 63 u32 y_address = z_address;
61 u32 pixel_base = layer_z * z + y_start * stride_x; 64 u32 pixel_base = layer_z * z + y_start * stride_x;
@@ -90,23 +93,19 @@ void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const
90 u32 z_address = tile_offset; 93 u32 z_address = tile_offset;
91 const u32 x_startb = x_start * bytes_per_pixel; 94 const u32 x_startb = x_start * bytes_per_pixel;
92 const u32 x_endb = x_end * bytes_per_pixel; 95 const u32 x_endb = x_end * bytes_per_pixel;
93 constexpr u32 copy_size = 16; 96
94 constexpr u32 gob_size_x = 64;
95 constexpr u32 gob_size_y = 8;
96 constexpr u32 gob_size_z = 1;
97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
98 for (u32 z = z_start; z < z_end; z++) { 97 for (u32 z = z_start; z < z_end; z++) {
99 u32 y_address = z_address; 98 u32 y_address = z_address;
100 u32 pixel_base = layer_z * z + y_start * stride_x; 99 u32 pixel_base = layer_z * z + y_start * stride_x;
101 for (u32 y = y_start; y < y_end; y++) { 100 for (u32 y = y_start; y < y_end; y++) {
102 const auto& table = fast_swizzle_table[y % gob_size_y]; 101 const auto& table = fast_swizzle_table[y % gob_size_y];
103 for (u32 xb = x_startb; xb < x_endb; xb += copy_size) { 102 for (u32 xb = x_startb; xb < x_endb; xb += fast_swizzle_align) {
104 const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]}; 103 const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]};
105 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel; 104 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
106 const u32 pixel_index{out_x + pixel_base}; 105 const u32 pixel_index{out_x + pixel_base};
107 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 106 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
108 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 107 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
109 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); 108 std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align);
110 } 109 }
111 pixel_base += stride_x; 110 pixel_base += stride_x;
112 if ((y + 1) % gob_size_y == 0) 111 if ((y + 1) % gob_size_y == 0)
@@ -132,17 +131,15 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); }; 131 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
133 const u32 stride_x = width * out_bytes_per_pixel; 132 const u32 stride_x = width * out_bytes_per_pixel;
134 const u32 layer_z = height * stride_x; 133 const u32 layer_z = height * stride_x;
135 constexpr u32 gob_x_bytes = 64; 134 const u32 gob_elements_x = gob_size_x / bytes_per_pixel;
136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel; 135 constexpr u32 gob_elements_y = gob_size_y;
137 constexpr u32 gob_elements_y = 8; 136 constexpr u32 gob_elements_z = gob_size_z;
138 constexpr u32 gob_elements_z = 1;
139 const u32 block_x_elements = gob_elements_x; 137 const u32 block_x_elements = gob_elements_x;
140 const u32 block_y_elements = gob_elements_y * block_height; 138 const u32 block_y_elements = gob_elements_y * block_height;
141 const u32 block_z_elements = gob_elements_z * block_depth; 139 const u32 block_z_elements = gob_elements_z * block_depth;
142 const u32 blocks_on_x = div_ceil(width, block_x_elements); 140 const u32 blocks_on_x = div_ceil(width, block_x_elements);
143 const u32 blocks_on_y = div_ceil(height, block_y_elements); 141 const u32 blocks_on_y = div_ceil(height, block_y_elements);
144 const u32 blocks_on_z = div_ceil(depth, block_z_elements); 142 const u32 blocks_on_z = div_ceil(depth, block_z_elements);
145 constexpr u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
146 const u32 xy_block_size = gob_size * block_height; 143 const u32 xy_block_size = gob_size * block_height;
147 const u32 block_size = xy_block_size * block_depth; 144 const u32 block_size = xy_block_size * block_depth;
148 u32 tile_offset = 0; 145 u32 tile_offset = 0;
@@ -173,7 +170,7 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
173void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, 170void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
174 u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data, 171 u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
175 bool unswizzle, u32 block_height, u32 block_depth) { 172 bool unswizzle, u32 block_height, u32 block_depth) {
176 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { 173 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % fast_swizzle_align == 0) {
177 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth, 174 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
178 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth); 175 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
179 } else { 176 } else {
@@ -229,29 +226,38 @@ u32 BytesPerPixel(TextureFormat format) {
229 } 226 }
230} 227}
231 228
229void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
230 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
231 u32 block_depth) {
232 CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
233 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
234 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true,
235 block_height, block_depth);
236}
237
232std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 238std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
233 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 239 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
234 u32 block_height, u32 block_depth) { 240 u32 block_height, u32 block_depth) {
235 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); 241 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
236 CopySwizzledData((width + tile_size_x - 1) / tile_size_x, 242 UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
237 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel, 243 width, height, depth, block_height, block_depth);
238 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
239 block_height, block_depth);
240 return unswizzled_data; 244 return unswizzled_data;
241} 245}
242 246
243void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 247void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
244 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 248 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
245 u32 block_height) { 249 u32 block_height) {
246 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64}; 250 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
251 gob_size_x};
247 for (u32 line = 0; line < subrect_height; ++line) { 252 for (u32 line = 0; line < subrect_height; ++line) {
248 const u32 gob_address_y = 253 const u32 gob_address_y =
249 (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs + 254 (line / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
250 (line % (8 * block_height) / 8) * 512; 255 ((line % (gob_size_y * block_height)) / gob_size_y) * gob_size;
251 const auto& table = legacy_swizzle_table[line % 8]; 256 const auto& table = legacy_swizzle_table[line % gob_size_y];
252 for (u32 x = 0; x < subrect_width; ++x) { 257 for (u32 x = 0; x < subrect_width; ++x) {
253 const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height; 258 const u32 gob_address =
254 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64]; 259 gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
260 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x];
255 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel; 261 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
256 const VAddr dest_addr = swizzled_data + swizzled_offset; 262 const VAddr dest_addr = swizzled_data + swizzled_offset;
257 263
@@ -265,13 +271,13 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
265 u32 block_height, u32 offset_x, u32 offset_y) { 271 u32 block_height, u32 offset_x, u32 offset_y) {
266 for (u32 line = 0; line < subrect_height; ++line) { 272 for (u32 line = 0; line < subrect_height; ++line) {
267 const u32 y2 = line + offset_y; 273 const u32 y2 = line + offset_y;
268 const u32 gob_address_y = 274 const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
269 (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512; 275 ((y2 % (gob_size_y * block_height)) / gob_size_y) * gob_size;
270 const auto& table = legacy_swizzle_table[y2 % 8]; 276 const auto& table = legacy_swizzle_table[y2 % gob_size_y];
271 for (u32 x = 0; x < subrect_width; ++x) { 277 for (u32 x = 0; x < subrect_width; ++x) {
272 const u32 x2 = (x + offset_x) * bytes_per_pixel; 278 const u32 x2 = (x + offset_x) * bytes_per_pixel;
273 const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height; 279 const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
274 const u32 swizzled_offset = gob_address + table[x2 % 64]; 280 const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
275 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel; 281 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
276 const VAddr source_addr = swizzled_data + swizzled_offset; 282 const VAddr source_addr = swizzled_data + swizzled_offset;
277 283
@@ -325,12 +331,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
325std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 331std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
326 u32 block_height, u32 block_depth) { 332 u32 block_height, u32 block_depth) {
327 if (tiled) { 333 if (tiled) {
328 constexpr u32 gobs_in_x = 64; 334 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gob_size_x);
329 constexpr u32 gobs_in_y = 8; 335 const u32 aligned_height = Common::AlignUp(height, gob_size_y * block_height);
330 constexpr u32 gobs_in_z = 1; 336 const u32 aligned_depth = Common::AlignUp(depth, gob_size_z * block_depth);
331 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
332 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
333 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
334 return aligned_width * aligned_height * aligned_depth; 337 return aligned_width * aligned_height * aligned_depth;
335 } else { 338 } else {
336 return width * height * depth * bytes_per_pixel; 339 return width * height * depth * bytes_per_pixel;
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index ba065510b..f4ef7c73e 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -19,6 +19,13 @@ inline std::size_t GetGOBSize() {
19/** 19/**
20 * Unswizzles a swizzled texture without changing its format. 20 * Unswizzles a swizzled texture without changing its format.
21 */ 21 */
22void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight,
25 u32 block_depth = TICEntry::DefaultBlockHeight);
26/**
27 * Unswizzles a swizzled texture without changing its format.
28 */
22std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 29std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 30 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight, 31 u32 block_height = TICEntry::DefaultBlockHeight,
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index e199d019a..ffa08f5c1 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -190,6 +190,7 @@ struct TICEntry {
190 union { 190 union {
191 BitField<0, 4, u32> res_min_mip_level; 191 BitField<0, 4, u32> res_min_mip_level;
192 BitField<4, 4, u32> res_max_mip_level; 192 BitField<4, 4, u32> res_max_mip_level;
193 BitField<12, 12, u32> min_lod_clamp;
193 }; 194 };
194 195
195 GPUVAddr Address() const { 196 GPUVAddr Address() const {
@@ -284,13 +285,25 @@ struct TSCEntry {
284 BitField<6, 3, WrapMode> wrap_p; 285 BitField<6, 3, WrapMode> wrap_p;
285 BitField<9, 1, u32> depth_compare_enabled; 286 BitField<9, 1, u32> depth_compare_enabled;
286 BitField<10, 3, DepthCompareFunc> depth_compare_func; 287 BitField<10, 3, DepthCompareFunc> depth_compare_func;
288 BitField<13, 1, u32> srgb_conversion;
289 BitField<20, 3, u32> max_anisotropy;
287 }; 290 };
288 union { 291 union {
289 BitField<0, 2, TextureFilter> mag_filter; 292 BitField<0, 2, TextureFilter> mag_filter;
290 BitField<4, 2, TextureFilter> min_filter; 293 BitField<4, 2, TextureFilter> min_filter;
291 BitField<6, 2, TextureMipmapFilter> mip_filter; 294 BitField<6, 2, TextureMipmapFilter> mip_filter;
295 BitField<9, 1, u32> cubemap_interface_filtering;
296 BitField<12, 13, u32> mip_lod_bias;
297 };
298 union {
299 BitField<0, 12, u32> min_lod_clamp;
300 BitField<12, 12, u32> max_lod_clamp;
301 BitField<24, 8, u32> srgb_border_color_r;
302 };
303 union {
304 BitField<12, 8, u32> srgb_border_color_g;
305 BitField<20, 8, u32> srgb_border_color_b;
292 }; 306 };
293 INSERT_PADDING_BYTES(8);
294 float border_color_r; 307 float border_color_r;
295 float border_color_g; 308 float border_color_g;
296 float border_color_b; 309 float border_color_b;
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
deleted file mode 100644
index e0a14d48f..000000000
--- a/src/video_core/utils.h
+++ /dev/null
@@ -1,164 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace VideoCore {
10
11// 8x8 Z-Order coordinate from 2D coordinates
12static inline u32 MortonInterleave(u32 x, u32 y) {
13 static const u32 xlut[] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15};
14 static const u32 ylut[] = {0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a};
15 return xlut[x % 8] + ylut[y % 8];
16}
17
18/**
19 * Calculates the offset of the position of the pixel in Morton order
20 */
21static inline u32 GetMortonOffset(u32 x, u32 y, u32 bytes_per_pixel) {
22 // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each
23 // of which is composed of four 2x2 subtiles each of which is composed of four texels.
24 // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g.
25 // texels are laid out in a 2x2 subtile like this:
26 // 2 3
27 // 0 1
28 //
29 // The full 8x8 tile has the texels arranged like this:
30 //
31 // 42 43 46 47 58 59 62 63
32 // 40 41 44 45 56 57 60 61
33 // 34 35 38 39 50 51 54 55
34 // 32 33 36 37 48 49 52 53
35 // 10 11 14 15 26 27 30 31
36 // 08 09 12 13 24 25 28 29
37 // 02 03 06 07 18 19 22 23
38 // 00 01 04 05 16 17 20 21
39 //
40 // This pattern is what's called Z-order curve, or Morton order.
41
42 const unsigned int block_height = 8;
43 const unsigned int coarse_x = x & ~7;
44
45 u32 i = VideoCore::MortonInterleave(x, y);
46
47 const unsigned int offset = coarse_x * block_height;
48
49 return (i + offset) * bytes_per_pixel;
50}
51
52static inline u32 MortonInterleave128(u32 x, u32 y) {
53 // 128x128 Z-Order coordinate from 2D coordinates
54 static constexpr u32 xlut[] = {
55 0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042,
56 0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809,
57 0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000,
58 0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043,
59 0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a,
60 0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001,
61 0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048,
62 0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b,
63 0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002,
64 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049,
65 0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840,
66 0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003,
67 0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a,
68 0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841,
69 0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008,
70 0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b,
71 0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842,
72 0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009,
73 0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800,
74 0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843,
75 0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a,
76 0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801,
77 0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848,
78 0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b,
79 0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802,
80 0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849,
81 0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040,
82 0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803,
83 0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a,
84 0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041,
85 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808,
86 0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b,
87 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042,
88 0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809,
89 0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b,
90 };
91 static constexpr u32 ylut[] = {
92 0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090,
93 0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124,
94 0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200,
95 0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294,
96 0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330,
97 0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404,
98 0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0,
99 0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534,
100 0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610,
101 0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4,
102 0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780,
103 0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014,
104 0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0,
105 0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184,
106 0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220,
107 0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4,
108 0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390,
109 0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424,
110 0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500,
111 0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594,
112 0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630,
113 0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704,
114 0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0,
115 0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034,
116 0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110,
117 0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4,
118 0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280,
119 0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314,
120 0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0,
121 0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484,
122 0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520,
123 0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4,
124 0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690,
125 0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724,
126 0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4,
127 };
128 return xlut[x % 128] + ylut[y % 128];
129}
130
131static inline u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) {
132 // Calculates the offset of the position of the pixel in Morton order
133 // Framebuffer images are split into 128x128 tiles.
134
135 const unsigned int block_height = 128;
136 const unsigned int coarse_x = x & ~127;
137
138 u32 i = MortonInterleave128(x, y);
139
140 const unsigned int offset = coarse_x * block_height;
141
142 return (i + offset) * bytes_per_pixel;
143}
144
145static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel,
146 u32 gl_bytes_per_pixel, u8* morton_data, u8* gl_data,
147 bool morton_to_gl) {
148 u8* data_ptrs[2];
149 for (unsigned y = 0; y < height; ++y) {
150 for (unsigned x = 0; x < width; ++x) {
151 const u32 coarse_y = y & ~127;
152 u32 morton_offset =
153 GetMortonOffset128(x, y, bytes_per_pixel) + coarse_y * width * bytes_per_pixel;
154 u32 gl_pixel_index = (x + y * width) * gl_bytes_per_pixel;
155
156 data_ptrs[morton_to_gl] = morton_data + morton_offset;
157 data_ptrs[!morton_to_gl] = &gl_data[gl_pixel_index];
158
159 memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
160 }
161 }
162}
163
164} // namespace VideoCore
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index f9ca2948e..cfca8f4a8 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -7,6 +7,8 @@ add_executable(yuzu
7 Info.plist 7 Info.plist
8 about_dialog.cpp 8 about_dialog.cpp
9 about_dialog.h 9 about_dialog.h
10 applets/software_keyboard.cpp
11 applets/software_keyboard.h
10 bootmanager.cpp 12 bootmanager.cpp
11 bootmanager.h 13 bootmanager.h
12 compatibility_list.cpp 14 compatibility_list.cpp
@@ -27,8 +29,14 @@ add_executable(yuzu
27 configuration/configure_graphics.h 29 configuration/configure_graphics.h
28 configuration/configure_input.cpp 30 configuration/configure_input.cpp
29 configuration/configure_input.h 31 configuration/configure_input.h
32 configuration/configure_input_player.cpp
33 configuration/configure_input_player.h
34 configuration/configure_mouse_advanced.cpp
35 configuration/configure_mouse_advanced.h
30 configuration/configure_system.cpp 36 configuration/configure_system.cpp
31 configuration/configure_system.h 37 configuration/configure_system.h
38 configuration/configure_touchscreen_advanced.cpp
39 configuration/configure_touchscreen_advanced.h
32 configuration/configure_web.cpp 40 configuration/configure_web.cpp
33 configuration/configure_web.h 41 configuration/configure_web.h
34 debugger/graphics/graphics_breakpoint_observer.cpp 42 debugger/graphics/graphics_breakpoint_observer.cpp
@@ -76,7 +84,10 @@ set(UIS
76 configuration/configure_general.ui 84 configuration/configure_general.ui
77 configuration/configure_graphics.ui 85 configuration/configure_graphics.ui
78 configuration/configure_input.ui 86 configuration/configure_input.ui
87 configuration/configure_input_player.ui
88 configuration/configure_mouse_advanced.ui
79 configuration/configure_system.ui 89 configuration/configure_system.ui
90 configuration/configure_touchscreen_advanced.ui
80 configuration/configure_web.ui 91 configuration/configure_web.ui
81 hotkeys.ui 92 hotkeys.ui
82 main.ui 93 main.ui
diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp
new file mode 100644
index 000000000..8a26fdff1
--- /dev/null
+++ b/src/yuzu/applets/software_keyboard.cpp
@@ -0,0 +1,152 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <mutex>
7#include <QDialogButtonBox>
8#include <QFont>
9#include <QLabel>
10#include <QLineEdit>
11#include <QVBoxLayout>
12#include "core/hle/lock.h"
13#include "yuzu/applets/software_keyboard.h"
14#include "yuzu/main.h"
15
16QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator(
17 Core::Frontend::SoftwareKeyboardParameters parameters)
18 : parameters(std::move(parameters)) {}
19
20QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const {
21 if (input.size() > parameters.max_length)
22 return Invalid;
23 if (parameters.disable_space && input.contains(' '))
24 return Invalid;
25 if (parameters.disable_address && input.contains('@'))
26 return Invalid;
27 if (parameters.disable_percent && input.contains('%'))
28 return Invalid;
29 if (parameters.disable_slash && (input.contains('/') || input.contains('\\')))
30 return Invalid;
31 if (parameters.disable_number &&
32 std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) {
33 return Invalid;
34 }
35
36 if (parameters.disable_download_code &&
37 std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) {
38 return Invalid;
39 }
40
41 return Acceptable;
42}
43
44QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
45 QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_)
46 : QDialog(parent), parameters(std::move(parameters_)) {
47 layout = new QVBoxLayout;
48
49 header_label = new QLabel(QString::fromStdU16String(parameters.header_text));
50 header_label->setFont({header_label->font().family(), 11, QFont::Bold});
51 if (header_label->text().isEmpty())
52 header_label->setText(tr("Enter text:"));
53
54 sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text));
55 sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(),
56 sub_label->font().weight(), true});
57 sub_label->setHidden(parameters.sub_text.empty());
58
59 guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text));
60 guide_label->setHidden(parameters.guide_text.empty());
61
62 length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length));
63 length_label->setAlignment(Qt::AlignRight);
64 length_label->setFont({length_label->font().family(), 8});
65
66 line_edit = new QLineEdit;
67 line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters));
68 line_edit->setMaxLength(static_cast<int>(parameters.max_length));
69 line_edit->setText(QString::fromStdU16String(parameters.initial_text));
70 line_edit->setCursorPosition(
71 parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size()));
72 line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal);
73
74 connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) {
75 length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length));
76 });
77
78 buttons = new QDialogButtonBox;
79 buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
80 buttons->addButton(parameters.submit_text.empty()
81 ? tr("OK")
82 : QString::fromStdU16String(parameters.submit_text),
83 QDialogButtonBox::AcceptRole);
84
85 connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept);
86 connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject);
87 layout->addWidget(header_label);
88 layout->addWidget(sub_label);
89 layout->addWidget(guide_label);
90 layout->addWidget(length_label);
91 layout->addWidget(line_edit);
92 layout->addWidget(buttons);
93 setLayout(layout);
94 setWindowTitle(tr("Software Keyboard"));
95}
96
97QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
98
99void QtSoftwareKeyboardDialog::accept() {
100 ok = true;
101 text = line_edit->text().toStdU16String();
102 QDialog::accept();
103}
104
105void QtSoftwareKeyboardDialog::reject() {
106 ok = false;
107 text.clear();
108 QDialog::reject();
109}
110
111std::u16string QtSoftwareKeyboardDialog::GetText() const {
112 return text;
113}
114
115bool QtSoftwareKeyboardDialog::GetStatus() const {
116 return ok;
117}
118
119QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
120 connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window,
121 &GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection);
122 connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window,
123 &GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection);
124 connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this,
125 &QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection);
126}
127
128QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
129
130void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out,
131 Core::Frontend::SoftwareKeyboardParameters parameters) const {
132 text_output = std::move(out);
133 emit MainWindowGetText(parameters);
134}
135
136void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
137 std::function<void()> finished_check) const {
138 this->finished_check = std::move(finished_check);
139 emit MainWindowTextCheckDialog(error_message);
140}
141
142void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
143 // Acquire the HLE mutex
144 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
145 text_output(text);
146}
147
148void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
149 // Acquire the HLE mutex
150 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
151 finished_check();
152}
diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h
new file mode 100644
index 000000000..c63720ba4
--- /dev/null
+++ b/src/yuzu/applets/software_keyboard.h
@@ -0,0 +1,79 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QDialog>
8#include <QValidator>
9#include "common/assert.h"
10#include "core/frontend/applets/software_keyboard.h"
11
12class GMainWindow;
13class QDialogButtonBox;
14class QLabel;
15class QLineEdit;
16class QVBoxLayout;
17class QtSoftwareKeyboard;
18
19class QtSoftwareKeyboardValidator final : public QValidator {
20public:
21 explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters);
22 State validate(QString& input, int& pos) const override;
23
24private:
25 Core::Frontend::SoftwareKeyboardParameters parameters;
26};
27
28class QtSoftwareKeyboardDialog final : public QDialog {
29 Q_OBJECT
30
31public:
32 QtSoftwareKeyboardDialog(QWidget* parent,
33 Core::Frontend::SoftwareKeyboardParameters parameters);
34 ~QtSoftwareKeyboardDialog() override;
35
36 void accept() override;
37 void reject() override;
38
39 std::u16string GetText() const;
40 bool GetStatus() const;
41
42private:
43 bool ok = false;
44 std::u16string text;
45
46 QDialogButtonBox* buttons;
47 QLabel* header_label;
48 QLabel* sub_label;
49 QLabel* guide_label;
50 QLabel* length_label;
51 QLineEdit* line_edit;
52 QVBoxLayout* layout;
53
54 Core::Frontend::SoftwareKeyboardParameters parameters;
55};
56
57class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
58 Q_OBJECT
59
60public:
61 explicit QtSoftwareKeyboard(GMainWindow& parent);
62 ~QtSoftwareKeyboard() override;
63
64 void RequestText(std::function<void(std::optional<std::u16string>)> out,
65 Core::Frontend::SoftwareKeyboardParameters parameters) const override;
66 void SendTextCheckDialog(std::u16string error_message,
67 std::function<void()> finished_check) const override;
68
69signals:
70 void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const;
71 void MainWindowTextCheckDialog(std::u16string error_message) const;
72
73private:
74 void MainWindowFinishedText(std::optional<std::u16string> text);
75 void MainWindowFinishedCheckDialog();
76
77 mutable std::function<void(std::optional<std::u16string>)> text_output;
78 mutable std::function<void()> finished_check;
79};
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 39eef8858..384e17921 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -310,7 +310,7 @@ void GRenderWindow::InitRenderTarget() {
310 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, 310 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
311 // WA_DontShowOnScreen, WA_DeleteOnClose 311 // WA_DontShowOnScreen, WA_DeleteOnClose
312 QGLFormat fmt; 312 QGLFormat fmt;
313 fmt.setVersion(3, 3); 313 fmt.setVersion(4, 3);
314 fmt.setProfile(QGLFormat::CoreProfile); 314 fmt.setProfile(QGLFormat::CoreProfile);
315 fmt.setSwapInterval(false); 315 fmt.setSwapInterval(false);
316 316
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index be69fb831..83ebbd1fe 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -5,6 +5,7 @@
5#include <QSettings> 5#include <QSettings>
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "core/hle/service/acc/profile_manager.h" 7#include "core/hle/service/acc/profile_manager.h"
8#include "core/hle/service/hid/controllers/npad.h"
8#include "input_common/main.h" 9#include "input_common/main.h"
9#include "yuzu/configuration/config.h" 10#include "yuzu/configuration/config.h"
10#include "yuzu/ui_settings.h" 11#include "yuzu/ui_settings.h"
@@ -47,40 +48,313 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
47 }, 48 },
48}}; 49}};
49 50
50void Config::ReadValues() { 51const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
51 qt_config->beginGroup("Controls"); 52 {
53 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
54};
55
56const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = {
57 0,
58 0,
59 0,
60 0,
61 Qt::Key_A,
62 Qt::Key_B,
63 Qt::Key_C,
64 Qt::Key_D,
65 Qt::Key_E,
66 Qt::Key_F,
67 Qt::Key_G,
68 Qt::Key_H,
69 Qt::Key_I,
70 Qt::Key_J,
71 Qt::Key_K,
72 Qt::Key_L,
73 Qt::Key_M,
74 Qt::Key_N,
75 Qt::Key_O,
76 Qt::Key_P,
77 Qt::Key_Q,
78 Qt::Key_R,
79 Qt::Key_S,
80 Qt::Key_T,
81 Qt::Key_U,
82 Qt::Key_V,
83 Qt::Key_W,
84 Qt::Key_X,
85 Qt::Key_Y,
86 Qt::Key_Z,
87 Qt::Key_1,
88 Qt::Key_2,
89 Qt::Key_3,
90 Qt::Key_4,
91 Qt::Key_5,
92 Qt::Key_6,
93 Qt::Key_7,
94 Qt::Key_8,
95 Qt::Key_9,
96 Qt::Key_0,
97 Qt::Key_Enter,
98 Qt::Key_Escape,
99 Qt::Key_Backspace,
100 Qt::Key_Tab,
101 Qt::Key_Space,
102 Qt::Key_Minus,
103 Qt::Key_Equal,
104 Qt::Key_BracketLeft,
105 Qt::Key_BracketRight,
106 Qt::Key_Backslash,
107 Qt::Key_Dead_Tilde,
108 Qt::Key_Semicolon,
109 Qt::Key_Apostrophe,
110 Qt::Key_Dead_Grave,
111 Qt::Key_Comma,
112 Qt::Key_Period,
113 Qt::Key_Slash,
114 Qt::Key_CapsLock,
115
116 Qt::Key_F1,
117 Qt::Key_F2,
118 Qt::Key_F3,
119 Qt::Key_F4,
120 Qt::Key_F5,
121 Qt::Key_F6,
122 Qt::Key_F7,
123 Qt::Key_F8,
124 Qt::Key_F9,
125 Qt::Key_F10,
126 Qt::Key_F11,
127 Qt::Key_F12,
128
129 Qt::Key_SysReq,
130 Qt::Key_ScrollLock,
131 Qt::Key_Pause,
132 Qt::Key_Insert,
133 Qt::Key_Home,
134 Qt::Key_PageUp,
135 Qt::Key_Delete,
136 Qt::Key_End,
137 Qt::Key_PageDown,
138 Qt::Key_Right,
139 Qt::Key_Left,
140 Qt::Key_Down,
141 Qt::Key_Up,
142
143 Qt::Key_NumLock,
144 Qt::Key_Slash,
145 Qt::Key_Asterisk,
146 Qt::Key_Minus,
147 Qt::Key_Plus,
148 Qt::Key_Enter,
149 Qt::Key_1,
150 Qt::Key_2,
151 Qt::Key_3,
152 Qt::Key_4,
153 Qt::Key_5,
154 Qt::Key_6,
155 Qt::Key_7,
156 Qt::Key_8,
157 Qt::Key_9,
158 Qt::Key_0,
159 Qt::Key_Period,
160
161 0,
162 0,
163 Qt::Key_PowerOff,
164 Qt::Key_Equal,
165
166 Qt::Key_F13,
167 Qt::Key_F14,
168 Qt::Key_F15,
169 Qt::Key_F16,
170 Qt::Key_F17,
171 Qt::Key_F18,
172 Qt::Key_F19,
173 Qt::Key_F20,
174 Qt::Key_F21,
175 Qt::Key_F22,
176 Qt::Key_F23,
177 Qt::Key_F24,
178
179 Qt::Key_Open,
180 Qt::Key_Help,
181 Qt::Key_Menu,
182 0,
183 Qt::Key_Stop,
184 Qt::Key_AudioRepeat,
185 Qt::Key_Undo,
186 Qt::Key_Cut,
187 Qt::Key_Copy,
188 Qt::Key_Paste,
189 Qt::Key_Find,
190 Qt::Key_VolumeMute,
191 Qt::Key_VolumeUp,
192 Qt::Key_VolumeDown,
193 Qt::Key_CapsLock,
194 Qt::Key_NumLock,
195 Qt::Key_ScrollLock,
196 Qt::Key_Comma,
197
198 Qt::Key_ParenLeft,
199 Qt::Key_ParenRight,
200};
201
202const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = {
203 Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt, Qt::Key_ApplicationLeft,
204 Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
205};
206
207void Config::ReadPlayerValues() {
208 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
209 Settings::values.players[p].connected =
210 qt_config->value(QString("player_%1_connected").arg(p), false).toBool();
211
212 Settings::values.players[p].type = static_cast<Settings::ControllerType>(
213 qt_config
214 ->value(QString("player_%1_type").arg(p),
215 static_cast<u8>(Settings::ControllerType::DualJoycon))
216 .toUInt());
217
218 Settings::values.players[p].body_color_left =
219 qt_config
220 ->value(QString("player_%1_body_color_left").arg(p),
221 Settings::JOYCON_BODY_NEON_BLUE)
222 .toUInt();
223 Settings::values.players[p].body_color_right =
224 qt_config
225 ->value(QString("player_%1_body_color_right").arg(p),
226 Settings::JOYCON_BODY_NEON_RED)
227 .toUInt();
228 Settings::values.players[p].button_color_left =
229 qt_config
230 ->value(QString("player_%1_button_color_left").arg(p),
231 Settings::JOYCON_BUTTONS_NEON_BLUE)
232 .toUInt();
233 Settings::values.players[p].button_color_right =
234 qt_config
235 ->value(QString("player_%1_button_color_right").arg(p),
236 Settings::JOYCON_BUTTONS_NEON_RED)
237 .toUInt();
238
239 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
240 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
241 Settings::values.players[p].buttons[i] =
242 qt_config
243 ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i],
244 QString::fromStdString(default_param))
245 .toString()
246 .toStdString();
247 if (Settings::values.players[p].buttons[i].empty())
248 Settings::values.players[p].buttons[i] = default_param;
249 }
250
251 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
252 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
253 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
254 default_analogs[i][3], default_analogs[i][4], 0.5f);
255 Settings::values.players[p].analogs[i] =
256 qt_config
257 ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i],
258 QString::fromStdString(default_param))
259 .toString()
260 .toStdString();
261 if (Settings::values.players[p].analogs[i].empty())
262 Settings::values.players[p].analogs[i] = default_param;
263 }
264 }
265
266 std::stable_partition(
267 Settings::values.players.begin(),
268 Settings::values.players.begin() +
269 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
270 [](const auto& player) { return player.connected; });
271}
272
273void Config::ReadDebugValues() {
274 Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool();
52 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 275 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
53 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 276 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
54 Settings::values.buttons[i] = 277 Settings::values.debug_pad_buttons[i] =
55 qt_config 278 qt_config
56 ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param)) 279 ->value(QString("debug_pad_") + Settings::NativeButton::mapping[i],
280 QString::fromStdString(default_param))
57 .toString() 281 .toString()
58 .toStdString(); 282 .toStdString();
59 if (Settings::values.buttons[i].empty()) 283 if (Settings::values.debug_pad_buttons[i].empty())
60 Settings::values.buttons[i] = default_param; 284 Settings::values.debug_pad_buttons[i] = default_param;
61 } 285 }
62 286
63 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 287 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
64 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 288 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
65 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 289 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
66 default_analogs[i][3], default_analogs[i][4], 0.5f); 290 default_analogs[i][3], default_analogs[i][4], 0.5f);
67 Settings::values.analogs[i] = 291 Settings::values.debug_pad_analogs[i] =
68 qt_config 292 qt_config
69 ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param)) 293 ->value(QString("debug_pad_") + Settings::NativeAnalog::mapping[i],
294 QString::fromStdString(default_param))
70 .toString() 295 .toString()
71 .toStdString(); 296 .toStdString();
72 if (Settings::values.analogs[i].empty()) 297 if (Settings::values.debug_pad_analogs[i].empty())
73 Settings::values.analogs[i] = default_param; 298 Settings::values.debug_pad_analogs[i] = default_param;
74 } 299 }
300}
301
302void Config::ReadKeyboardValues() {
303 Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool();
304
305 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
306 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
307 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
308 Settings::values.keyboard_keys.begin() +
309 Settings::NativeKeyboard::LeftControlKey,
310 InputCommon::GenerateKeyboardParam);
311 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
312 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
313}
314
315void Config::ReadMouseValues() {
316 Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool();
317
318 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
319 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
320 Settings::values.mouse_buttons[i] =
321 qt_config
322 ->value(QString("mouse_") + Settings::NativeMouseButton::mapping[i],
323 QString::fromStdString(default_param))
324 .toString()
325 .toStdString();
326 if (Settings::values.mouse_buttons[i].empty())
327 Settings::values.mouse_buttons[i] = default_param;
328 }
329}
330
331void Config::ReadTouchscreenValues() {
332 Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool();
333 Settings::values.touchscreen.device =
334 qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString();
335
336 Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt();
337 Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt();
338 Settings::values.touchscreen.diameter_x =
339 qt_config->value("touchscreen_diameter_x", 15).toUInt();
340 Settings::values.touchscreen.diameter_y =
341 qt_config->value("touchscreen_diameter_y", 15).toUInt();
342 qt_config->endGroup();
343}
344
345void Config::ReadValues() {
346 qt_config->beginGroup("Controls");
347
348 ReadPlayerValues();
349 ReadDebugValues();
350 ReadKeyboardValues();
351 ReadMouseValues();
352 ReadTouchscreenValues();
75 353
76 Settings::values.motion_device = 354 Settings::values.motion_device =
77 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") 355 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
78 .toString() 356 .toString()
79 .toStdString(); 357 .toStdString();
80 Settings::values.touch_device =
81 qt_config->value("touch_device", "engine:emu_window").toString().toStdString();
82
83 qt_config->endGroup();
84 358
85 qt_config->beginGroup("Core"); 359 qt_config->beginGroup("Core");
86 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); 360 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
@@ -126,6 +400,11 @@ void Config::ReadValues() {
126 .toStdString()); 400 .toStdString());
127 qt_config->endGroup(); 401 qt_config->endGroup();
128 402
403 qt_config->beginGroup("Core");
404 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
405 Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool();
406 qt_config->endGroup();
407
129 qt_config->beginGroup("System"); 408 qt_config->beginGroup("System");
130 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 409 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
131 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); 410 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
@@ -153,6 +432,7 @@ void Config::ReadValues() {
153 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 432 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
154 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 433 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
155 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); 434 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
435 Settings::values.dump_exefs = qt_config->value("dump_exefs", false).toBool();
156 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); 436 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool();
157 qt_config->endGroup(); 437 qt_config->endGroup();
158 438
@@ -230,18 +510,81 @@ void Config::ReadValues() {
230 qt_config->endGroup(); 510 qt_config->endGroup();
231} 511}
232 512
233void Config::SaveValues() { 513void Config::SavePlayerValues() {
234 qt_config->beginGroup("Controls"); 514 for (int p = 0; p < Settings::values.players.size(); ++p) {
515 qt_config->setValue(QString("player_%1_connected").arg(p),
516 Settings::values.players[p].connected);
517 qt_config->setValue(QString("player_%1_type").arg(p),
518 static_cast<u8>(Settings::values.players[p].type));
519
520 qt_config->setValue(QString("player_%1_body_color_left").arg(p),
521 Settings::values.players[p].body_color_left);
522 qt_config->setValue(QString("player_%1_body_color_right").arg(p),
523 Settings::values.players[p].body_color_right);
524 qt_config->setValue(QString("player_%1_button_color_left").arg(p),
525 Settings::values.players[p].button_color_left);
526 qt_config->setValue(QString("player_%1_button_color_right").arg(p),
527 Settings::values.players[p].button_color_right);
528
529 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
530 qt_config->setValue(QString("player_%1_").arg(p) +
531 QString::fromStdString(Settings::NativeButton::mapping[i]),
532 QString::fromStdString(Settings::values.players[p].buttons[i]));
533 }
534 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
535 qt_config->setValue(QString("player_%1_").arg(p) +
536 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
537 QString::fromStdString(Settings::values.players[p].analogs[i]));
538 }
539 }
540}
541
542void Config::SaveDebugValues() {
543 qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled);
235 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 544 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
236 qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]), 545 qt_config->setValue(QString("debug_pad_") +
237 QString::fromStdString(Settings::values.buttons[i])); 546 QString::fromStdString(Settings::NativeButton::mapping[i]),
547 QString::fromStdString(Settings::values.debug_pad_buttons[i]));
238 } 548 }
239 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 549 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
240 qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]), 550 qt_config->setValue(QString("debug_pad_") +
241 QString::fromStdString(Settings::values.analogs[i])); 551 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
552 QString::fromStdString(Settings::values.debug_pad_analogs[i]));
242 } 553 }
554}
555
556void Config::SaveMouseValues() {
557 qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled);
558
559 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
560 qt_config->setValue(QString("mouse_") +
561 QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
562 QString::fromStdString(Settings::values.mouse_buttons[i]));
563 }
564}
565
566void Config::SaveTouchscreenValues() {
567 qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled);
568 qt_config->setValue("touchscreen_device",
569 QString::fromStdString(Settings::values.touchscreen.device));
570
571 qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger);
572 qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle);
573 qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x);
574 qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y);
575}
576
577void Config::SaveValues() {
578 qt_config->beginGroup("Controls");
579
580 SavePlayerValues();
581 SaveDebugValues();
582 SaveMouseValues();
583 SaveTouchscreenValues();
584
243 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); 585 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device));
244 qt_config->setValue("touch_device", QString::fromStdString(Settings::values.touch_device)); 586 qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled);
587
245 qt_config->endGroup(); 588 qt_config->endGroup();
246 589
247 qt_config->beginGroup("Core"); 590 qt_config->beginGroup("Core");
@@ -280,7 +623,6 @@ void Config::SaveValues() {
280 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 623 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
281 qt_config->setValue("enable_nfc", Settings::values.enable_nfc); 624 qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
282 qt_config->setValue("current_user", Settings::values.current_user); 625 qt_config->setValue("current_user", Settings::values.current_user);
283
284 qt_config->setValue("language_index", Settings::values.language_index); 626 qt_config->setValue("language_index", Settings::values.language_index);
285 627
286 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); 628 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value());
@@ -297,6 +639,7 @@ void Config::SaveValues() {
297 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 639 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
298 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 640 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
299 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); 641 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
642 qt_config->setValue("dump_exefs", Settings::values.dump_exefs);
300 qt_config->setValue("dump_nso", Settings::values.dump_nso); 643 qt_config->setValue("dump_nso", Settings::values.dump_nso);
301 qt_config->endGroup(); 644 qt_config->endGroup();
302 645
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 9c99c1b75..a1c27bbf9 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -22,10 +22,24 @@ public:
22 22
23 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 23 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
24 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 24 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
25 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
26 default_mouse_buttons;
27 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
28 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
25 29
26private: 30private:
27 void ReadValues(); 31 void ReadValues();
32 void ReadPlayerValues();
33 void ReadDebugValues();
34 void ReadKeyboardValues();
35 void ReadMouseValues();
36 void ReadTouchscreenValues();
37
28 void SaveValues(); 38 void SaveValues();
39 void SavePlayerValues();
40 void SaveDebugValues();
41 void SaveMouseValues();
42 void SaveTouchscreenValues();
29 43
30 std::unique_ptr<QSettings> qt_config; 44 std::unique_ptr<QSettings> qt_config;
31 std::string qt_config_loc; 45 std::string qt_config_loc;
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index fd5876b41..aa7de7b54 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -34,6 +34,7 @@ void ConfigureDebug::setConfiguration() {
34 ui->toggle_console->setChecked(UISettings::values.show_console); 34 ui->toggle_console->setChecked(UISettings::values.show_console);
35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); 35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->dump_exefs->setChecked(Settings::values.dump_exefs);
37 ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); 38 ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
38} 39}
39 40
@@ -43,6 +44,7 @@ void ConfigureDebug::applyConfiguration() {
43 UISettings::values.show_console = ui->toggle_console->isChecked(); 44 UISettings::values.show_console = ui->toggle_console->isChecked();
44 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 45 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
45 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 46 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
47 Settings::values.dump_exefs = ui->dump_exefs->isChecked();
46 Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); 48 Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
47 Debugger::ToggleConsole(); 49 Debugger::ToggleConsole();
48 Log::Filter filter; 50 Log::Filter filter;
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 9c5b702f8..758a92335 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -145,6 +145,16 @@
145 </property> 145 </property>
146 </widget> 146 </widget>
147 </item> 147 </item>
148 <item>
149 <widget class="QCheckBox" name="dump_exefs">
150 <property name="whatsThis">
151 <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
152 </property>
153 <property name="text">
154 <string>Dump ExeFS</string>
155 </property>
156 </widget>
157 </item>
148 </layout> 158 </layout>
149 </widget> 159 </widget>
150 </item> 160 </item>
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
index 639d5df0f..ae8cac243 100644
--- a/src/yuzu/configuration/configure_gamelist.cpp
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -36,6 +36,16 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
36 InitializeRowComboBoxes(); 36 InitializeRowComboBoxes();
37 37
38 this->setConfiguration(); 38 this->setConfiguration();
39
40 // Force game list reload if any of the relevant settings are changed.
41 connect(ui->show_unknown, &QCheckBox::stateChanged, this,
42 &ConfigureGameList::RequestGameListUpdate);
43 connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
44 &ConfigureGameList::RequestGameListUpdate);
45 connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
46 &ConfigureGameList::RequestGameListUpdate);
47 connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
48 &ConfigureGameList::RequestGameListUpdate);
39} 49}
40 50
41ConfigureGameList::~ConfigureGameList() = default; 51ConfigureGameList::~ConfigureGameList() = default;
@@ -49,6 +59,10 @@ void ConfigureGameList::applyConfiguration() {
49 Settings::Apply(); 59 Settings::Apply();
50} 60}
51 61
62void ConfigureGameList::RequestGameListUpdate() {
63 UISettings::values.is_game_list_reload_pending.exchange(true);
64}
65
52void ConfigureGameList::setConfiguration() { 66void ConfigureGameList::setConfiguration() {
53 ui->show_unknown->setChecked(UISettings::values.show_unknown); 67 ui->show_unknown->setChecked(UISettings::values.show_unknown);
54 ui->show_add_ons->setChecked(UISettings::values.show_add_ons); 68 ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h
index ff7406c60..bbf7e25f1 100644
--- a/src/yuzu/configuration/configure_gamelist.h
+++ b/src/yuzu/configuration/configure_gamelist.h
@@ -21,6 +21,8 @@ public:
21 void applyConfiguration(); 21 void applyConfiguration();
22 22
23private: 23private:
24 void RequestGameListUpdate();
25
24 void setConfiguration(); 26 void setConfiguration();
25 27
26 void changeEvent(QEvent*) override; 28 void changeEvent(QEvent*) override;
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 314f51203..92a441308 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -3,10 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/service/am/am.h"
7#include "core/hle/service/am/applet_ae.h"
8#include "core/hle/service/am/applet_oe.h"
9#include "core/hle/service/sm/sm.h"
10#include "core/settings.h" 6#include "core/settings.h"
11#include "ui_configure_general.h" 7#include "ui_configure_general.h"
12#include "yuzu/configuration/configure_general.h" 8#include "yuzu/configuration/configure_general.h"
@@ -23,6 +19,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
23 19
24 this->setConfiguration(); 20 this->setConfiguration();
25 21
22 connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
23 [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
24
26 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 25 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
27} 26}
28 27
@@ -33,7 +32,6 @@ void ConfigureGeneral::setConfiguration() {
33 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); 32 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
34 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 33 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
35 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); 34 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
36 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
37 ui->enable_nfc->setChecked(Settings::values.enable_nfc); 35 ui->enable_nfc->setChecked(Settings::values.enable_nfc);
38} 36}
39 37
@@ -41,33 +39,6 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
41 ui->widget->Populate(registry); 39 ui->widget->Populate(registry);
42} 40}
43 41
44void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) {
45 if (last_state == new_state) {
46 return;
47 }
48
49 Core::System& system{Core::System::GetInstance()};
50 if (!system.IsPoweredOn()) {
51 return;
52 }
53 Service::SM::ServiceManager& sm = system.ServiceManager();
54
55 // Message queue is shared between these services, we just need to signal an operation
56 // change to one and it will handle both automatically
57 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
58 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
59 bool has_signalled = false;
60
61 if (applet_oe != nullptr) {
62 applet_oe->GetMessageQueue()->OperationModeChanged();
63 has_signalled = true;
64 }
65
66 if (applet_ae != nullptr && !has_signalled) {
67 applet_ae->GetMessageQueue()->OperationModeChanged();
68 }
69}
70
71void ConfigureGeneral::applyConfiguration() { 42void ConfigureGeneral::applyConfiguration() {
72 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); 43 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
73 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 44 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
@@ -75,9 +46,5 @@ void ConfigureGeneral::applyConfiguration() {
75 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 46 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
76 47
77 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); 48 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
78 const bool pre_docked_mode = Settings::values.use_docked_mode;
79 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
80 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
81
82 Settings::values.enable_nfc = ui->enable_nfc->isChecked(); 49 Settings::values.enable_nfc = ui->enable_nfc->isChecked();
83} 50}
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 2210d48da..4770034cc 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -25,7 +25,6 @@ public:
25 25
26private: 26private:
27 void setConfiguration(); 27 void setConfiguration();
28 void OnDockedModeChanged(bool last_state, bool new_state);
29 28
30 std::unique_ptr<Ui::ConfigureGeneral> ui; 29 std::unique_ptr<Ui::ConfigureGeneral> ui;
31}; 30};
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index b82fffde8..bf37446c6 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>300</width> 9 <width>300</width>
10 <height>377</height> 10 <height>407</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -72,13 +72,6 @@
72 <item> 72 <item>
73 <layout class="QVBoxLayout" name="EmulationVerticalLayout"> 73 <layout class="QVBoxLayout" name="EmulationVerticalLayout">
74 <item> 74 <item>
75 <widget class="QCheckBox" name="use_docked_mode">
76 <property name="text">
77 <string>Enable docked mode</string>
78 </property>
79 </widget>
80 </item>
81 <item>
82 <widget class="QCheckBox" name="enable_nfc"> 75 <widget class="QCheckBox" name="enable_nfc">
83 <property name="text"> 76 <property name="text">
84 <string>Enable NFC</string> 77 <string>Enable NFC</string>
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 91fcad994..e278cdd05 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -23,31 +23,31 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_2"> 24 <layout class="QVBoxLayout" name="verticalLayout_2">
25 <item> 25 <item>
26 <layout class="QHBoxLayout" name="horizontalLayout_2"> 26 <layout class="QHBoxLayout" name="horizontalLayout_2">
27 <item> 27 <item>
28 <widget class="QCheckBox" name="toggle_frame_limit"> 28 <widget class="QCheckBox" name="toggle_frame_limit">
29 <property name="text"> 29 <property name="text">
30 <string>Limit Speed Percent</string> 30 <string>Limit Speed Percent</string>
31 </property> 31 </property>
32 </widget> 32 </widget>
33 </item> 33 </item>
34 <item> 34 <item>
35 <widget class="QSpinBox" name="frame_limit"> 35 <widget class="QSpinBox" name="frame_limit">
36 <property name="suffix"> 36 <property name="suffix">
37 <string>%</string> 37 <string>%</string>
38 </property> 38 </property>
39 <property name="minimum"> 39 <property name="minimum">
40 <number>1</number> 40 <number>1</number>
41 </property> 41 </property>
42 <property name="maximum"> 42 <property name="maximum">
43 <number>9999</number> 43 <number>9999</number>
44 </property> 44 </property>
45 <property name="value"> 45 <property name="value">
46 <number>100</number> 46 <number>100</number>
47 </property> 47 </property>
48 </widget> 48 </widget>
49 </item> 49 </item>
50 </layout> 50 </layout>
51 </item> 51 </item>
52 <item> 52 <item>
53 <widget class="QCheckBox" name="use_accurate_gpu_emulation"> 53 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
@@ -61,7 +61,7 @@
61 <item> 61 <item>
62 <widget class="QLabel" name="label"> 62 <widget class="QLabel" name="label">
63 <property name="text"> 63 <property name="text">
64 <string>Internal Resolution:(Currently does nothing.)</string> 64 <string>Internal Resolution</string>
65 </property> 65 </property>
66 </widget> 66 </widget>
67 </item> 67 </item>
@@ -96,27 +96,27 @@
96 </item> 96 </item>
97 </layout> 97 </layout>
98 </item> 98 </item>
99 <item> 99 <item>
100 <layout class="QHBoxLayout" name="horizontalLayout_6"> 100 <layout class="QHBoxLayout" name="horizontalLayout_6">
101 <item> 101 <item>
102 <widget class="QLabel" name="bg_label"> 102 <widget class="QLabel" name="bg_label">
103 <property name="text"> 103 <property name="text">
104 <string>Background Color:</string> 104 <string>Background Color:</string>
105 </property> 105 </property>
106 </widget> 106 </widget>
107 </item> 107 </item>
108 <item> 108 <item>
109 <widget class="QPushButton" name="bg_button"> 109 <widget class="QPushButton" name="bg_button">
110 <property name="maximumSize"> 110 <property name="maximumSize">
111 <size> 111 <size>
112 <width>40</width> 112 <width>40</width>
113 <height>16777215</height> 113 <height>16777215</height>
114 </size> 114 </size>
115 </property> 115 </property>
116 </widget> 116 </widget>
117 </item> 117 </item>
118 </layout> 118 </layout>
119 </item> 119 </item>
120 </layout> 120 </layout>
121 </widget> 121 </widget>
122 </item> 122 </item>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 42a7beac6..7ee572761 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -9,334 +9,202 @@
9#include <QMessageBox> 9#include <QMessageBox>
10#include <QTimer> 10#include <QTimer>
11#include "common/param_package.h" 11#include "common/param_package.h"
12#include "configuration/configure_touchscreen_advanced.h"
13#include "core/core.h"
14#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applet_ae.h"
16#include "core/hle/service/am/applet_oe.h"
17#include "core/hle/service/hid/controllers/npad.h"
18#include "core/hle/service/sm/sm.h"
12#include "input_common/main.h" 19#include "input_common/main.h"
20#include "ui_configure_input.h"
21#include "ui_configure_input_player.h"
22#include "ui_configure_mouse_advanced.h"
23#include "ui_configure_touchscreen_advanced.h"
13#include "yuzu/configuration/config.h" 24#include "yuzu/configuration/config.h"
14#include "yuzu/configuration/configure_input.h" 25#include "yuzu/configuration/configure_input.h"
26#include "yuzu/configuration/configure_input_player.h"
27#include "yuzu/configuration/configure_mouse_advanced.h"
15 28
16const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM> 29ConfigureInput::ConfigureInput(QWidget* parent)
17 ConfigureInput::analog_sub_buttons{{ 30 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
18 "up", 31 ui->setupUi(this);
19 "down",
20 "left",
21 "right",
22 "modifier",
23 }};
24
25static QString getKeyName(int key_code) {
26 switch (key_code) {
27 case Qt::Key_Shift:
28 return QObject::tr("Shift");
29 case Qt::Key_Control:
30 return QObject::tr("Ctrl");
31 case Qt::Key_Alt:
32 return QObject::tr("Alt");
33 case Qt::Key_Meta:
34 return "";
35 default:
36 return QKeySequence(key_code).toString();
37 }
38}
39 32
40static void SetAnalogButton(const Common::ParamPackage& input_param, 33 players_controller = {
41 Common::ParamPackage& analog_param, const std::string& button_name) { 34 ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
42 if (analog_param.Get("engine", "") != "analog_from_button") { 35 ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
43 analog_param = { 36 };
44 {"engine", "analog_from_button"},
45 {"modifier_scale", "0.5"},
46 };
47 }
48 analog_param.Set(button_name, input_param.Serialize());
49}
50 37
51static QString ButtonToText(const Common::ParamPackage& param) { 38 players_configure = {
52 if (!param.Has("engine")) { 39 ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure,
53 return QObject::tr("[not set]"); 40 ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure,
54 } else if (param.Get("engine", "") == "keyboard") { 41 };
55 return getKeyName(param.Get("code", 0));
56 } else if (param.Get("engine", "") == "sdl") {
57 if (param.Has("hat")) {
58 return QString(QObject::tr("Hat %1 %2"))
59 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
60 }
61 if (param.Has("axis")) {
62 return QString(QObject::tr("Axis %1%2"))
63 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
64 }
65 if (param.Has("button")) {
66 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
67 }
68 return QString();
69 } else {
70 return QObject::tr("[unknown]");
71 }
72};
73
74static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
75 if (!param.Has("engine")) {
76 return QObject::tr("[not set]");
77 } else if (param.Get("engine", "") == "analog_from_button") {
78 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
79 } else if (param.Get("engine", "") == "sdl") {
80 if (dir == "modifier") {
81 return QString(QObject::tr("[unused]"));
82 }
83 42
84 if (dir == "left" || dir == "right") { 43 for (auto* controller_box : players_controller) {
85 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str()); 44 controller_box->addItems({"None", "Pro Controller", "Dual Joycons", "Single Right Joycon",
86 } else if (dir == "up" || dir == "down") { 45 "Single Left Joycon"});
87 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
88 }
89 return QString();
90 } else {
91 return QObject::tr("[unknown]");
92 } 46 }
93};
94 47
95ConfigureInput::ConfigureInput(QWidget* parent) 48 this->loadConfiguration();
96 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), 49 updateUIEnabled();
97 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
98 50
99 ui->setupUi(this); 51 connect(ui->restore_defaults_button, &QPushButton::pressed, this,
100 setFocusPolicy(Qt::ClickFocus); 52 &ConfigureInput::restoreDefaults);
101
102 button_map = {
103 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
104 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
105 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
106 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
107 ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
108 ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
109 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
110 };
111 53
112 analog_map_buttons = {{ 54 for (auto* enabled : players_controller)
113 { 55 connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
114 ui->buttonLStickUp, 56 &ConfigureInput::updateUIEnabled);
115 ui->buttonLStickDown, 57 connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
116 ui->buttonLStickLeft, 58 connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
117 ui->buttonLStickRight, 59 &ConfigureInput::updateUIEnabled);
118 ui->buttonLStickMod, 60 connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
119 }, 61 connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
120 { 62 connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
121 ui->buttonRStickUp, 63 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
122 ui->buttonRStickDown, 64 &ConfigureInput::updateUIEnabled);
123 ui->buttonRStickLeft,
124 ui->buttonRStickRight,
125 ui->buttonRStickMod,
126 },
127 }};
128
129 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
130
131 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
132 if (!button_map[button_id])
133 continue;
134 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
135 connect(button_map[button_id], &QPushButton::released, [=]() {
136 handleClick(
137 button_map[button_id],
138 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
139 InputCommon::Polling::DeviceType::Button);
140 });
141 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
142 [=](const QPoint& menu_location) {
143 QMenu context_menu;
144 context_menu.addAction(tr("Clear"), [&] {
145 buttons_param[button_id].Clear();
146 button_map[button_id]->setText(tr("[not set]"));
147 });
148 context_menu.addAction(tr("Restore Default"), [&] {
149 buttons_param[button_id] = Common::ParamPackage{
150 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
151 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
152 });
153 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
154 });
155 }
156 65
157 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 66 for (std::size_t i = 0; i < players_configure.size(); ++i) {
158 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 67 connect(players_configure[i], &QPushButton::pressed, this,
159 if (!analog_map_buttons[analog_id][sub_button_id]) 68 [this, i]() { CallConfigureDialog<ConfigureInputPlayer>(i, false); });
160 continue;
161 analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
162 Qt::CustomContextMenu);
163 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
164 handleClick(analog_map_buttons[analog_id][sub_button_id],
165 [=](const Common::ParamPackage& params) {
166 SetAnalogButton(params, analogs_param[analog_id],
167 analog_sub_buttons[sub_button_id]);
168 },
169 InputCommon::Polling::DeviceType::Button);
170 });
171 connect(analog_map_buttons[analog_id][sub_button_id],
172 &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
173 QMenu context_menu;
174 context_menu.addAction(tr("Clear"), [&] {
175 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
176 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
177 });
178 context_menu.addAction(tr("Restore Default"), [&] {
179 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
180 Config::default_analogs[analog_id][sub_button_id])};
181 SetAnalogButton(params, analogs_param[analog_id],
182 analog_sub_buttons[sub_button_id]);
183 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
184 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
185 });
186 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
187 menu_location));
188 });
189 }
190 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
191 QMessageBox::information(this, tr("Information"),
192 tr("After pressing OK, first move your joystick horizontally, "
193 "and then vertically."));
194 handleClick(
195 analog_map_stick[analog_id],
196 [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
197 InputCommon::Polling::DeviceType::Analog);
198 });
199 } 69 }
200 70
201 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); 71 connect(ui->handheld_configure, &QPushButton::pressed, this,
202 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); 72 [this]() { CallConfigureDialog<ConfigureInputPlayer>(8, false); });
203
204 timeout_timer->setSingleShot(true);
205 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
206 73
207 connect(poll_timer.get(), &QTimer::timeout, [this]() { 74 connect(ui->debug_configure, &QPushButton::pressed, this,
208 Common::ParamPackage params; 75 [this]() { CallConfigureDialog<ConfigureInputPlayer>(9, true); });
209 for (auto& poller : device_pollers) {
210 params = poller->GetNextInput();
211 if (params.Has("engine")) {
212 setPollingResult(params, false);
213 return;
214 }
215 }
216 });
217 76
218 this->loadConfiguration(); 77 connect(ui->mouse_advanced, &QPushButton::pressed, this,
78 [this]() { CallConfigureDialog<ConfigureMouseAdvanced>(); });
219 79
220 // TODO(wwylele): enable this when we actually emulate it 80 connect(ui->touchscreen_advanced, &QPushButton::pressed, this,
221 ui->buttonHome->setEnabled(false); 81 [this]() { CallConfigureDialog<ConfigureTouchscreenAdvanced>(); });
222} 82}
223 83
224void ConfigureInput::applyConfiguration() { 84template <typename Dialog, typename... Args>
225 std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(), 85void ConfigureInput::CallConfigureDialog(Args&&... args) {
226 [](const Common::ParamPackage& param) { return param.Serialize(); }); 86 this->applyConfiguration();
227 std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(), 87 Dialog dialog(this, std::forward<Args>(args)...);
228 [](const Common::ParamPackage& param) { return param.Serialize(); });
229}
230 88
231void ConfigureInput::loadConfiguration() { 89 const auto res = dialog.exec();
232 std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(), 90 if (res == QDialog::Accepted) {
233 buttons_param.begin(), 91 dialog.applyConfiguration();
234 [](const std::string& str) { return Common::ParamPackage(str); }); 92 }
235 std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(),
236 analogs_param.begin(),
237 [](const std::string& str) { return Common::ParamPackage(str); });
238 updateButtonLabels();
239} 93}
240 94
241void ConfigureInput::restoreDefaults() { 95void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
242 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 96 if (last_state == new_state) {
243 buttons_param[button_id] = Common::ParamPackage{ 97 return;
244 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
245 } 98 }
246 99
247 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 100 Core::System& system{Core::System::GetInstance()};
248 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 101 if (!system.IsPoweredOn()) {
249 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 102 return;
250 Config::default_analogs[analog_id][sub_button_id])};
251 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
252 }
253 } 103 }
254 updateButtonLabels(); 104 Service::SM::ServiceManager& sm = system.ServiceManager();
255}
256 105
257void ConfigureInput::ClearAll() { 106 // Message queue is shared between these services, we just need to signal an operation
258 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 107 // change to one and it will handle both automatically
259 if (button_map[button_id] && button_map[button_id]->isEnabled()) 108 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
260 buttons_param[button_id].Clear(); 109 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
261 } 110 bool has_signalled = false;
262 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
263 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
264 if (analog_map_buttons[analog_id][sub_button_id] &&
265 analog_map_buttons[analog_id][sub_button_id]->isEnabled())
266 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
267 }
268 }
269 updateButtonLabels();
270}
271 111
272void ConfigureInput::updateButtonLabels() { 112 if (applet_oe != nullptr) {
273 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { 113 applet_oe->GetMessageQueue()->OperationModeChanged();
274 button_map[button]->setText(ButtonToText(buttons_param[button])); 114 has_signalled = true;
275 } 115 }
276 116
277 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 117 if (applet_ae != nullptr && !has_signalled) {
278 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 118 applet_ae->GetMessageQueue()->OperationModeChanged();
279 if (analog_map_buttons[analog_id][sub_button_id]) {
280 analog_map_buttons[analog_id][sub_button_id]->setText(
281 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
282 }
283 }
284 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
285 } 119 }
286} 120}
287 121
288void ConfigureInput::handleClick(QPushButton* button, 122void ConfigureInput::applyConfiguration() {
289 std::function<void(const Common::ParamPackage&)> new_input_setter, 123 for (std::size_t i = 0; i < players_controller.size(); ++i) {
290 InputCommon::Polling::DeviceType type) { 124 const auto controller_type_index = players_controller[i]->currentIndex();
291 button->setText(tr("[press key]"));
292 button->setFocus();
293
294 input_setter = new_input_setter;
295
296 device_pollers = InputCommon::Polling::GetPollers(type);
297 125
298 // Keyboard keys can only be used as button devices 126 Settings::values.players[i].connected = controller_type_index != 0;
299 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
300 127
301 for (auto& poller : device_pollers) { 128 if (controller_type_index > 0) {
302 poller->Start(); 129 Settings::values.players[i].type =
130 static_cast<Settings::ControllerType>(controller_type_index - 1);
131 } else {
132 Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
133 }
303 } 134 }
304 135
305 grabKeyboard(); 136 const bool pre_docked_mode = Settings::values.use_docked_mode;
306 grabMouse(); 137 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
307 timeout_timer->start(5000); // Cancel after 5 seconds 138 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
308 poll_timer->start(200); // Check for new inputs every 200ms 139 Settings::values
140 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
141 .connected = ui->handheld_connected->isChecked();
142 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
143 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
144 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
145 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
309} 146}
310 147
311void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) { 148void ConfigureInput::updateUIEnabled() {
312 releaseKeyboard(); 149 bool hit_disabled = false;
313 releaseMouse(); 150 for (auto* player : players_controller) {
314 timeout_timer->stop(); 151 player->setDisabled(hit_disabled);
315 poll_timer->stop(); 152 if (hit_disabled)
316 for (auto& poller : device_pollers) { 153 player->setCurrentIndex(0);
317 poller->Stop(); 154 if (!hit_disabled && player->currentIndex() == 0)
155 hit_disabled = true;
318 } 156 }
319 157
320 if (!abort) { 158 for (std::size_t i = 0; i < players_controller.size(); ++i) {
321 (*input_setter)(params); 159 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
322 } 160 }
323 161
324 updateButtonLabels(); 162 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
325 input_setter = {}; 163 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
164 !ui->use_docked_mode->isChecked());
165 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
166 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
167 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
326} 168}
327 169
328void ConfigureInput::keyPressEvent(QKeyEvent* event) { 170void ConfigureInput::loadConfiguration() {
329 if (!input_setter || !event) 171 std::stable_partition(
330 return; 172 Settings::values.players.begin(),
173 Settings::values.players.begin() +
174 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
175 [](const auto& player) { return player.connected; });
176
177 for (std::size_t i = 0; i < players_controller.size(); ++i) {
178 const auto connected = Settings::values.players[i].connected;
179 players_controller[i]->setCurrentIndex(
180 connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
181 }
182
183 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
184 ui->handheld_connected->setChecked(
185 Settings::values
186 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
187 .connected);
188 ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
189 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
190 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
191 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
192
193 updateUIEnabled();
194}
331 195
332 if (event->key() != Qt::Key_Escape) { 196void ConfigureInput::restoreDefaults() {
333 if (want_keyboard_keys) { 197 players_controller[0]->setCurrentIndex(2);
334 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, 198
335 false); 199 for (std::size_t i = 1; i < players_controller.size(); ++i) {
336 } else { 200 players_controller[i]->setCurrentIndex(0);
337 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
338 return;
339 }
340 } 201 }
341 setPollingResult({}, true); 202
203 ui->use_docked_mode->setCheckState(Qt::Unchecked);
204 ui->handheld_connected->setCheckState(Qt::Unchecked);
205 ui->mouse_enabled->setCheckState(Qt::Unchecked);
206 ui->keyboard_enabled->setCheckState(Qt::Unchecked);
207 ui->debug_enabled->setCheckState(Qt::Unchecked);
208 ui->touchscreen_enabled->setCheckState(Qt::Checked);
209 updateUIEnabled();
342} 210}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 32c7183f9..29a8a03f8 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -18,6 +18,7 @@
18#include "core/settings.h" 18#include "core/settings.h"
19#include "input_common/main.h" 19#include "input_common/main.h"
20#include "ui_configure_input.h" 20#include "ui_configure_input.h"
21#include "yuzu/configuration/config.h"
21 22
22class QPushButton; 23class QPushButton;
23class QString; 24class QString;
@@ -37,57 +38,20 @@ public:
37 void applyConfiguration(); 38 void applyConfiguration();
38 39
39private: 40private:
40 std::unique_ptr<Ui::ConfigureInput> ui; 41 void updateUIEnabled();
41
42 std::unique_ptr<QTimer> timeout_timer;
43 std::unique_ptr<QTimer> poll_timer;
44
45 /// This will be the the setting function when an input is awaiting configuration.
46 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
47
48 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
49 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
50
51 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
52
53 /// Each button input is represented by a QPushButton.
54 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
55 42
56 /// A group of five QPushButtons represent one analog input. The buttons each represent up, 43 template <typename Dialog, typename... Args>
57 /// down, left, right, and modifier, respectively. 44 void CallConfigureDialog(Args&&... args);
58 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
59 analog_map_buttons;
60 45
61 /// Analog inputs are also represented each with a single button, used to configure with an 46 void OnDockedModeChanged(bool last_state, bool new_state);
62 /// actual analog stick
63 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
64
65 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
66
67 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
68
69 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
70 /// keyboard events are ignored.
71 bool want_keyboard_keys = false;
72 47
73 /// Load configuration settings. 48 /// Load configuration settings.
74 void loadConfiguration(); 49 void loadConfiguration();
75 /// Restore all buttons to their default values. 50 /// Restore all buttons to their default values.
76 void restoreDefaults(); 51 void restoreDefaults();
77 /// Clear all input configuration
78 void ClearAll();
79 52
80 /// Update UI to reflect current configuration. 53 std::unique_ptr<Ui::ConfigureInput> ui;
81 void updateButtonLabels();
82
83 /// Called when the button was pressed.
84 void handleClick(QPushButton* button,
85 std::function<void(const Common::ParamPackage&)> new_input_setter,
86 InputCommon::Polling::DeviceType type);
87
88 /// Finish polling and configure input using the input_setter
89 void setPollingResult(const Common::ParamPackage& params, bool abort);
90 54
91 /// Handle key press events. 55 std::array<QComboBox*, 8> players_controller;
92 void keyPressEvent(QKeyEvent* event) override; 56 std::array<QPushButton*, 8> players_configure;
93}; 57};
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index 8a019a693..dae8277bc 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>343</width> 9 <width>473</width>
10 <height>677</height> 10 <height>685</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -15,740 +15,470 @@
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item> 17 <item>
18 <layout class="QGridLayout" name="buttons"> 18 <layout class="QVBoxLayout" name="verticalLayout">
19 <item row="3" column="1"> 19 <item>
20 <widget class="QGroupBox" name="misc"> 20 <widget class="QGroupBox" name="gridGroupBox">
21 <property name="title"> 21 <property name="title">
22 <string>Misc.</string> 22 <string>Players</string>
23 </property>
24 <property name="flat">
25 <bool>false</bool>
26 </property>
27 <property name="checkable">
28 <bool>false</bool>
29 </property> 23 </property>
30 <layout class="QGridLayout" name="gridLayout_6"> 24 <layout class="QGridLayout" name="gridLayout">
31 <item row="0" column="0"> 25 <item row="1" column="2">
32 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> 26 <widget class="QComboBox" name="player1_combobox">
33 <item> 27 <property name="minimumSize">
34 <widget class="QLabel" name="labelMinus"> 28 <size>
35 <property name="text"> 29 <width>110</width>
36 <string>Minus:</string> 30 <height>0</height>
37 </property> 31 </size>
38 </widget> 32 </property>
39 </item> 33 </widget>
40 <item>
41 <widget class="QPushButton" name="buttonMinus">
42 <property name="text">
43 <string/>
44 </property>
45 </widget>
46 </item>
47 </layout>
48 </item> 34 </item>
49 <item row="0" column="1"> 35 <item row="1" column="3">
50 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout"> 36 <widget class="QPushButton" name="player1_configure">
51 <item> 37 <property name="text">
52 <widget class="QLabel" name="labelPlus"> 38 <string>Configure</string>
53 <property name="text"> 39 </property>
54 <string>Plus:</string> 40 </widget>
55 </property>
56 </widget>
57 </item>
58 <item>
59 <widget class="QPushButton" name="buttonPlus">
60 <property name="text">
61 <string/>
62 </property>
63 </widget>
64 </item>
65 </layout>
66 </item> 41 </item>
67 <item row="1" column="0"> 42 <item row="0" column="2">
68 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout"> 43 <widget class="QLabel" name="label">
69 <item> 44 <property name="text">
70 <widget class="QLabel" name="labelHome"> 45 <string>Controller Type</string>
71 <property name="text"> 46 </property>
72 <string>Home:</string> 47 <property name="alignment">
73 </property> 48 <set>Qt::AlignCenter</set>
74 </widget> 49 </property>
75 </item> 50 </widget>
76 <item>
77 <widget class="QPushButton" name="buttonHome">
78 <property name="text">
79 <string/>
80 </property>
81 </widget>
82 </item>
83 </layout>
84 </item> 51 </item>
85 <item row="1" column="1"> 52 <item row="2" column="2">
86 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout"> 53 <widget class="QComboBox" name="player2_combobox">
87 <item> 54 <property name="minimumSize">
88 <widget class="QLabel" name="labelScrCap"> 55 <size>
89 <property name="text"> 56 <width>110</width>
90 <string>Screen 57 <height>0</height>
91Capture:</string> 58 </size>
92 </property> 59 </property>
93 </widget> 60 </widget>
94 </item>
95 <item>
96 <widget class="QPushButton" name="buttonScreenshot">
97 <property name="text">
98 <string/>
99 </property>
100 </widget>
101 </item>
102 </layout>
103 </item> 61 </item>
104 <item row="2" column="1"> 62 <item row="3" column="2">
105 <spacer name="verticalSpacer"> 63 <widget class="QComboBox" name="player3_combobox">
64 <property name="minimumSize">
65 <size>
66 <width>110</width>
67 <height>0</height>
68 </size>
69 </property>
70 </widget>
71 </item>
72 <item row="4" column="2">
73 <widget class="QComboBox" name="player4_combobox">
74 <property name="minimumSize">
75 <size>
76 <width>110</width>
77 <height>0</height>
78 </size>
79 </property>
80 </widget>
81 </item>
82 <item row="5" column="2">
83 <widget class="QComboBox" name="player5_combobox">
84 <property name="minimumSize">
85 <size>
86 <width>110</width>
87 <height>0</height>
88 </size>
89 </property>
90 </widget>
91 </item>
92 <item row="6" column="2">
93 <widget class="QComboBox" name="player6_combobox">
94 <property name="minimumSize">
95 <size>
96 <width>110</width>
97 <height>0</height>
98 </size>
99 </property>
100 </widget>
101 </item>
102 <item row="7" column="2">
103 <widget class="QComboBox" name="player7_combobox">
104 <property name="minimumSize">
105 <size>
106 <width>110</width>
107 <height>0</height>
108 </size>
109 </property>
110 </widget>
111 </item>
112 <item row="8" column="2">
113 <widget class="QComboBox" name="player8_combobox">
114 <property name="minimumSize">
115 <size>
116 <width>110</width>
117 <height>0</height>
118 </size>
119 </property>
120 </widget>
121 </item>
122 <item row="2" column="3">
123 <widget class="QPushButton" name="player2_configure">
124 <property name="text">
125 <string>Configure</string>
126 </property>
127 </widget>
128 </item>
129 <item row="3" column="3">
130 <widget class="QPushButton" name="player3_configure">
131 <property name="text">
132 <string>Configure</string>
133 </property>
134 </widget>
135 </item>
136 <item row="4" column="3">
137 <widget class="QPushButton" name="player4_configure">
138 <property name="text">
139 <string>Configure</string>
140 </property>
141 </widget>
142 </item>
143 <item row="5" column="3">
144 <widget class="QPushButton" name="player5_configure">
145 <property name="text">
146 <string>Configure</string>
147 </property>
148 </widget>
149 </item>
150 <item row="6" column="3">
151 <widget class="QPushButton" name="player6_configure">
152 <property name="text">
153 <string>Configure</string>
154 </property>
155 </widget>
156 </item>
157 <item row="7" column="3">
158 <widget class="QPushButton" name="player7_configure">
159 <property name="text">
160 <string>Configure</string>
161 </property>
162 </widget>
163 </item>
164 <item row="8" column="3">
165 <widget class="QPushButton" name="player8_configure">
166 <property name="text">
167 <string>Configure</string>
168 </property>
169 </widget>
170 </item>
171 <item row="0" column="0">
172 <spacer name="horizontalSpacer">
106 <property name="orientation"> 173 <property name="orientation">
107 <enum>Qt::Vertical</enum> 174 <enum>Qt::Horizontal</enum>
108 </property> 175 </property>
109 <property name="sizeHint" stdset="0"> 176 <property name="sizeHint" stdset="0">
110 <size> 177 <size>
111 <width>20</width> 178 <width>40</width>
112 <height>40</height> 179 <height>20</height>
113 </size> 180 </size>
114 </property> 181 </property>
115 </spacer> 182 </spacer>
116 </item> 183 </item>
117 </layout> 184 <item row="0" column="4">
118 </widget> 185 <spacer name="horizontalSpacer_2">
119 </item> 186 <property name="orientation">
120 <item row="0" column="0"> 187 <enum>Qt::Horizontal</enum>
121 <widget class="QGroupBox" name="faceButtons"> 188 </property>
122 <property name="title"> 189 <property name="sizeHint" stdset="0">
123 <string>Face Buttons</string> 190 <size>
124 </property> 191 <width>40</width>
125 <property name="flat"> 192 <height>20</height>
126 <bool>false</bool> 193 </size>
127 </property> 194 </property>
128 <property name="checkable"> 195 </spacer>
129 <bool>false</bool>
130 </property>
131 <layout class="QGridLayout" name="gridLayout">
132 <item row="0" column="0">
133 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
134 <item>
135 <widget class="QLabel" name="labelA">
136 <property name="text">
137 <string>A:</string>
138 </property>
139 </widget>
140 </item>
141 <item>
142 <widget class="QPushButton" name="buttonA">
143 <property name="text">
144 <string/>
145 </property>
146 </widget>
147 </item>
148 </layout>
149 </item> 196 </item>
150 <item row="0" column="1"> 197 <item row="1" column="1">
151 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout"> 198 <widget class="QLabel" name="label_3">
152 <item> 199 <property name="minimumSize">
153 <widget class="QLabel" name="labelB"> 200 <size>
154 <property name="text"> 201 <width>55</width>
155 <string>B:</string> 202 <height>0</height>
156 </property> 203 </size>
157 </widget> 204 </property>
158 </item> 205 <property name="text">
159 <item> 206 <string>Player 1</string>
160 <widget class="QPushButton" name="buttonB"> 207 </property>
161 <property name="text"> 208 </widget>
162 <string/>
163 </property>
164 </widget>
165 </item>
166 </layout>
167 </item> 209 </item>
168 <item row="1" column="0"> 210 <item row="2" column="1">
169 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout"> 211 <widget class="QLabel" name="label_4">
170 <item> 212 <property name="text">
171 <widget class="QLabel" name="labelX"> 213 <string>Player 2</string>
172 <property name="text"> 214 </property>
173 <string>X:</string> 215 </widget>
174 </property>
175 </widget>
176 </item>
177 <item>
178 <widget class="QPushButton" name="buttonX">
179 <property name="text">
180 <string/>
181 </property>
182 </widget>
183 </item>
184 </layout>
185 </item> 216 </item>
186 <item row="1" column="1"> 217 <item row="3" column="1">
187 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout"> 218 <widget class="QLabel" name="label_5">
188 <item> 219 <property name="text">
189 <widget class="QLabel" name="labelY"> 220 <string>Player 3</string>
190 <property name="text"> 221 </property>
191 <string>Y:</string> 222 </widget>
192 </property>
193 </widget>
194 </item>
195 <item>
196 <widget class="QPushButton" name="buttonY">
197 <property name="text">
198 <string/>
199 </property>
200 </widget>
201 </item>
202 </layout>
203 </item> 223 </item>
204 </layout> 224 <item row="4" column="1">
205 </widget> 225 <widget class="QLabel" name="label_6">
206 </item> 226 <property name="text">
207 <item row="0" column="1"> 227 <string>Player 4</string>
208 <widget class="QGroupBox" name="Dpad"> 228 </property>
209 <property name="title"> 229 </widget>
210 <string>Directional Pad</string>
211 </property>
212 <property name="flat">
213 <bool>false</bool>
214 </property>
215 <property name="checkable">
216 <bool>false</bool>
217 </property>
218 <layout class="QGridLayout" name="gridLayout_2">
219 <item row="1" column="0">
220 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
221 <item>
222 <widget class="QLabel" name="labelDpadUp">
223 <property name="text">
224 <string>Up:</string>
225 </property>
226 </widget>
227 </item>
228 <item>
229 <widget class="QPushButton" name="buttonDpadUp">
230 <property name="text">
231 <string/>
232 </property>
233 </widget>
234 </item>
235 </layout>
236 </item> 230 </item>
237 <item row="1" column="1"> 231 <item row="5" column="1">
238 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout"> 232 <widget class="QLabel" name="label_7">
239 <item> 233 <property name="text">
240 <widget class="QLabel" name="labelDpadDown"> 234 <string>Player 5</string>
241 <property name="text"> 235 </property>
242 <string>Down:</string> 236 </widget>
243 </property>
244 </widget>
245 </item>
246 <item>
247 <widget class="QPushButton" name="buttonDpadDown">
248 <property name="text">
249 <string/>
250 </property>
251 </widget>
252 </item>
253 </layout>
254 </item> 237 </item>
255 <item row="0" column="0"> 238 <item row="6" column="1">
256 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout"> 239 <widget class="QLabel" name="label_8">
257 <item> 240 <property name="text">
258 <widget class="QLabel" name="labelDpadLeft"> 241 <string>Player 6</string>
259 <property name="text"> 242 </property>
260 <string>Left:</string> 243 </widget>
261 </property>
262 </widget>
263 </item>
264 <item>
265 <widget class="QPushButton" name="buttonDpadLeft">
266 <property name="text">
267 <string/>
268 </property>
269 </widget>
270 </item>
271 </layout>
272 </item> 244 </item>
273 <item row="0" column="1"> 245 <item row="7" column="1">
274 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout"> 246 <widget class="QLabel" name="label_9">
275 <item> 247 <property name="text">
276 <widget class="QLabel" name="labelDpadRight"> 248 <string>Player 7</string>
277 <property name="text"> 249 </property>
278 <string>Right:</string> 250 </widget>
279 </property> 251 </item>
280 </widget> 252 <item row="8" column="1">
281 </item> 253 <widget class="QLabel" name="label_10">
282 <item> 254 <property name="text">
283 <widget class="QPushButton" name="buttonDpadRight"> 255 <string>Player 8</string>
284 <property name="text"> 256 </property>
285 <string/> 257 </widget>
286 </property>
287 </widget>
288 </item>
289 </layout>
290 </item> 258 </item>
291 </layout> 259 </layout>
292 </widget> 260 </widget>
293 </item> 261 </item>
294 <item row="3" column="0"> 262 <item>
295 <widget class="QGroupBox" name="shoulderButtons"> 263 <widget class="QGroupBox" name="gridGroupBox">
296 <property name="title"> 264 <property name="title">
297 <string>Shoulder Buttons</string> 265 <string>Handheld</string>
298 </property>
299 <property name="flat">
300 <bool>false</bool>
301 </property> 266 </property>
302 <property name="checkable"> 267 <layout class="QGridLayout" name="gridLayout_2">
303 <bool>false</bool> 268 <item row="1" column="2">
304 </property> 269 <spacer name="horizontalSpacer_5">
305 <layout class="QGridLayout" name="gridLayout_3"> 270 <property name="orientation">
306 <item row="0" column="0"> 271 <enum>Qt::Horizontal</enum>
307 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> 272 </property>
308 <item> 273 <property name="sizeType">
309 <widget class="QLabel" name="labelL"> 274 <enum>QSizePolicy::Fixed</enum>
310 <property name="text"> 275 </property>
311 <string>L:</string> 276 <property name="sizeHint" stdset="0">
312 </property> 277 <size>
313 </widget> 278 <width>72</width>
314 </item> 279 <height>20</height>
315 <item> 280 </size>
316 <widget class="QPushButton" name="buttonL"> 281 </property>
317 <property name="text"> 282 </spacer>
318 <string/>
319 </property>
320 </widget>
321 </item>
322 </layout>
323 </item> 283 </item>
324 <item row="0" column="1"> 284 <item row="1" column="4">
325 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> 285 <spacer name="horizontalSpacer_4">
326 <item> 286 <property name="orientation">
327 <widget class="QLabel" name="labelR"> 287 <enum>Qt::Horizontal</enum>
328 <property name="text"> 288 </property>
329 <string>R:</string> 289 <property name="sizeHint" stdset="0">
330 </property> 290 <size>
331 </widget> 291 <width>40</width>
332 </item> 292 <height>20</height>
333 <item> 293 </size>
334 <widget class="QPushButton" name="buttonR"> 294 </property>
335 <property name="text"> 295 </spacer>
336 <string/> 296 </item>
337 </property> 297 <item row="1" column="3">
338 </widget> 298 <widget class="QPushButton" name="handheld_configure">
339 </item> 299 <property name="text">
340 </layout> 300 <string>Configure</string>
301 </property>
302 </widget>
341 </item> 303 </item>
342 <item row="1" column="0"> 304 <item row="1" column="0">
343 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> 305 <spacer name="horizontalSpacer_3">
344 <item> 306 <property name="orientation">
345 <widget class="QLabel" name="labelZL"> 307 <enum>Qt::Horizontal</enum>
346 <property name="text"> 308 </property>
347 <string>ZL:</string> 309 <property name="sizeHint" stdset="0">
348 </property> 310 <size>
349 </widget> 311 <width>40</width>
350 </item> 312 <height>20</height>
351 <item> 313 </size>
352 <widget class="QPushButton" name="buttonZL"> 314 </property>
353 <property name="text"> 315 </spacer>
354 <string/>
355 </property>
356 </widget>
357 </item>
358 </layout>
359 </item> 316 </item>
360 <item row="1" column="1"> 317 <item row="1" column="1">
361 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> 318 <widget class="QCheckBox" name="handheld_connected">
362 <item> 319 <property name="text">
363 <widget class="QLabel" name="labelZR"> 320 <string>Joycons Docked</string>
364 <property name="text"> 321 </property>
365 <string>ZR:</string> 322 </widget>
366 </property>
367 </widget>
368 </item>
369 <item>
370 <widget class="QPushButton" name="buttonZR">
371 <property name="text">
372 <string/>
373 </property>
374 </widget>
375 </item>
376 </layout>
377 </item>
378 <item row="2" column="0">
379 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
380 <item>
381 <widget class="QLabel" name="labelSL">
382 <property name="text">
383 <string>SL:</string>
384 </property>
385 </widget>
386 </item>
387 <item>
388 <widget class="QPushButton" name="buttonSL">
389 <property name="text">
390 <string/>
391 </property>
392 </widget>
393 </item>
394 </layout>
395 </item> 323 </item>
396 <item row="2" column="1"> 324 <item row="0" column="1">
397 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> 325 <widget class="QCheckBox" name="use_docked_mode">
398 <item> 326 <property name="text">
399 <widget class="QLabel" name="labelSR"> 327 <string>Use Docked Mode</string>
400 <property name="text"> 328 </property>
401 <string>SR:</string> 329 </widget>
402 </property>
403 </widget>
404 </item>
405 <item>
406 <widget class="QPushButton" name="buttonSR">
407 <property name="text">
408 <string/>
409 </property>
410 </widget>
411 </item>
412 </layout>
413 </item> 330 </item>
414 </layout> 331 </layout>
415 </widget> 332 </widget>
416 </item> 333 </item>
417 <item row="1" column="1"> 334 <item>
418 <widget class="QGroupBox" name="RStick"> 335 <widget class="QGroupBox" name="gridGroupBox">
419 <property name="title"> 336 <property name="title">
420 <string>Right Stick</string> 337 <string>Other</string>
421 </property>
422 <property name="alignment">
423 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
424 </property>
425 <property name="flat">
426 <bool>false</bool>
427 </property> 338 </property>
428 <property name="checkable"> 339 <layout class="QGridLayout" name="gridLayout_3">
429 <bool>false</bool>
430 </property>
431 <layout class="QGridLayout" name="gridLayout_5">
432 <item row="1" column="1"> 340 <item row="1" column="1">
433 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> 341 <widget class="QCheckBox" name="keyboard_enabled">
434 <item> 342 <property name="minimumSize">
435 <widget class="QLabel" name="labelRStickDown"> 343 <size>
436 <property name="text"> 344 <width>0</width>
437 <string>Down:</string> 345 <height>23</height>
438 </property> 346 </size>
439 </widget> 347 </property>
440 </item>
441 <item>
442 <widget class="QPushButton" name="buttonRStickDown">
443 <property name="text">
444 <string/>
445 </property>
446 </widget>
447 </item>
448 </layout>
449 </item>
450 <item row="0" column="1">
451 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
452 <item>
453 <widget class="QLabel" name="labelRStickRight">
454 <property name="text">
455 <string>Right:</string>
456 </property>
457 </widget>
458 </item>
459 <item>
460 <widget class="QPushButton" name="buttonRStickRight">
461 <property name="text">
462 <string/>
463 </property>
464 </widget>
465 </item>
466 </layout>
467 </item>
468 <item row="3" column="0" colspan="2">
469 <widget class="QPushButton" name="buttonRStickAnalog">
470 <property name="text"> 348 <property name="text">
471 <string>Set Analog Stick</string> 349 <string>Keyboard</string>
472 </property> 350 </property>
473 </widget> 351 </widget>
474 </item> 352 </item>
475 <item row="0" column="0">
476 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
477 <item>
478 <widget class="QLabel" name="labelRStickLeft">
479 <property name="text">
480 <string>Left:</string>
481 </property>
482 </widget>
483 </item>
484 <item>
485 <widget class="QPushButton" name="buttonRStickLeft">
486 <property name="text">
487 <string/>
488 </property>
489 </widget>
490 </item>
491 </layout>
492 </item>
493 <item row="1" column="0">
494 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
495 <item>
496 <widget class="QLabel" name="labelRStickUp">
497 <property name="text">
498 <string>Up:</string>
499 </property>
500 </widget>
501 </item>
502 <item>
503 <widget class="QPushButton" name="buttonRStickUp">
504 <property name="text">
505 <string/>
506 </property>
507 </widget>
508 </item>
509 </layout>
510 </item>
511 <item row="2" column="0">
512 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
513 <item>
514 <widget class="QLabel" name="labelRStickPressed">
515 <property name="text">
516 <string>Pressed:</string>
517 </property>
518 </widget>
519 </item>
520 <item>
521 <widget class="QPushButton" name="buttonRStick">
522 <property name="text">
523 <string/>
524 </property>
525 </widget>
526 </item>
527 </layout>
528 </item>
529 <item row="2" column="1"> 353 <item row="2" column="1">
530 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> 354 <widget class="QCheckBox" name="debug_enabled">
531 <item> 355 <property name="text">
532 <widget class="QLabel" name="labelRStickMod"> 356 <string>Debug Controller</string>
533 <property name="text"> 357 </property>
534 <string>Modifier:</string> 358 </widget>
535 </property>
536 </widget>
537 </item>
538 <item>
539 <widget class="QPushButton" name="buttonRStickMod">
540 <property name="text">
541 <string/>
542 </property>
543 </widget>
544 </item>
545 </layout>
546 </item> 359 </item>
547 </layout> 360 <item row="3" column="1">
548 </widget> 361 <widget class="QCheckBox" name="touchscreen_enabled">
549 </item>
550 <item row="1" column="0">
551 <widget class="QGroupBox" name="LStick">
552 <property name="title">
553 <string>Left Stick</string>
554 </property>
555 <property name="flat">
556 <bool>false</bool>
557 </property>
558 <property name="checkable">
559 <bool>false</bool>
560 </property>
561 <layout class="QGridLayout" name="gridLayout_4">
562 <item row="1" column="1">
563 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
564 <item>
565 <widget class="QLabel" name="labelLStickDown">
566 <property name="text">
567 <string>Down:</string>
568 </property>
569 </widget>
570 </item>
571 <item>
572 <widget class="QPushButton" name="buttonLStickDown">
573 <property name="text">
574 <string/>
575 </property>
576 </widget>
577 </item>
578 </layout>
579 </item>
580 <item row="4" column="0" colspan="2">
581 <widget class="QPushButton" name="buttonLStickAnalog">
582 <property name="text"> 362 <property name="text">
583 <string>Set Analog Stick</string> 363 <string>Touchscreen</string>
584 </property> 364 </property>
585 </widget> 365 </widget>
586 </item> 366 </item>
587 <item row="0" column="1"> 367 <item row="0" column="1">
588 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout"> 368 <widget class="QCheckBox" name="mouse_enabled">
589 <item> 369 <property name="minimumSize">
590 <widget class="QLabel" name="labelLStickRight"> 370 <size>
591 <property name="text"> 371 <width>0</width>
592 <string>Right:</string> 372 <height>23</height>
593 </property> 373 </size>
594 </widget> 374 </property>
595 </item> 375 <property name="text">
596 <item> 376 <string>Mouse</string>
597 <widget class="QPushButton" name="buttonLStickRight"> 377 </property>
598 <property name="text"> 378 </widget>
599 <string/> 379 </item>
600 </property> 380 <item row="0" column="4">
601 </widget> 381 <spacer name="horizontalSpacer_7">
602 </item> 382 <property name="orientation">
603 </layout> 383 <enum>Qt::Horizontal</enum>
384 </property>
385 <property name="sizeHint" stdset="0">
386 <size>
387 <width>40</width>
388 <height>20</height>
389 </size>
390 </property>
391 </spacer>
392 </item>
393 <item row="0" column="2">
394 <spacer name="horizontalSpacer_8">
395 <property name="orientation">
396 <enum>Qt::Horizontal</enum>
397 </property>
398 <property name="sizeType">
399 <enum>QSizePolicy::Fixed</enum>
400 </property>
401 <property name="sizeHint" stdset="0">
402 <size>
403 <width>76</width>
404 <height>20</height>
405 </size>
406 </property>
407 </spacer>
604 </item> 408 </item>
605 <item row="0" column="0"> 409 <item row="0" column="0">
606 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout"> 410 <spacer name="horizontalSpacer_6">
607 <item> 411 <property name="orientation">
608 <widget class="QLabel" name="labelLStickLeft"> 412 <enum>Qt::Horizontal</enum>
609 <property name="text"> 413 </property>
610 <string>Left:</string> 414 <property name="sizeHint" stdset="0">
611 </property> 415 <size>
612 </widget> 416 <width>40</width>
613 </item> 417 <height>20</height>
614 <item> 418 </size>
615 <widget class="QPushButton" name="buttonLStickLeft"> 419 </property>
616 <property name="text"> 420 </spacer>
617 <string/>
618 </property>
619 </widget>
620 </item>
621 </layout>
622 </item> 421 </item>
623 <item row="1" column="0"> 422 <item row="3" column="3">
624 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> 423 <widget class="QPushButton" name="touchscreen_advanced">
625 <item> 424 <property name="text">
626 <widget class="QLabel" name="labelLStickUp"> 425 <string>Advanced</string>
627 <property name="text"> 426 </property>
628 <string>Up:</string> 427 </widget>
629 </property>
630 </widget>
631 </item>
632 <item>
633 <widget class="QPushButton" name="buttonLStickUp">
634 <property name="text">
635 <string/>
636 </property>
637 </widget>
638 </item>
639 </layout>
640 </item>
641 <item row="3" column="0">
642 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
643 <item>
644 <widget class="QLabel" name="labelLStickMod">
645 <property name="text">
646 <string>Modifier:</string>
647 </property>
648 </widget>
649 </item>
650 <item>
651 <widget class="QPushButton" name="buttonLStickMod">
652 <property name="text">
653 <string/>
654 </property>
655 </widget>
656 </item>
657 </layout>
658 </item> 428 </item>
659 <item row="3" column="1"> 429 <item row="2" column="3">
660 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0"> 430 <widget class="QPushButton" name="debug_configure">
661 <item> 431 <property name="text">
662 <widget class="QLabel" name="labelLStickPressed"> 432 <string>Configure</string>
663 <property name="text"> 433 </property>
664 <string>Pressed:</string> 434 </widget>
665 </property> 435 </item>
666 </widget> 436 <item row="0" column="3">
667 </item> 437 <widget class="QPushButton" name="mouse_advanced">
668 <item> 438 <property name="text">
669 <widget class="QPushButton" name="buttonLStick"> 439 <string>Advanced</string>
670 <property name="text"> 440 </property>
671 <string/> 441 </widget>
672 </property>
673 </widget>
674 </item>
675 </layout>
676 </item> 442 </item>
677 </layout> 443 </layout>
678 </widget> 444 </widget>
679 </item> 445 </item>
680 </layout>
681 </item>
682 <item>
683 <layout class="QHBoxLayout" name="horizontalLayout">
684 <item> 446 <item>
685 <spacer name="horizontalSpacer"> 447 <spacer name="verticalSpacer">
686 <property name="orientation"> 448 <property name="orientation">
687 <enum>Qt::Horizontal</enum> 449 <enum>Qt::Vertical</enum>
688 </property> 450 </property>
689 <property name="sizeHint" stdset="0"> 451 <property name="sizeHint" stdset="0">
690 <size> 452 <size>
691 <width>40</width> 453 <width>20</width>
692 <height>20</height> 454 <height>40</height>
693 </size> 455 </size>
694 </property> 456 </property>
695 </spacer> 457 </spacer>
696 </item> 458 </item>
697 <item> 459 <item>
698 <widget class="QPushButton" name="buttonClearAll"> 460 <layout class="QHBoxLayout" name="horizontalLayout">
699 <property name="sizePolicy"> 461 <item>
700 <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> 462 <widget class="QPushButton" name="restore_defaults_button">
701 <horstretch>0</horstretch> 463 <property name="text">
702 <verstretch>0</verstretch> 464 <string>Restore Defaults</string>
703 </sizepolicy> 465 </property>
704 </property> 466 </widget>
705 <property name="sizeIncrement"> 467 </item>
706 <size> 468 <item>
707 <width>0</width> 469 <spacer name="horizontalSpacer_9">
708 <height>0</height> 470 <property name="orientation">
709 </size> 471 <enum>Qt::Horizontal</enum>
710 </property> 472 </property>
711 <property name="baseSize"> 473 <property name="sizeHint" stdset="0">
712 <size> 474 <size>
713 <width>0</width> 475 <width>40</width>
714 <height>0</height> 476 <height>20</height>
715 </size> 477 </size>
716 </property> 478 </property>
717 <property name="layoutDirection"> 479 </spacer>
718 <enum>Qt::LeftToRight</enum> 480 </item>
719 </property> 481 </layout>
720 <property name="text">
721 <string>Clear All</string>
722 </property>
723 </widget>
724 </item>
725 <item>
726 <widget class="QPushButton" name="buttonRestoreDefaults">
727 <property name="sizePolicy">
728 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
729 <horstretch>0</horstretch>
730 <verstretch>0</verstretch>
731 </sizepolicy>
732 </property>
733 <property name="sizeIncrement">
734 <size>
735 <width>0</width>
736 <height>0</height>
737 </size>
738 </property>
739 <property name="baseSize">
740 <size>
741 <width>0</width>
742 <height>0</height>
743 </size>
744 </property>
745 <property name="layoutDirection">
746 <enum>Qt::LeftToRight</enum>
747 </property>
748 <property name="text">
749 <string>Restore Defaults</string>
750 </property>
751 </widget>
752 </item> 482 </item>
753 </layout> 483 </layout>
754 </item> 484 </item>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
new file mode 100644
index 000000000..ba6e09368
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -0,0 +1,508 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <utility>
8#include <QColorDialog>
9#include <QMenu>
10#include <QMessageBox>
11#include <QTimer>
12#include "common/assert.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
15#include "ui_configure_input_player.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_input_player.h"
18
19const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
20 ConfigureInputPlayer::analog_sub_buttons{{
21 "up",
22 "down",
23 "left",
24 "right",
25 "modifier",
26 }};
27
28static void MoveGridElement(QGridLayout* grid, int row_old, int column_old, int row_new,
29 int column_new) {
30 const auto item = grid->itemAtPosition(row_old, column_old);
31 // grid->removeItem(item);
32 grid->addItem(item, row_new, column_new);
33}
34
35static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) {
36 const int index1 = grid->indexOf(item);
37 const int index2 = grid->indexOf(onTopOf);
38 int row, column, rowSpan, columnSpan;
39 grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan);
40 grid->takeAt(index1);
41 grid->addWidget(item, row, column, rowSpan, columnSpan);
42}
43
44static QString GetKeyName(int key_code) {
45 switch (key_code) {
46 case Qt::Key_Shift:
47 return QObject::tr("Shift");
48 case Qt::Key_Control:
49 return QObject::tr("Ctrl");
50 case Qt::Key_Alt:
51 return QObject::tr("Alt");
52 case Qt::Key_Meta:
53 return "";
54 default:
55 return QKeySequence(key_code).toString();
56 }
57}
58
59static void SetAnalogButton(const Common::ParamPackage& input_param,
60 Common::ParamPackage& analog_param, const std::string& button_name) {
61 if (analog_param.Get("engine", "") != "analog_from_button") {
62 analog_param = {
63 {"engine", "analog_from_button"},
64 {"modifier_scale", "0.5"},
65 };
66 }
67 analog_param.Set(button_name, input_param.Serialize());
68}
69
70static QString ButtonToText(const Common::ParamPackage& param) {
71 if (!param.Has("engine")) {
72 return QObject::tr("[not set]");
73 } else if (param.Get("engine", "") == "keyboard") {
74 return GetKeyName(param.Get("code", 0));
75 } else if (param.Get("engine", "") == "sdl") {
76 if (param.Has("hat")) {
77 return QString(QObject::tr("Hat %1 %2"))
78 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
79 }
80 if (param.Has("axis")) {
81 return QString(QObject::tr("Axis %1%2"))
82 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
83 }
84 if (param.Has("button")) {
85 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
86 }
87 return QString();
88 } else {
89 return QObject::tr("[unknown]");
90 }
91};
92
93static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
94 if (!param.Has("engine")) {
95 return QObject::tr("[not set]");
96 } else if (param.Get("engine", "") == "analog_from_button") {
97 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
98 } else if (param.Get("engine", "") == "sdl") {
99 if (dir == "modifier") {
100 return QString(QObject::tr("[unused]"));
101 }
102
103 if (dir == "left" || dir == "right") {
104 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str());
105 } else if (dir == "up" || dir == "down") {
106 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
107 }
108 return QString();
109 } else {
110 return QObject::tr("[unknown]");
111 }
112};
113
114ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug)
115 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()),
116 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
117 player_index(player_index), debug(debug) {
118
119 ui->setupUi(this);
120 setFocusPolicy(Qt::ClickFocus);
121
122 button_map = {
123 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
124 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
125 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
126 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
127 ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
128 ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
129 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
130 };
131
132 analog_map_buttons = {{
133 {
134 ui->buttonLStickUp,
135 ui->buttonLStickDown,
136 ui->buttonLStickLeft,
137 ui->buttonLStickRight,
138 ui->buttonLStickMod,
139 },
140 {
141 ui->buttonRStickUp,
142 ui->buttonRStickDown,
143 ui->buttonRStickLeft,
144 ui->buttonRStickRight,
145 ui->buttonRStickMod,
146 },
147 }};
148
149 debug_hidden = {
150 ui->buttonSL, ui->labelSL,
151 ui->buttonSR, ui->labelSR,
152 ui->buttonLStick, ui->labelLStickPressed,
153 ui->buttonRStick, ui->labelRStickPressed,
154 ui->buttonHome, ui->labelHome,
155 ui->buttonScreenshot, ui->labelScreenshot,
156 };
157
158 auto layout = Settings::values.players[player_index].type;
159 if (debug)
160 layout = Settings::ControllerType::DualJoycon;
161
162 switch (layout) {
163 case Settings::ControllerType::ProController:
164 case Settings::ControllerType::DualJoycon:
165 layout_hidden = {
166 ui->buttonSL,
167 ui->labelSL,
168 ui->buttonSR,
169 ui->labelSR,
170 };
171 break;
172 case Settings::ControllerType::LeftJoycon:
173 layout_hidden = {
174 ui->right_body_button,
175 ui->right_buttons_button,
176 ui->right_body_label,
177 ui->right_buttons_label,
178 ui->buttonR,
179 ui->labelR,
180 ui->buttonZR,
181 ui->labelZR,
182 ui->labelHome,
183 ui->buttonHome,
184 ui->buttonPlus,
185 ui->labelPlus,
186 ui->RStick,
187 ui->faceButtons,
188 };
189 break;
190 case Settings::ControllerType::RightJoycon:
191 layout_hidden = {
192 ui->left_body_button, ui->left_buttons_button,
193 ui->left_body_label, ui->left_buttons_label,
194 ui->buttonL, ui->labelL,
195 ui->buttonZL, ui->labelZL,
196 ui->labelScreenshot, ui->buttonScreenshot,
197 ui->buttonMinus, ui->labelMinus,
198 ui->LStick, ui->Dpad,
199 };
200 break;
201 }
202
203 if (debug || layout == Settings::ControllerType::ProController) {
204 ui->controller_color->hide();
205 } else {
206 if (layout == Settings::ControllerType::LeftJoycon ||
207 layout == Settings::ControllerType::RightJoycon) {
208 ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
209
210 LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
211 LayerGridElements(ui->buttons, ui->misc, ui->RStick);
212 LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
213 LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
214 }
215 }
216
217 for (auto* widget : layout_hidden)
218 widget->setVisible(false);
219
220 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
221
222 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
223 if (!button_map[button_id])
224 continue;
225 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
226 connect(button_map[button_id], &QPushButton::released, [=]() {
227 handleClick(
228 button_map[button_id],
229 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
230 InputCommon::Polling::DeviceType::Button);
231 });
232 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
233 [=](const QPoint& menu_location) {
234 QMenu context_menu;
235 context_menu.addAction(tr("Clear"), [&] {
236 buttons_param[button_id].Clear();
237 button_map[button_id]->setText(tr("[not set]"));
238 });
239 context_menu.addAction(tr("Restore Default"), [&] {
240 buttons_param[button_id] = Common::ParamPackage{
241 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
242 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
243 });
244 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
245 });
246 }
247
248 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
249 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
250 if (!analog_map_buttons[analog_id][sub_button_id])
251 continue;
252 analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
253 Qt::CustomContextMenu);
254 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
255 handleClick(analog_map_buttons[analog_id][sub_button_id],
256 [=](const Common::ParamPackage& params) {
257 SetAnalogButton(params, analogs_param[analog_id],
258 analog_sub_buttons[sub_button_id]);
259 },
260 InputCommon::Polling::DeviceType::Button);
261 });
262 connect(analog_map_buttons[analog_id][sub_button_id],
263 &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
264 QMenu context_menu;
265 context_menu.addAction(tr("Clear"), [&] {
266 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
267 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
268 });
269 context_menu.addAction(tr("Restore Default"), [&] {
270 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
271 Config::default_analogs[analog_id][sub_button_id])};
272 SetAnalogButton(params, analogs_param[analog_id],
273 analog_sub_buttons[sub_button_id]);
274 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
275 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
276 });
277 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
278 menu_location));
279 });
280 }
281 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
282 QMessageBox::information(this, tr("Information"),
283 tr("After pressing OK, first move your joystick horizontally, "
284 "and then vertically."));
285 handleClick(
286 analog_map_stick[analog_id],
287 [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
288 InputCommon::Polling::DeviceType::Analog);
289 });
290 }
291
292 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
293 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
294
295 timeout_timer->setSingleShot(true);
296 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
297
298 connect(poll_timer.get(), &QTimer::timeout, [this]() {
299 Common::ParamPackage params;
300 for (auto& poller : device_pollers) {
301 params = poller->GetNextInput();
302 if (params.Has("engine")) {
303 setPollingResult(params, false);
304 return;
305 }
306 }
307 });
308
309 controller_color_buttons = {
310 ui->left_body_button,
311 ui->left_buttons_button,
312 ui->right_body_button,
313 ui->right_buttons_button,
314 };
315
316 for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
317 connect(controller_color_buttons[i], &QPushButton::clicked, this,
318 std::bind(&ConfigureInputPlayer::OnControllerButtonClick, this, i));
319 }
320
321 this->loadConfiguration();
322 this->resize(0, 0);
323
324 // TODO(wwylele): enable this when we actually emulate it
325 ui->buttonHome->setEnabled(false);
326}
327
328ConfigureInputPlayer::~ConfigureInputPlayer() = default;
329
330void ConfigureInputPlayer::applyConfiguration() {
331 auto& buttons =
332 debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons;
333 auto& analogs =
334 debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
335
336 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
337 [](const Common::ParamPackage& param) { return param.Serialize(); });
338 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
339 [](const Common::ParamPackage& param) { return param.Serialize(); });
340
341 if (debug)
342 return;
343
344 std::array<u32, 4> colors{};
345 std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(),
346 [](QColor color) { return color.rgb(); });
347
348 Settings::values.players[player_index].body_color_left = colors[0];
349 Settings::values.players[player_index].button_color_left = colors[1];
350 Settings::values.players[player_index].body_color_right = colors[2];
351 Settings::values.players[player_index].button_color_right = colors[3];
352}
353
354void ConfigureInputPlayer::OnControllerButtonClick(int i) {
355 const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
356 if (!new_bg_color.isValid())
357 return;
358 controller_colors[i] = new_bg_color;
359 controller_color_buttons[i]->setStyleSheet(
360 QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
361}
362
363void ConfigureInputPlayer::loadConfiguration() {
364 if (debug) {
365 std::transform(Settings::values.debug_pad_buttons.begin(),
366 Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
367 [](const std::string& str) { return Common::ParamPackage(str); });
368 std::transform(Settings::values.debug_pad_analogs.begin(),
369 Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
370 [](const std::string& str) { return Common::ParamPackage(str); });
371 } else {
372 std::transform(Settings::values.players[player_index].buttons.begin(),
373 Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
374 [](const std::string& str) { return Common::ParamPackage(str); });
375 std::transform(Settings::values.players[player_index].analogs.begin(),
376 Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
377 [](const std::string& str) { return Common::ParamPackage(str); });
378 }
379
380 updateButtonLabels();
381
382 if (debug)
383 return;
384
385 std::array<u32, 4> colors = {
386 Settings::values.players[player_index].body_color_left,
387 Settings::values.players[player_index].button_color_left,
388 Settings::values.players[player_index].body_color_right,
389 Settings::values.players[player_index].button_color_right,
390 };
391
392 std::transform(colors.begin(), colors.end(), controller_colors.begin(),
393 [](u32 rgb) { return QColor::fromRgb(rgb); });
394
395 for (std::size_t i = 0; i < colors.size(); ++i) {
396 controller_color_buttons[i]->setStyleSheet(
397 QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
398 }
399}
400
401void ConfigureInputPlayer::restoreDefaults() {
402 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
403 buttons_param[button_id] = Common::ParamPackage{
404 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
405 }
406
407 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
408 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
409 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
410 Config::default_analogs[analog_id][sub_button_id])};
411 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
412 }
413 }
414 updateButtonLabels();
415}
416
417void ConfigureInputPlayer::ClearAll() {
418 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
419 if (button_map[button_id] && button_map[button_id]->isEnabled())
420 buttons_param[button_id].Clear();
421 }
422 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
423 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
424 if (analog_map_buttons[analog_id][sub_button_id] &&
425 analog_map_buttons[analog_id][sub_button_id]->isEnabled())
426 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
427 }
428 }
429
430 updateButtonLabels();
431}
432
433void ConfigureInputPlayer::updateButtonLabels() {
434 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
435 button_map[button]->setText(ButtonToText(buttons_param[button]));
436 }
437
438 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
439 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
440 if (analog_map_buttons[analog_id][sub_button_id]) {
441 analog_map_buttons[analog_id][sub_button_id]->setText(
442 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
443 }
444 }
445 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
446 }
447}
448
449void ConfigureInputPlayer::handleClick(
450 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
451 InputCommon::Polling::DeviceType type) {
452 button->setText(tr("[press key]"));
453 button->setFocus();
454
455 const auto iter = std::find(button_map.begin(), button_map.end(), button);
456 ASSERT(iter != button_map.end());
457 const auto index = std::distance(button_map.begin(), iter);
458 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
459
460 input_setter = new_input_setter;
461
462 device_pollers = InputCommon::Polling::GetPollers(type);
463
464 // Keyboard keys can only be used as button devices
465 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
466
467 for (auto& poller : device_pollers) {
468 poller->Start();
469 }
470
471 grabKeyboard();
472 grabMouse();
473 timeout_timer->start(5000); // Cancel after 5 seconds
474 poll_timer->start(200); // Check for new inputs every 200ms
475}
476
477void ConfigureInputPlayer::setPollingResult(const Common::ParamPackage& params, bool abort) {
478 releaseKeyboard();
479 releaseMouse();
480 timeout_timer->stop();
481 poll_timer->stop();
482 for (auto& poller : device_pollers) {
483 poller->Stop();
484 }
485
486 if (!abort) {
487 (*input_setter)(params);
488 }
489
490 updateButtonLabels();
491 input_setter = std::nullopt;
492}
493
494void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
495 if (!input_setter || !event)
496 return;
497
498 if (event->key() != Qt::Key_Escape) {
499 if (want_keyboard_keys) {
500 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
501 false);
502 } else {
503 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
504 return;
505 }
506 }
507 setPollingResult({}, true);
508}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
new file mode 100644
index 000000000..b0e5550c5
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -0,0 +1,103 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <functional>
9#include <memory>
10#include <optional>
11#include <string>
12#include <unordered_map>
13#include <QDialog>
14#include <QKeyEvent>
15#include "common/param_package.h"
16#include "core/settings.h"
17#include "input_common/main.h"
18#include "ui_configure_input.h"
19
20class QPushButton;
21class QString;
22class QTimer;
23
24namespace Ui {
25class ConfigureInputPlayer;
26}
27
28class ConfigureInputPlayer : public QDialog {
29 Q_OBJECT
30
31public:
32 explicit ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug = false);
33 ~ConfigureInputPlayer() override;
34
35 /// Save all button configurations to settings file
36 void applyConfiguration();
37
38private:
39 std::unique_ptr<Ui::ConfigureInputPlayer> ui;
40
41 u8 player_index;
42 bool debug;
43
44 std::unique_ptr<QTimer> timeout_timer;
45 std::unique_ptr<QTimer> poll_timer;
46
47 /// This will be the the setting function when an input is awaiting configuration.
48 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
49
50 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
51 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
52
53 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
54
55 /// Each button input is represented by a QPushButton.
56 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
57
58 std::vector<QWidget*> debug_hidden;
59 std::vector<QWidget*> layout_hidden;
60
61 /// A group of five QPushButtons represent one analog input. The buttons each represent up,
62 /// down, left, right, and modifier, respectively.
63 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
64 analog_map_buttons;
65
66 /// Analog inputs are also represented each with a single button, used to configure with an
67 /// actual analog stick
68 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
69
70 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
71
72 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
73
74 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
75 /// keyboard events are ignored.
76 bool want_keyboard_keys = false;
77
78 std::array<QPushButton*, 4> controller_color_buttons;
79 std::array<QColor, 4> controller_colors;
80
81 void OnControllerButtonClick(int i);
82
83 /// Load configuration settings.
84 void loadConfiguration();
85 /// Restore all buttons to their default values.
86 void restoreDefaults();
87 /// Clear all input configuration
88 void ClearAll();
89
90 /// Update UI to reflect current configuration.
91 void updateButtonLabels();
92
93 /// Called when the button was pressed.
94 void handleClick(QPushButton* button,
95 std::function<void(const Common::ParamPackage&)> new_input_setter,
96 InputCommon::Polling::DeviceType type);
97
98 /// Finish polling and configure input using the input_setter
99 void setPollingResult(const Common::ParamPackage& params, bool abort);
100
101 /// Handle key press events.
102 void keyPressEvent(QKeyEvent* event) override;
103};
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
new file mode 100644
index 000000000..42db020be
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -0,0 +1,1164 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputPlayer</class>
4 <widget class="QDialog" name="ConfigureInputPlayer">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>408</width>
10 <height>731</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Input</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item>
18 <layout class="QGridLayout" name="buttons">
19 <item row="1" column="1">
20 <widget class="QGroupBox" name="RStick">
21 <property name="title">
22 <string>Right Stick</string>
23 </property>
24 <property name="alignment">
25 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
26 </property>
27 <property name="flat">
28 <bool>false</bool>
29 </property>
30 <property name="checkable">
31 <bool>false</bool>
32 </property>
33 <layout class="QGridLayout" name="gridLayout_5">
34 <item row="1" column="1">
35 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
36 <item>
37 <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout">
38 <item>
39 <widget class="QLabel" name="labelRStickDown">
40 <property name="text">
41 <string>Down:</string>
42 </property>
43 </widget>
44 </item>
45 </layout>
46 </item>
47 <item>
48 <widget class="QPushButton" name="buttonRStickDown">
49 <property name="text">
50 <string/>
51 </property>
52 </widget>
53 </item>
54 </layout>
55 </item>
56 <item row="0" column="1">
57 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
58 <item>
59 <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout">
60 <item>
61 <widget class="QLabel" name="labelRStickRight">
62 <property name="text">
63 <string>Right:</string>
64 </property>
65 </widget>
66 </item>
67 </layout>
68 </item>
69 <item>
70 <widget class="QPushButton" name="buttonRStickRight">
71 <property name="text">
72 <string/>
73 </property>
74 </widget>
75 </item>
76 </layout>
77 </item>
78 <item row="3" column="0" colspan="2">
79 <widget class="QPushButton" name="buttonRStickAnalog">
80 <property name="text">
81 <string>Set Analog Stick</string>
82 </property>
83 </widget>
84 </item>
85 <item row="0" column="0">
86 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
87 <item>
88 <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout">
89 <item>
90 <widget class="QLabel" name="labelRStickLeft">
91 <property name="text">
92 <string>Left:</string>
93 </property>
94 </widget>
95 </item>
96 </layout>
97 </item>
98 <item>
99 <widget class="QPushButton" name="buttonRStickLeft">
100 <property name="text">
101 <string/>
102 </property>
103 </widget>
104 </item>
105 </layout>
106 </item>
107 <item row="1" column="0">
108 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
109 <item>
110 <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout">
111 <item>
112 <widget class="QLabel" name="labelRStickUp">
113 <property name="text">
114 <string>Up:</string>
115 </property>
116 </widget>
117 </item>
118 </layout>
119 </item>
120 <item>
121 <widget class="QPushButton" name="buttonRStickUp">
122 <property name="text">
123 <string/>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </item>
129 <item row="2" column="0">
130 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
131 <item>
132 <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout">
133 <item>
134 <widget class="QLabel" name="labelRStickPressed">
135 <property name="text">
136 <string>Pressed:</string>
137 </property>
138 </widget>
139 </item>
140 </layout>
141 </item>
142 <item>
143 <widget class="QPushButton" name="buttonRStick">
144 <property name="text">
145 <string/>
146 </property>
147 </widget>
148 </item>
149 </layout>
150 </item>
151 <item row="2" column="1">
152 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
153 <item>
154 <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout">
155 <item>
156 <widget class="QLabel" name="labelRStickMod">
157 <property name="text">
158 <string>Modifier:</string>
159 </property>
160 </widget>
161 </item>
162 </layout>
163 </item>
164 <item>
165 <widget class="QPushButton" name="buttonRStickMod">
166 <property name="text">
167 <string/>
168 </property>
169 </widget>
170 </item>
171 </layout>
172 </item>
173 </layout>
174 </widget>
175 </item>
176 <item row="0" column="1">
177 <widget class="QGroupBox" name="Dpad">
178 <property name="title">
179 <string>Directional Pad</string>
180 </property>
181 <property name="flat">
182 <bool>false</bool>
183 </property>
184 <property name="checkable">
185 <bool>false</bool>
186 </property>
187 <layout class="QGridLayout" name="gridLayout_2">
188 <item row="1" column="0">
189 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
190 <item>
191 <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout">
192 <item>
193 <widget class="QLabel" name="labelDpadUp">
194 <property name="text">
195 <string>Up:</string>
196 </property>
197 </widget>
198 </item>
199 </layout>
200 </item>
201 <item>
202 <widget class="QPushButton" name="buttonDpadUp">
203 <property name="text">
204 <string/>
205 </property>
206 </widget>
207 </item>
208 </layout>
209 </item>
210 <item row="1" column="1">
211 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
212 <item>
213 <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout">
214 <item>
215 <widget class="QLabel" name="labelDpadDown">
216 <property name="text">
217 <string>Down:</string>
218 </property>
219 </widget>
220 </item>
221 </layout>
222 </item>
223 <item>
224 <widget class="QPushButton" name="buttonDpadDown">
225 <property name="text">
226 <string/>
227 </property>
228 </widget>
229 </item>
230 </layout>
231 </item>
232 <item row="0" column="0">
233 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
234 <item>
235 <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout">
236 <item>
237 <widget class="QLabel" name="labelDpadLeft">
238 <property name="minimumSize">
239 <size>
240 <width>80</width>
241 <height>0</height>
242 </size>
243 </property>
244 <property name="text">
245 <string>Left:</string>
246 </property>
247 </widget>
248 </item>
249 </layout>
250 </item>
251 <item>
252 <widget class="QPushButton" name="buttonDpadLeft">
253 <property name="text">
254 <string/>
255 </property>
256 </widget>
257 </item>
258 </layout>
259 </item>
260 <item row="0" column="1">
261 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
262 <item>
263 <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout">
264 <item>
265 <widget class="QLabel" name="labelDpadRight">
266 <property name="minimumSize">
267 <size>
268 <width>80</width>
269 <height>0</height>
270 </size>
271 </property>
272 <property name="text">
273 <string>Right:</string>
274 </property>
275 </widget>
276 </item>
277 </layout>
278 </item>
279 <item>
280 <widget class="QPushButton" name="buttonDpadRight">
281 <property name="text">
282 <string/>
283 </property>
284 </widget>
285 </item>
286 </layout>
287 </item>
288 </layout>
289 </widget>
290 </item>
291 <item row="0" column="0">
292 <widget class="QGroupBox" name="faceButtons">
293 <property name="title">
294 <string>Face Buttons</string>
295 </property>
296 <property name="flat">
297 <bool>false</bool>
298 </property>
299 <property name="checkable">
300 <bool>false</bool>
301 </property>
302 <layout class="QGridLayout" name="gridLayout">
303 <item row="0" column="0">
304 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
305 <item>
306 <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout">
307 <item>
308 <widget class="QLabel" name="labelA">
309 <property name="minimumSize">
310 <size>
311 <width>80</width>
312 <height>0</height>
313 </size>
314 </property>
315 <property name="text">
316 <string>A:</string>
317 </property>
318 </widget>
319 </item>
320 </layout>
321 </item>
322 <item>
323 <widget class="QPushButton" name="buttonA">
324 <property name="text">
325 <string/>
326 </property>
327 </widget>
328 </item>
329 </layout>
330 </item>
331 <item row="0" column="1">
332 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
333 <item>
334 <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout">
335 <item>
336 <widget class="QLabel" name="labelB">
337 <property name="minimumSize">
338 <size>
339 <width>80</width>
340 <height>0</height>
341 </size>
342 </property>
343 <property name="text">
344 <string>B:</string>
345 </property>
346 </widget>
347 </item>
348 </layout>
349 </item>
350 <item>
351 <widget class="QPushButton" name="buttonB">
352 <property name="text">
353 <string/>
354 </property>
355 </widget>
356 </item>
357 </layout>
358 </item>
359 <item row="1" column="0">
360 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
361 <item>
362 <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout">
363 <item>
364 <widget class="QLabel" name="labelX">
365 <property name="text">
366 <string>X:</string>
367 </property>
368 </widget>
369 </item>
370 </layout>
371 </item>
372 <item>
373 <widget class="QPushButton" name="buttonX">
374 <property name="text">
375 <string/>
376 </property>
377 </widget>
378 </item>
379 </layout>
380 </item>
381 <item row="1" column="1">
382 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
383 <item>
384 <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout">
385 <item>
386 <widget class="QLabel" name="labelY">
387 <property name="text">
388 <string>Y:</string>
389 </property>
390 </widget>
391 </item>
392 </layout>
393 </item>
394 <item>
395 <widget class="QPushButton" name="buttonY">
396 <property name="text">
397 <string/>
398 </property>
399 </widget>
400 </item>
401 </layout>
402 </item>
403 </layout>
404 </widget>
405 </item>
406 <item row="5" column="0" colspan="2">
407 <widget class="QGroupBox" name="controller_color">
408 <property name="title">
409 <string>Controller Color</string>
410 </property>
411 <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0">
412 <item row="0" column="0">
413 <spacer name="horizontalSpacer_2">
414 <property name="orientation">
415 <enum>Qt::Horizontal</enum>
416 </property>
417 <property name="sizeHint" stdset="0">
418 <size>
419 <width>40</width>
420 <height>20</height>
421 </size>
422 </property>
423 </spacer>
424 </item>
425 <item row="0" column="1">
426 <widget class="QLabel" name="left_body_label">
427 <property name="text">
428 <string>Left Body</string>
429 </property>
430 </widget>
431 </item>
432 <item row="0" column="6">
433 <spacer name="horizontalSpacer_3">
434 <property name="orientation">
435 <enum>Qt::Horizontal</enum>
436 </property>
437 <property name="sizeHint" stdset="0">
438 <size>
439 <width>40</width>
440 <height>20</height>
441 </size>
442 </property>
443 </spacer>
444 </item>
445 <item row="1" column="1">
446 <widget class="QLabel" name="left_buttons_label">
447 <property name="minimumSize">
448 <size>
449 <width>90</width>
450 <height>0</height>
451 </size>
452 </property>
453 <property name="text">
454 <string>Left Buttons</string>
455 </property>
456 </widget>
457 </item>
458 <item row="1" column="5">
459 <widget class="QPushButton" name="right_buttons_button">
460 <property name="sizePolicy">
461 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
462 <horstretch>0</horstretch>
463 <verstretch>0</verstretch>
464 </sizepolicy>
465 </property>
466 <property name="minimumSize">
467 <size>
468 <width>32</width>
469 <height>0</height>
470 </size>
471 </property>
472 <property name="maximumSize">
473 <size>
474 <width>40</width>
475 <height>16777215</height>
476 </size>
477 </property>
478 <property name="text">
479 <string/>
480 </property>
481 </widget>
482 </item>
483 <item row="0" column="4">
484 <widget class="QLabel" name="right_body_label">
485 <property name="text">
486 <string>Right Body</string>
487 </property>
488 </widget>
489 </item>
490 <item row="1" column="4">
491 <widget class="QLabel" name="right_buttons_label">
492 <property name="minimumSize">
493 <size>
494 <width>90</width>
495 <height>0</height>
496 </size>
497 </property>
498 <property name="text">
499 <string>Right Buttons</string>
500 </property>
501 </widget>
502 </item>
503 <item row="1" column="2">
504 <widget class="QPushButton" name="left_buttons_button">
505 <property name="sizePolicy">
506 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
507 <horstretch>0</horstretch>
508 <verstretch>0</verstretch>
509 </sizepolicy>
510 </property>
511 <property name="minimumSize">
512 <size>
513 <width>32</width>
514 <height>0</height>
515 </size>
516 </property>
517 <property name="maximumSize">
518 <size>
519 <width>40</width>
520 <height>16777215</height>
521 </size>
522 </property>
523 <property name="text">
524 <string/>
525 </property>
526 </widget>
527 </item>
528 <item row="0" column="2">
529 <widget class="QPushButton" name="left_body_button">
530 <property name="sizePolicy">
531 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
532 <horstretch>0</horstretch>
533 <verstretch>0</verstretch>
534 </sizepolicy>
535 </property>
536 <property name="minimumSize">
537 <size>
538 <width>32</width>
539 <height>0</height>
540 </size>
541 </property>
542 <property name="maximumSize">
543 <size>
544 <width>40</width>
545 <height>16777215</height>
546 </size>
547 </property>
548 <property name="text">
549 <string/>
550 </property>
551 </widget>
552 </item>
553 <item row="0" column="5">
554 <widget class="QPushButton" name="right_body_button">
555 <property name="sizePolicy">
556 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
557 <horstretch>0</horstretch>
558 <verstretch>0</verstretch>
559 </sizepolicy>
560 </property>
561 <property name="minimumSize">
562 <size>
563 <width>32</width>
564 <height>0</height>
565 </size>
566 </property>
567 <property name="maximumSize">
568 <size>
569 <width>40</width>
570 <height>16777215</height>
571 </size>
572 </property>
573 <property name="text">
574 <string/>
575 </property>
576 </widget>
577 </item>
578 <item row="0" column="3">
579 <spacer name="horizontalSpacer_4">
580 <property name="orientation">
581 <enum>Qt::Horizontal</enum>
582 </property>
583 <property name="sizeType">
584 <enum>QSizePolicy::Fixed</enum>
585 </property>
586 <property name="sizeHint" stdset="0">
587 <size>
588 <width>20</width>
589 <height>20</height>
590 </size>
591 </property>
592 </spacer>
593 </item>
594 </layout>
595 </widget>
596 </item>
597 <item row="1" column="0">
598 <widget class="QGroupBox" name="LStick">
599 <property name="title">
600 <string>Left Stick</string>
601 </property>
602 <property name="flat">
603 <bool>false</bool>
604 </property>
605 <property name="checkable">
606 <bool>false</bool>
607 </property>
608 <layout class="QGridLayout" name="gridLayout_4">
609 <item row="1" column="1">
610 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
611 <item>
612 <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout">
613 <item>
614 <widget class="QLabel" name="labelLStickUp">
615 <property name="text">
616 <string>Up:</string>
617 </property>
618 </widget>
619 </item>
620 </layout>
621 </item>
622 <item>
623 <widget class="QPushButton" name="buttonLStickUp">
624 <property name="text">
625 <string/>
626 </property>
627 </widget>
628 </item>
629 </layout>
630 </item>
631 <item row="0" column="2">
632 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
633 <item>
634 <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout">
635 <item>
636 <widget class="QLabel" name="labelLStickRight">
637 <property name="text">
638 <string>Right:</string>
639 </property>
640 </widget>
641 </item>
642 </layout>
643 </item>
644 <item>
645 <widget class="QPushButton" name="buttonLStickRight">
646 <property name="text">
647 <string/>
648 </property>
649 </widget>
650 </item>
651 </layout>
652 </item>
653 <item row="4" column="1" colspan="2">
654 <widget class="QPushButton" name="buttonLStickAnalog">
655 <property name="text">
656 <string>Set Analog Stick</string>
657 </property>
658 </widget>
659 </item>
660 <item row="0" column="1">
661 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
662 <item>
663 <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2">
664 <item>
665 <widget class="QLabel" name="labelLStickLeft">
666 <property name="text">
667 <string>Left:</string>
668 </property>
669 </widget>
670 </item>
671 </layout>
672 </item>
673 <item>
674 <widget class="QPushButton" name="buttonLStickLeft">
675 <property name="text">
676 <string/>
677 </property>
678 </widget>
679 </item>
680 </layout>
681 </item>
682 <item row="1" column="2">
683 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
684 <item>
685 <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout">
686 <item>
687 <widget class="QLabel" name="labelLStickDown">
688 <property name="text">
689 <string>Down:</string>
690 </property>
691 </widget>
692 </item>
693 </layout>
694 </item>
695 <item>
696 <widget class="QPushButton" name="buttonLStickDown">
697 <property name="text">
698 <string/>
699 </property>
700 </widget>
701 </item>
702 </layout>
703 </item>
704 <item row="3" column="2">
705 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
706 <item>
707 <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout">
708 <item>
709 <widget class="QLabel" name="labelLStickMod">
710 <property name="text">
711 <string>Modifier:</string>
712 </property>
713 </widget>
714 </item>
715 </layout>
716 </item>
717 <item>
718 <widget class="QPushButton" name="buttonLStickMod">
719 <property name="text">
720 <string/>
721 </property>
722 </widget>
723 </item>
724 </layout>
725 </item>
726 <item row="3" column="1">
727 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0">
728 <item>
729 <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout">
730 <item>
731 <widget class="QLabel" name="labelLStickPressed">
732 <property name="text">
733 <string>Pressed:</string>
734 </property>
735 </widget>
736 </item>
737 </layout>
738 </item>
739 <item>
740 <widget class="QPushButton" name="buttonLStick">
741 <property name="text">
742 <string/>
743 </property>
744 </widget>
745 </item>
746 </layout>
747 </item>
748 </layout>
749 </widget>
750 </item>
751 <item row="3" column="0">
752 <widget class="QGroupBox" name="shoulderButtons">
753 <property name="title">
754 <string>Shoulder Buttons</string>
755 </property>
756 <property name="flat">
757 <bool>false</bool>
758 </property>
759 <property name="checkable">
760 <bool>false</bool>
761 </property>
762 <layout class="QGridLayout" name="gridLayout_3">
763 <item row="3" column="0">
764 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
765 <item>
766 <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
767 <item>
768 <widget class="QLabel" name="labelSL">
769 <property name="text">
770 <string>SL:</string>
771 </property>
772 </widget>
773 </item>
774 </layout>
775 </item>
776 <item>
777 <widget class="QPushButton" name="buttonSL">
778 <property name="text">
779 <string/>
780 </property>
781 </widget>
782 </item>
783 </layout>
784 </item>
785 <item row="2" column="1">
786 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
787 <item>
788 <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
789 <item>
790 <widget class="QLabel" name="labelZR">
791 <property name="text">
792 <string>ZR:</string>
793 </property>
794 </widget>
795 </item>
796 </layout>
797 </item>
798 <item>
799 <widget class="QPushButton" name="buttonZR">
800 <property name="text">
801 <string/>
802 </property>
803 </widget>
804 </item>
805 </layout>
806 </item>
807 <item row="3" column="1">
808 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
809 <item>
810 <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
811 <item>
812 <widget class="QLabel" name="labelSR">
813 <property name="text">
814 <string>SR:</string>
815 </property>
816 </widget>
817 </item>
818 </layout>
819 </item>
820 <item>
821 <widget class="QPushButton" name="buttonSR">
822 <property name="text">
823 <string/>
824 </property>
825 </widget>
826 </item>
827 </layout>
828 </item>
829 <item row="0" column="1">
830 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
831 <item>
832 <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
833 <item>
834 <widget class="QLabel" name="labelZL">
835 <property name="text">
836 <string>ZL:</string>
837 </property>
838 </widget>
839 </item>
840 </layout>
841 </item>
842 <item>
843 <widget class="QPushButton" name="buttonZL">
844 <property name="text">
845 <string/>
846 </property>
847 </widget>
848 </item>
849 </layout>
850 </item>
851 <item row="0" column="0">
852 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
853 <item>
854 <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
855 <item>
856 <widget class="QLabel" name="labelL">
857 <property name="text">
858 <string>L:</string>
859 </property>
860 </widget>
861 </item>
862 </layout>
863 </item>
864 <item>
865 <widget class="QPushButton" name="buttonL">
866 <property name="text">
867 <string/>
868 </property>
869 </widget>
870 </item>
871 </layout>
872 </item>
873 <item row="2" column="0">
874 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
875 <item>
876 <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">
877 <item>
878 <widget class="QLabel" name="labelR">
879 <property name="text">
880 <string>R:</string>
881 </property>
882 </widget>
883 </item>
884 </layout>
885 </item>
886 <item>
887 <widget class="QPushButton" name="buttonR">
888 <property name="text">
889 <string/>
890 </property>
891 </widget>
892 </item>
893 </layout>
894 </item>
895 </layout>
896 </widget>
897 </item>
898 <item row="3" column="1">
899 <widget class="QGroupBox" name="misc">
900 <property name="title">
901 <string>Misc.</string>
902 </property>
903 <property name="flat">
904 <bool>false</bool>
905 </property>
906 <property name="checkable">
907 <bool>false</bool>
908 </property>
909 <layout class="QGridLayout" name="gridLayout_6">
910 <item row="1" column="0">
911 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
912 <item>
913 <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout">
914 <item>
915 <widget class="QLabel" name="labelMinus">
916 <property name="text">
917 <string>Minus:</string>
918 </property>
919 </widget>
920 </item>
921 </layout>
922 </item>
923 <item>
924 <widget class="QPushButton" name="buttonMinus">
925 <property name="text">
926 <string/>
927 </property>
928 </widget>
929 </item>
930 </layout>
931 </item>
932 <item row="3" column="1">
933 <spacer name="verticalSpacer_2">
934 <property name="orientation">
935 <enum>Qt::Vertical</enum>
936 </property>
937 <property name="sizeHint" stdset="0">
938 <size>
939 <width>20</width>
940 <height>40</height>
941 </size>
942 </property>
943 </spacer>
944 </item>
945 <item row="0" column="0">
946 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
947 <item>
948 <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout">
949 <item>
950 <widget class="QLabel" name="labelPlus">
951 <property name="text">
952 <string>Plus:</string>
953 </property>
954 </widget>
955 </item>
956 </layout>
957 </item>
958 <item>
959 <widget class="QPushButton" name="buttonPlus">
960 <property name="text">
961 <string/>
962 </property>
963 </widget>
964 </item>
965 </layout>
966 </item>
967 <item row="0" column="1">
968 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
969 <item>
970 <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout">
971 <item>
972 <widget class="QLabel" name="labelHome">
973 <property name="text">
974 <string>Home:</string>
975 </property>
976 </widget>
977 </item>
978 </layout>
979 </item>
980 <item>
981 <widget class="QPushButton" name="buttonHome">
982 <property name="text">
983 <string/>
984 </property>
985 </widget>
986 </item>
987 </layout>
988 </item>
989 <item row="1" column="1">
990 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
991 <item>
992 <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout">
993 <item>
994 <widget class="QLabel" name="labelScreenshot">
995 <property name="text">
996 <string>Screen Capture:</string>
997 </property>
998 <property name="wordWrap">
999 <bool>false</bool>
1000 </property>
1001 </widget>
1002 </item>
1003 </layout>
1004 </item>
1005 <item>
1006 <widget class="QPushButton" name="buttonScreenshot">
1007 <property name="sizePolicy">
1008 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1009 <horstretch>0</horstretch>
1010 <verstretch>0</verstretch>
1011 </sizepolicy>
1012 </property>
1013 <property name="maximumSize">
1014 <size>
1015 <width>80</width>
1016 <height>16777215</height>
1017 </size>
1018 </property>
1019 <property name="text">
1020 <string/>
1021 </property>
1022 </widget>
1023 </item>
1024 </layout>
1025 </item>
1026 </layout>
1027 </widget>
1028 </item>
1029 </layout>
1030 </item>
1031 <item>
1032 <spacer name="verticalSpacer">
1033 <property name="orientation">
1034 <enum>Qt::Vertical</enum>
1035 </property>
1036 <property name="sizeHint" stdset="0">
1037 <size>
1038 <width>20</width>
1039 <height>40</height>
1040 </size>
1041 </property>
1042 </spacer>
1043 </item>
1044 <item>
1045 <layout class="QHBoxLayout" name="horizontalLayout"/>
1046 </item>
1047 <item>
1048 <layout class="QHBoxLayout" name="horizontalLayout_2">
1049 <item>
1050 <widget class="QPushButton" name="buttonClearAll">
1051 <property name="sizePolicy">
1052 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1053 <horstretch>0</horstretch>
1054 <verstretch>0</verstretch>
1055 </sizepolicy>
1056 </property>
1057 <property name="sizeIncrement">
1058 <size>
1059 <width>0</width>
1060 <height>0</height>
1061 </size>
1062 </property>
1063 <property name="baseSize">
1064 <size>
1065 <width>0</width>
1066 <height>0</height>
1067 </size>
1068 </property>
1069 <property name="layoutDirection">
1070 <enum>Qt::LeftToRight</enum>
1071 </property>
1072 <property name="text">
1073 <string>Clear All</string>
1074 </property>
1075 </widget>
1076 </item>
1077 <item>
1078 <widget class="QPushButton" name="buttonRestoreDefaults">
1079 <property name="sizePolicy">
1080 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1081 <horstretch>0</horstretch>
1082 <verstretch>0</verstretch>
1083 </sizepolicy>
1084 </property>
1085 <property name="sizeIncrement">
1086 <size>
1087 <width>0</width>
1088 <height>0</height>
1089 </size>
1090 </property>
1091 <property name="baseSize">
1092 <size>
1093 <width>0</width>
1094 <height>0</height>
1095 </size>
1096 </property>
1097 <property name="layoutDirection">
1098 <enum>Qt::LeftToRight</enum>
1099 </property>
1100 <property name="text">
1101 <string>Restore Defaults</string>
1102 </property>
1103 </widget>
1104 </item>
1105 <item>
1106 <spacer name="horizontalSpacer">
1107 <property name="orientation">
1108 <enum>Qt::Horizontal</enum>
1109 </property>
1110 <property name="sizeHint" stdset="0">
1111 <size>
1112 <width>40</width>
1113 <height>20</height>
1114 </size>
1115 </property>
1116 </spacer>
1117 </item>
1118 <item>
1119 <widget class="QDialogButtonBox" name="buttonBox">
1120 <property name="standardButtons">
1121 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
1122 </property>
1123 </widget>
1124 </item>
1125 </layout>
1126 </item>
1127 </layout>
1128 </widget>
1129 <resources/>
1130 <connections>
1131 <connection>
1132 <sender>buttonBox</sender>
1133 <signal>accepted()</signal>
1134 <receiver>ConfigureInputPlayer</receiver>
1135 <slot>accept()</slot>
1136 <hints>
1137 <hint type="sourcelabel">
1138 <x>371</x>
1139 <y>730</y>
1140 </hint>
1141 <hint type="destinationlabel">
1142 <x>229</x>
1143 <y>375</y>
1144 </hint>
1145 </hints>
1146 </connection>
1147 <connection>
1148 <sender>buttonBox</sender>
1149 <signal>rejected()</signal>
1150 <receiver>ConfigureInputPlayer</receiver>
1151 <slot>reject()</slot>
1152 <hints>
1153 <hint type="sourcelabel">
1154 <x>371</x>
1155 <y>730</y>
1156 </hint>
1157 <hint type="destinationlabel">
1158 <x>229</x>
1159 <y>375</y>
1160 </hint>
1161 </hints>
1162 </connection>
1163 </connections>
1164</ui>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
new file mode 100644
index 000000000..dab58fbaa
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -0,0 +1,213 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <utility>
8#include <QKeyEvent>
9#include <QMenu>
10#include <QMessageBox>
11#include <QTimer>
12#include "common/assert.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
15#include "ui_configure_mouse_advanced.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_mouse_advanced.h"
18
19static QString GetKeyName(int key_code) {
20 switch (key_code) {
21 case Qt::Key_Shift:
22 return QObject::tr("Shift");
23 case Qt::Key_Control:
24 return QObject::tr("Ctrl");
25 case Qt::Key_Alt:
26 return QObject::tr("Alt");
27 case Qt::Key_Meta:
28 return "";
29 default:
30 return QKeySequence(key_code).toString();
31 }
32}
33
34static QString ButtonToText(const Common::ParamPackage& param) {
35 if (!param.Has("engine")) {
36 return QObject::tr("[not set]");
37 } else if (param.Get("engine", "") == "keyboard") {
38 return GetKeyName(param.Get("code", 0));
39 } else if (param.Get("engine", "") == "sdl") {
40 if (param.Has("hat")) {
41 return QString(QObject::tr("Hat %1 %2"))
42 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
43 }
44 if (param.Has("axis")) {
45 return QString(QObject::tr("Axis %1%2"))
46 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
47 }
48 if (param.Has("button")) {
49 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
50 }
51 return QString();
52 } else {
53 return QObject::tr("[unknown]");
54 }
55}
56
57ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
58 : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()),
59 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
60 ui->setupUi(this);
61 setFocusPolicy(Qt::ClickFocus);
62
63 button_map = {
64 ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button,
65 };
66
67 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
68 if (!button_map[button_id])
69 continue;
70 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
71 connect(button_map[button_id], &QPushButton::released, [=]() {
72 handleClick(
73 button_map[button_id],
74 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
75 InputCommon::Polling::DeviceType::Button);
76 });
77 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
78 [=](const QPoint& menu_location) {
79 QMenu context_menu;
80 context_menu.addAction(tr("Clear"), [&] {
81 buttons_param[button_id].Clear();
82 button_map[button_id]->setText(tr("[not set]"));
83 });
84 context_menu.addAction(tr("Restore Default"), [&] {
85 buttons_param[button_id] =
86 Common::ParamPackage{InputCommon::GenerateKeyboardParam(
87 Config::default_mouse_buttons[button_id])};
88 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
89 });
90 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
91 });
92 }
93
94 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
95 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
96
97 timeout_timer->setSingleShot(true);
98 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
99
100 connect(poll_timer.get(), &QTimer::timeout, [this]() {
101 Common::ParamPackage params;
102 for (auto& poller : device_pollers) {
103 params = poller->GetNextInput();
104 if (params.Has("engine")) {
105 setPollingResult(params, false);
106 return;
107 }
108 }
109 });
110
111 loadConfiguration();
112 resize(0, 0);
113}
114
115ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
116
117void ConfigureMouseAdvanced::applyConfiguration() {
118 std::transform(buttons_param.begin(), buttons_param.end(),
119 Settings::values.mouse_buttons.begin(),
120 [](const Common::ParamPackage& param) { return param.Serialize(); });
121}
122
123void ConfigureMouseAdvanced::loadConfiguration() {
124 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
125 buttons_param.begin(),
126 [](const std::string& str) { return Common::ParamPackage(str); });
127 updateButtonLabels();
128}
129
130void ConfigureMouseAdvanced::restoreDefaults() {
131 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
132 buttons_param[button_id] = Common::ParamPackage{
133 InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
134 }
135
136 updateButtonLabels();
137}
138
139void ConfigureMouseAdvanced::ClearAll() {
140 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
141 if (button_map[i] && button_map[i]->isEnabled())
142 buttons_param[i].Clear();
143 }
144
145 updateButtonLabels();
146}
147
148void ConfigureMouseAdvanced::updateButtonLabels() {
149 for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
150 button_map[button]->setText(ButtonToText(buttons_param[button]));
151 }
152}
153
154void ConfigureMouseAdvanced::handleClick(
155 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
156 InputCommon::Polling::DeviceType type) {
157 button->setText(tr("[press key]"));
158 button->setFocus();
159
160 const auto iter = std::find(button_map.begin(), button_map.end(), button);
161 ASSERT(iter != button_map.end());
162 const auto index = std::distance(button_map.begin(), iter);
163 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
164
165 input_setter = new_input_setter;
166
167 device_pollers = InputCommon::Polling::GetPollers(type);
168
169 // Keyboard keys can only be used as button devices
170 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
171
172 for (auto& poller : device_pollers) {
173 poller->Start();
174 }
175
176 grabKeyboard();
177 grabMouse();
178 timeout_timer->start(5000); // Cancel after 5 seconds
179 poll_timer->start(200); // Check for new inputs every 200ms
180}
181
182void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params, bool abort) {
183 releaseKeyboard();
184 releaseMouse();
185 timeout_timer->stop();
186 poll_timer->stop();
187 for (auto& poller : device_pollers) {
188 poller->Stop();
189 }
190
191 if (!abort) {
192 (*input_setter)(params);
193 }
194
195 updateButtonLabels();
196 input_setter = std::nullopt;
197}
198
199void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
200 if (!input_setter || !event)
201 return;
202
203 if (event->key() != Qt::Key_Escape) {
204 if (want_keyboard_keys) {
205 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
206 false);
207 } else {
208 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
209 return;
210 }
211 }
212 setPollingResult({}, true);
213}
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
new file mode 100644
index 000000000..218df2bda
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_advanced.h
@@ -0,0 +1,68 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <optional>
9#include <QDialog>
10#include <QWidget>
11#include "core/settings.h"
12
13class QCheckBox;
14class QPushButton;
15class QTimer;
16
17namespace Ui {
18class ConfigureMouseAdvanced;
19}
20
21class ConfigureMouseAdvanced : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureMouseAdvanced(QWidget* parent);
26 ~ConfigureMouseAdvanced() override;
27
28 void applyConfiguration();
29
30private:
31 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
32
33 /// This will be the the setting function when an input is awaiting configuration.
34 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
35
36 std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
37 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
38
39 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
40
41 std::unique_ptr<QTimer> timeout_timer;
42 std::unique_ptr<QTimer> poll_timer;
43
44 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
45 /// keyboard events are ignored.
46 bool want_keyboard_keys = false;
47
48 /// Load configuration settings.
49 void loadConfiguration();
50 /// Restore all buttons to their default values.
51 void restoreDefaults();
52 /// Clear all input configuration
53 void ClearAll();
54
55 /// Update UI to reflect current configuration.
56 void updateButtonLabels();
57
58 /// Called when the button was pressed.
59 void handleClick(QPushButton* button,
60 std::function<void(const Common::ParamPackage&)> new_input_setter,
61 InputCommon::Polling::DeviceType type);
62
63 /// Finish polling and configure input using the input_setter
64 void setPollingResult(const Common::ParamPackage& params, bool abort);
65
66 /// Handle key press events.
67 void keyPressEvent(QKeyEvent* event) override;
68};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
new file mode 100644
index 000000000..08245ecf0
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_advanced.ui
@@ -0,0 +1,261 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureMouseAdvanced</class>
4 <widget class="QDialog" name="ConfigureMouseAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>250</width>
10 <height>261</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Mouse</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QGroupBox" name="gridGroupBox">
19 <property name="title">
20 <string>Mouse Buttons</string>
21 </property>
22 <layout class="QGridLayout" name="gridLayout">
23 <item row="0" column="4">
24 <spacer name="horizontalSpacer_2">
25 <property name="orientation">
26 <enum>Qt::Horizontal</enum>
27 </property>
28 <property name="sizeType">
29 <enum>QSizePolicy::Fixed</enum>
30 </property>
31 <property name="sizeHint" stdset="0">
32 <size>
33 <width>20</width>
34 <height>20</height>
35 </size>
36 </property>
37 </spacer>
38 </item>
39 <item row="0" column="3">
40 <layout class="QVBoxLayout" name="verticalLayout_4">
41 <item>
42 <layout class="QHBoxLayout" name="horizontalLayout_3">
43 <item>
44 <widget class="QLabel" name="label_3">
45 <property name="text">
46 <string>Right:</string>
47 </property>
48 </widget>
49 </item>
50 </layout>
51 </item>
52 <item>
53 <widget class="QPushButton" name="right_button">
54 <property name="minimumSize">
55 <size>
56 <width>75</width>
57 <height>0</height>
58 </size>
59 </property>
60 <property name="text">
61 <string/>
62 </property>
63 </widget>
64 </item>
65 </layout>
66 </item>
67 <item row="0" column="0">
68 <spacer name="horizontalSpacer">
69 <property name="orientation">
70 <enum>Qt::Horizontal</enum>
71 </property>
72 <property name="sizeType">
73 <enum>QSizePolicy::Fixed</enum>
74 </property>
75 <property name="sizeHint" stdset="0">
76 <size>
77 <width>20</width>
78 <height>20</height>
79 </size>
80 </property>
81 </spacer>
82 </item>
83 <item row="2" column="1">
84 <layout class="QVBoxLayout" name="verticalLayout_3">
85 <item>
86 <layout class="QHBoxLayout" name="horizontalLayout_2">
87 <item>
88 <widget class="QLabel" name="label_2">
89 <property name="text">
90 <string>Middle:</string>
91 </property>
92 </widget>
93 </item>
94 </layout>
95 </item>
96 <item>
97 <widget class="QPushButton" name="middle_button">
98 <property name="text">
99 <string/>
100 </property>
101 </widget>
102 </item>
103 </layout>
104 </item>
105 <item row="3" column="1">
106 <layout class="QVBoxLayout" name="verticalLayout_5">
107 <item>
108 <layout class="QHBoxLayout" name="horizontalLayout_4">
109 <item>
110 <widget class="QLabel" name="label_4">
111 <property name="minimumSize">
112 <size>
113 <width>54</width>
114 <height>0</height>
115 </size>
116 </property>
117 <property name="text">
118 <string>Back:</string>
119 </property>
120 </widget>
121 </item>
122 </layout>
123 </item>
124 <item>
125 <widget class="QPushButton" name="back_button">
126 <property name="text">
127 <string/>
128 </property>
129 </widget>
130 </item>
131 </layout>
132 </item>
133 <item row="0" column="1">
134 <layout class="QVBoxLayout" name="verticalLayout_2">
135 <item>
136 <layout class="QHBoxLayout" name="horizontalLayout">
137 <item>
138 <widget class="QLabel" name="label">
139 <property name="text">
140 <string>Left:</string>
141 </property>
142 </widget>
143 </item>
144 </layout>
145 </item>
146 <item>
147 <widget class="QPushButton" name="left_button">
148 <property name="minimumSize">
149 <size>
150 <width>75</width>
151 <height>0</height>
152 </size>
153 </property>
154 <property name="text">
155 <string/>
156 </property>
157 </widget>
158 </item>
159 </layout>
160 </item>
161 <item row="3" column="3">
162 <layout class="QVBoxLayout" name="verticalLayout_6">
163 <item>
164 <layout class="QHBoxLayout" name="horizontalLayout_5">
165 <item>
166 <widget class="QLabel" name="label_5">
167 <property name="text">
168 <string>Forward:</string>
169 </property>
170 </widget>
171 </item>
172 </layout>
173 </item>
174 <item>
175 <widget class="QPushButton" name="forward_button">
176 <property name="text">
177 <string/>
178 </property>
179 </widget>
180 </item>
181 </layout>
182 </item>
183 </layout>
184 </widget>
185 </item>
186 <item>
187 <layout class="QHBoxLayout" name="horizontalLayout_6">
188 <item>
189 <widget class="QPushButton" name="buttonClearAll">
190 <property name="text">
191 <string>Clear All</string>
192 </property>
193 </widget>
194 </item>
195 <item>
196 <widget class="QPushButton" name="buttonRestoreDefaults">
197 <property name="text">
198 <string>Restore Defaults</string>
199 </property>
200 </widget>
201 </item>
202 <item>
203 <spacer name="horizontalSpacer_3">
204 <property name="orientation">
205 <enum>Qt::Horizontal</enum>
206 </property>
207 <property name="sizeHint" stdset="0">
208 <size>
209 <width>40</width>
210 <height>20</height>
211 </size>
212 </property>
213 </spacer>
214 </item>
215 </layout>
216 </item>
217 <item>
218 <widget class="QDialogButtonBox" name="buttonBox">
219 <property name="standardButtons">
220 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
221 </property>
222 </widget>
223 </item>
224 </layout>
225 </widget>
226 <resources/>
227 <connections>
228 <connection>
229 <sender>buttonBox</sender>
230 <signal>accepted()</signal>
231 <receiver>ConfigureMouseAdvanced</receiver>
232 <slot>accept()</slot>
233 <hints>
234 <hint type="sourcelabel">
235 <x>124</x>
236 <y>266</y>
237 </hint>
238 <hint type="destinationlabel">
239 <x>124</x>
240 <y>143</y>
241 </hint>
242 </hints>
243 </connection>
244 <connection>
245 <sender>buttonBox</sender>
246 <signal>rejected()</signal>
247 <receiver>ConfigureMouseAdvanced</receiver>
248 <slot>reject()</slot>
249 <hints>
250 <hint type="sourcelabel">
251 <x>124</x>
252 <y>266</y>
253 </hint>
254 <hint type="destinationlabel">
255 <x>124</x>
256 <y>143</y>
257 </hint>
258 </hints>
259 </connection>
260 </connections>
261</ui>
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
new file mode 100644
index 000000000..9c1561e9d
--- /dev/null
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
@@ -0,0 +1,42 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include "ui_configure_touchscreen_advanced.h"
7#include "yuzu/configuration/config.h"
8#include "yuzu/configuration/configure_touchscreen_advanced.h"
9
10ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
11 : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchscreenAdvanced>()) {
12 ui->setupUi(this);
13
14 connect(ui->restore_defaults_button, &QPushButton::pressed, this,
15 &ConfigureTouchscreenAdvanced::restoreDefaults);
16
17 loadConfiguration();
18 resize(0, 0);
19}
20
21ConfigureTouchscreenAdvanced::~ConfigureTouchscreenAdvanced() = default;
22
23void ConfigureTouchscreenAdvanced::applyConfiguration() {
24 Settings::values.touchscreen.finger = ui->finger_box->value();
25 Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value();
26 Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value();
27 Settings::values.touchscreen.rotation_angle = ui->angle_box->value();
28}
29
30void ConfigureTouchscreenAdvanced::loadConfiguration() {
31 ui->finger_box->setValue(Settings::values.touchscreen.finger);
32 ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x);
33 ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y);
34 ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle);
35}
36
37void ConfigureTouchscreenAdvanced::restoreDefaults() {
38 ui->finger_box->setValue(0);
39 ui->diameter_x_box->setValue(15);
40 ui->diameter_y_box->setValue(15);
41 ui->angle_box->setValue(0);
42}
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h
new file mode 100644
index 000000000..41cd255fb
--- /dev/null
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.h
@@ -0,0 +1,32 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QDialog>
9#include <QWidget>
10#include "yuzu/configuration/config.h"
11
12namespace Ui {
13class ConfigureTouchscreenAdvanced;
14}
15
16class ConfigureTouchscreenAdvanced : public QDialog {
17 Q_OBJECT
18
19public:
20 explicit ConfigureTouchscreenAdvanced(QWidget* parent);
21 ~ConfigureTouchscreenAdvanced() override;
22
23 void applyConfiguration();
24
25private:
26 /// Load configuration settings.
27 void loadConfiguration();
28 /// Restore all buttons to their default values.
29 void restoreDefaults();
30
31 std::unique_ptr<Ui::ConfigureTouchscreenAdvanced> ui;
32};
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui
new file mode 100644
index 000000000..1171c2dd1
--- /dev/null
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui
@@ -0,0 +1,199 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureTouchscreenAdvanced</class>
4 <widget class="QDialog" name="ConfigureTouchscreenAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>298</width>
10 <height>339</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Touchscreen</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QLabel" name="label_2">
19 <property name="minimumSize">
20 <size>
21 <width>280</width>
22 <height>0</height>
23 </size>
24 </property>
25 <property name="text">
26 <string>Warning: The settings in this page affect the inner workings of yuzu's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</string>
27 </property>
28 <property name="wordWrap">
29 <bool>true</bool>
30 </property>
31 </widget>
32 </item>
33 <item>
34 <spacer name="verticalSpacer_2">
35 <property name="orientation">
36 <enum>Qt::Vertical</enum>
37 </property>
38 <property name="sizeType">
39 <enum>QSizePolicy::Fixed</enum>
40 </property>
41 <property name="sizeHint" stdset="0">
42 <size>
43 <width>20</width>
44 <height>20</height>
45 </size>
46 </property>
47 </spacer>
48 </item>
49 <item>
50 <widget class="QGroupBox" name="gridGroupBox">
51 <property name="title">
52 <string>Touch Parameters</string>
53 </property>
54 <layout class="QGridLayout" name="gridLayout">
55 <item row="0" column="0">
56 <spacer name="horizontalSpacer">
57 <property name="orientation">
58 <enum>Qt::Horizontal</enum>
59 </property>
60 <property name="sizeHint" stdset="0">
61 <size>
62 <width>40</width>
63 <height>20</height>
64 </size>
65 </property>
66 </spacer>
67 </item>
68 <item row="2" column="1">
69 <widget class="QLabel" name="label_4">
70 <property name="text">
71 <string>Touch Diameter Y</string>
72 </property>
73 </widget>
74 </item>
75 <item row="0" column="1">
76 <widget class="QLabel" name="label">
77 <property name="text">
78 <string>Finger</string>
79 </property>
80 </widget>
81 </item>
82 <item row="0" column="3">
83 <spacer name="horizontalSpacer_2">
84 <property name="orientation">
85 <enum>Qt::Horizontal</enum>
86 </property>
87 <property name="sizeHint" stdset="0">
88 <size>
89 <width>40</width>
90 <height>20</height>
91 </size>
92 </property>
93 </spacer>
94 </item>
95 <item row="1" column="1">
96 <widget class="QLabel" name="label_3">
97 <property name="text">
98 <string>Touch Diameter X</string>
99 </property>
100 </widget>
101 </item>
102 <item row="0" column="2">
103 <widget class="QSpinBox" name="finger_box">
104 <property name="minimumSize">
105 <size>
106 <width>80</width>
107 <height>0</height>
108 </size>
109 </property>
110 </widget>
111 </item>
112 <item row="3" column="1">
113 <widget class="QLabel" name="label_5">
114 <property name="text">
115 <string>Rotational Angle</string>
116 </property>
117 </widget>
118 </item>
119 <item row="1" column="2">
120 <widget class="QSpinBox" name="diameter_x_box"/>
121 </item>
122 <item row="2" column="2">
123 <widget class="QSpinBox" name="diameter_y_box"/>
124 </item>
125 <item row="3" column="2">
126 <widget class="QSpinBox" name="angle_box"/>
127 </item>
128 </layout>
129 </widget>
130 </item>
131 <item>
132 <spacer name="verticalSpacer">
133 <property name="orientation">
134 <enum>Qt::Vertical</enum>
135 </property>
136 <property name="sizeHint" stdset="0">
137 <size>
138 <width>20</width>
139 <height>40</height>
140 </size>
141 </property>
142 </spacer>
143 </item>
144 <item>
145 <layout class="QHBoxLayout" name="horizontalLayout">
146 <item>
147 <widget class="QPushButton" name="restore_defaults_button">
148 <property name="text">
149 <string>Restore Defaults</string>
150 </property>
151 </widget>
152 </item>
153 <item>
154 <widget class="QDialogButtonBox" name="buttonBox">
155 <property name="standardButtons">
156 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
157 </property>
158 </widget>
159 </item>
160 </layout>
161 </item>
162 </layout>
163 </widget>
164 <resources/>
165 <connections>
166 <connection>
167 <sender>buttonBox</sender>
168 <signal>accepted()</signal>
169 <receiver>ConfigureTouchscreenAdvanced</receiver>
170 <slot>accept()</slot>
171 <hints>
172 <hint type="sourcelabel">
173 <x>140</x>
174 <y>318</y>
175 </hint>
176 <hint type="destinationlabel">
177 <x>140</x>
178 <y>169</y>
179 </hint>
180 </hints>
181 </connection>
182 <connection>
183 <sender>buttonBox</sender>
184 <signal>rejected()</signal>
185 <receiver>ConfigureTouchscreenAdvanced</receiver>
186 <slot>reject()</slot>
187 <hints>
188 <hint type="sourcelabel">
189 <x>140</x>
190 <y>318</y>
191 </hint>
192 <hint type="destinationlabel">
193 <x>140</x>
194 <y>169</y>
195 </hint>
196 </hints>
197 </connection>
198 </connections>
199</ui>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 999086e7f..9c6d150a5 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -8,9 +8,11 @@
8#include <thread> 8#include <thread>
9 9
10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
11#include "applets/software_keyboard.h"
11#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
12#include "core/file_sys/vfs_real.h" 13#include "core/file_sys/vfs_real.h"
13#include "core/hle/service/acc/profile_manager.h" 14#include "core/hle/service/acc/profile_manager.h"
15#include "core/hle/service/am/applets/applets.h"
14 16
15// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows 17// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
16// defines. 18// defines.
@@ -59,6 +61,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
59#include "core/file_sys/romfs.h" 61#include "core/file_sys/romfs.h"
60#include "core/file_sys/savedata_factory.h" 62#include "core/file_sys/savedata_factory.h"
61#include "core/file_sys/submission_package.h" 63#include "core/file_sys/submission_package.h"
64#include "core/frontend/applets/software_keyboard.h"
62#include "core/hle/kernel/process.h" 65#include "core/hle/kernel/process.h"
63#include "core/hle/service/filesystem/filesystem.h" 66#include "core/hle/service/filesystem/filesystem.h"
64#include "core/hle/service/filesystem/fsp_ldr.h" 67#include "core/hle/service/filesystem/fsp_ldr.h"
@@ -204,6 +207,27 @@ GMainWindow::~GMainWindow() {
204 delete render_window; 207 delete render_window;
205} 208}
206 209
210void GMainWindow::SoftwareKeyboardGetText(
211 const Core::Frontend::SoftwareKeyboardParameters& parameters) {
212 QtSoftwareKeyboardDialog dialog(this, parameters);
213 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
214 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
215 dialog.setWindowModality(Qt::WindowModal);
216 dialog.exec();
217
218 if (!dialog.GetStatus()) {
219 emit SoftwareKeyboardFinishedText(std::nullopt);
220 return;
221 }
222
223 emit SoftwareKeyboardFinishedText(dialog.GetText());
224}
225
226void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) {
227 QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message));
228 emit SoftwareKeyboardFinishedCheckDialog();
229}
230
207void GMainWindow::InitializeWidgets() { 231void GMainWindow::InitializeWidgets() {
208#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING 232#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
209 ui.action_Report_Compatibility->setVisible(true); 233 ui.action_Report_Compatibility->setVisible(true);
@@ -494,32 +518,18 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
494QStringList GMainWindow::GetUnsupportedGLExtensions() { 518QStringList GMainWindow::GetUnsupportedGLExtensions() {
495 QStringList unsupported_ext; 519 QStringList unsupported_ext;
496 520
497 if (!GLAD_GL_ARB_program_interface_query)
498 unsupported_ext.append("ARB_program_interface_query");
499 if (!GLAD_GL_ARB_separate_shader_objects)
500 unsupported_ext.append("ARB_separate_shader_objects");
501 if (!GLAD_GL_ARB_vertex_attrib_binding)
502 unsupported_ext.append("ARB_vertex_attrib_binding");
503 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) 521 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
504 unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev"); 522 unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
505 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) 523 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
506 unsupported_ext.append("ARB_texture_mirror_clamp_to_edge"); 524 unsupported_ext.append("ARB_texture_mirror_clamp_to_edge");
507 if (!GLAD_GL_ARB_base_instance)
508 unsupported_ext.append("ARB_base_instance");
509 if (!GLAD_GL_ARB_texture_storage)
510 unsupported_ext.append("ARB_texture_storage");
511 if (!GLAD_GL_ARB_multi_bind) 525 if (!GLAD_GL_ARB_multi_bind)
512 unsupported_ext.append("ARB_multi_bind"); 526 unsupported_ext.append("ARB_multi_bind");
513 if (!GLAD_GL_ARB_copy_image)
514 unsupported_ext.append("ARB_copy_image");
515 527
516 // Extensions required to support some texture formats. 528 // Extensions required to support some texture formats.
517 if (!GLAD_GL_EXT_texture_compression_s3tc) 529 if (!GLAD_GL_EXT_texture_compression_s3tc)
518 unsupported_ext.append("EXT_texture_compression_s3tc"); 530 unsupported_ext.append("EXT_texture_compression_s3tc");
519 if (!GLAD_GL_ARB_texture_compression_rgtc) 531 if (!GLAD_GL_ARB_texture_compression_rgtc)
520 unsupported_ext.append("ARB_texture_compression_rgtc"); 532 unsupported_ext.append("ARB_texture_compression_rgtc");
521 if (!GLAD_GL_ARB_texture_compression_bptc)
522 unsupported_ext.append("ARB_texture_compression_bptc");
523 if (!GLAD_GL_ARB_depth_buffer_float) 533 if (!GLAD_GL_ARB_depth_buffer_float)
524 unsupported_ext.append("ARB_depth_buffer_float"); 534 unsupported_ext.append("ARB_depth_buffer_float");
525 535
@@ -538,8 +548,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
538 render_window->MakeCurrent(); 548 render_window->MakeCurrent();
539 549
540 if (!gladLoadGL()) { 550 if (!gladLoadGL()) {
541 QMessageBox::critical(this, tr("Error while initializing OpenGL 3.3 Core!"), 551 QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"),
542 tr("Your GPU may not support OpenGL 3.3, or you do not " 552 tr("Your GPU may not support OpenGL 4.3, or you do not "
543 "have the latest graphics driver.")); 553 "have the latest graphics driver."));
544 return false; 554 return false;
545 } 555 }
@@ -559,6 +569,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
559 569
560 system.SetGPUDebugContext(debug_context); 570 system.SetGPUDebugContext(debug_context);
561 571
572 system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
573
562 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 574 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
563 575
564 const auto drd_callout = 576 const auto drd_callout =
@@ -1228,8 +1240,13 @@ void GMainWindow::OnMenuRecentFile() {
1228 1240
1229void GMainWindow::OnStartGame() { 1241void GMainWindow::OnStartGame() {
1230 emu_thread->SetRunning(true); 1242 emu_thread->SetRunning(true);
1243
1244 qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
1245 "Core::Frontend::SoftwareKeyboardParameters");
1231 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); 1246 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
1232 qRegisterMetaType<std::string>("std::string"); 1247 qRegisterMetaType<std::string>("std::string");
1248 qRegisterMetaType<std::optional<std::u16string>>("std::optional<std::u16string>");
1249
1233 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); 1250 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
1234 1251
1235 ui.action_Start->setEnabled(false); 1252 ui.action_Start->setEnabled(false);
@@ -1341,7 +1358,13 @@ void GMainWindow::OnConfigure() {
1341 UpdateUITheme(); 1358 UpdateUITheme();
1342 if (UISettings::values.enable_discord_presence != old_discord_presence) 1359 if (UISettings::values.enable_discord_presence != old_discord_presence)
1343 SetDiscordEnabled(UISettings::values.enable_discord_presence); 1360 SetDiscordEnabled(UISettings::values.enable_discord_presence);
1344 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1361
1362 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1363 if (reload) {
1364 game_list->PopulateAsync(UISettings::values.gamedir,
1365 UISettings::values.gamedir_deepscan);
1366 }
1367
1345 config->Save(); 1368 config->Save();
1346 } 1369 }
1347} 1370}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 929250e8c..674e73412 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -29,6 +29,10 @@ class ProfilerWidget;
29class WaitTreeWidget; 29class WaitTreeWidget;
30enum class GameListOpenTarget; 30enum class GameListOpenTarget;
31 31
32namespace Core::Frontend {
33struct SoftwareKeyboardParameters;
34} // namespace Core::Frontend
35
32namespace FileSys { 36namespace FileSys {
33class RegisteredCacheUnion; 37class RegisteredCacheUnion;
34class VfsFilesystem; 38class VfsFilesystem;
@@ -95,6 +99,13 @@ signals:
95 // Signal that tells widgets to update icons to use the current theme 99 // Signal that tells widgets to update icons to use the current theme
96 void UpdateThemedIcons(); 100 void UpdateThemedIcons();
97 101
102 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
103 void SoftwareKeyboardFinishedCheckDialog();
104
105public slots:
106 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
107 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
108
98private: 109private:
99 void InitializeWidgets(); 110 void InitializeWidgets();
100 void InitializeDebugWidgets(); 111 void InitializeDebugWidgets();
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 32a0d813c..e80aebc0a 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
8#include <vector> 9#include <vector>
9#include <QByteArray> 10#include <QByteArray>
10#include <QString> 11#include <QString>
@@ -63,6 +64,7 @@ struct Values {
63 uint32_t icon_size; 64 uint32_t icon_size;
64 uint8_t row_1_text_id; 65 uint8_t row_1_text_id;
65 uint8_t row_2_text_id; 66 uint8_t row_2_text_id;
67 std::atomic_bool is_game_list_reload_pending{false};
66}; 68};
67 69
68extern Values values; 70extern Values values;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 9cc409fd5..097c1fbe3 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -65,54 +65,246 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>
65 }, 65 },
66}}; 66}};
67 67
68static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = {
69 SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE,
70 SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS,
71};
72
73static const std::array<int, 0x8A> keyboard_keys = {
74 0,
75 0,
76 0,
77 0,
78 SDL_SCANCODE_A,
79 SDL_SCANCODE_B,
80 SDL_SCANCODE_C,
81 SDL_SCANCODE_D,
82 SDL_SCANCODE_E,
83 SDL_SCANCODE_F,
84 SDL_SCANCODE_G,
85 SDL_SCANCODE_H,
86 SDL_SCANCODE_I,
87 SDL_SCANCODE_J,
88 SDL_SCANCODE_K,
89 SDL_SCANCODE_L,
90 SDL_SCANCODE_M,
91 SDL_SCANCODE_N,
92 SDL_SCANCODE_O,
93 SDL_SCANCODE_P,
94 SDL_SCANCODE_Q,
95 SDL_SCANCODE_R,
96 SDL_SCANCODE_S,
97 SDL_SCANCODE_T,
98 SDL_SCANCODE_U,
99 SDL_SCANCODE_V,
100 SDL_SCANCODE_W,
101 SDL_SCANCODE_X,
102 SDL_SCANCODE_Y,
103 SDL_SCANCODE_Z,
104 SDL_SCANCODE_1,
105 SDL_SCANCODE_2,
106 SDL_SCANCODE_3,
107 SDL_SCANCODE_4,
108 SDL_SCANCODE_5,
109 SDL_SCANCODE_6,
110 SDL_SCANCODE_7,
111 SDL_SCANCODE_8,
112 SDL_SCANCODE_9,
113 SDL_SCANCODE_0,
114 SDL_SCANCODE_RETURN,
115 SDL_SCANCODE_ESCAPE,
116 SDL_SCANCODE_BACKSPACE,
117 SDL_SCANCODE_TAB,
118 SDL_SCANCODE_SPACE,
119 SDL_SCANCODE_MINUS,
120 SDL_SCANCODE_EQUALS,
121 SDL_SCANCODE_LEFTBRACKET,
122 SDL_SCANCODE_RIGHTBRACKET,
123 SDL_SCANCODE_BACKSLASH,
124 0,
125 SDL_SCANCODE_SEMICOLON,
126 SDL_SCANCODE_APOSTROPHE,
127 SDL_SCANCODE_GRAVE,
128 SDL_SCANCODE_COMMA,
129 SDL_SCANCODE_PERIOD,
130 SDL_SCANCODE_SLASH,
131 SDL_SCANCODE_CAPSLOCK,
132
133 SDL_SCANCODE_F1,
134 SDL_SCANCODE_F2,
135 SDL_SCANCODE_F3,
136 SDL_SCANCODE_F4,
137 SDL_SCANCODE_F5,
138 SDL_SCANCODE_F6,
139 SDL_SCANCODE_F7,
140 SDL_SCANCODE_F8,
141 SDL_SCANCODE_F9,
142 SDL_SCANCODE_F10,
143 SDL_SCANCODE_F11,
144 SDL_SCANCODE_F12,
145
146 0,
147 SDL_SCANCODE_SCROLLLOCK,
148 SDL_SCANCODE_PAUSE,
149 SDL_SCANCODE_INSERT,
150 SDL_SCANCODE_HOME,
151 SDL_SCANCODE_PAGEUP,
152 SDL_SCANCODE_DELETE,
153 SDL_SCANCODE_END,
154 SDL_SCANCODE_PAGEDOWN,
155 SDL_SCANCODE_RIGHT,
156 SDL_SCANCODE_LEFT,
157 SDL_SCANCODE_DOWN,
158 SDL_SCANCODE_UP,
159
160 SDL_SCANCODE_NUMLOCKCLEAR,
161 SDL_SCANCODE_KP_DIVIDE,
162 SDL_SCANCODE_KP_MULTIPLY,
163 SDL_SCANCODE_KP_MINUS,
164 SDL_SCANCODE_KP_PLUS,
165 SDL_SCANCODE_KP_ENTER,
166 SDL_SCANCODE_KP_1,
167 SDL_SCANCODE_KP_2,
168 SDL_SCANCODE_KP_3,
169 SDL_SCANCODE_KP_4,
170 SDL_SCANCODE_KP_5,
171 SDL_SCANCODE_KP_6,
172 SDL_SCANCODE_KP_7,
173 SDL_SCANCODE_KP_8,
174 SDL_SCANCODE_KP_9,
175 SDL_SCANCODE_KP_0,
176 SDL_SCANCODE_KP_PERIOD,
177
178 0,
179 0,
180 SDL_SCANCODE_POWER,
181 SDL_SCANCODE_KP_EQUALS,
182
183 SDL_SCANCODE_F13,
184 SDL_SCANCODE_F14,
185 SDL_SCANCODE_F15,
186 SDL_SCANCODE_F16,
187 SDL_SCANCODE_F17,
188 SDL_SCANCODE_F18,
189 SDL_SCANCODE_F19,
190 SDL_SCANCODE_F20,
191 SDL_SCANCODE_F21,
192 SDL_SCANCODE_F22,
193 SDL_SCANCODE_F23,
194 SDL_SCANCODE_F24,
195
196 0,
197 SDL_SCANCODE_HELP,
198 SDL_SCANCODE_MENU,
199 0,
200 0,
201 0,
202 0,
203 0,
204 0,
205 0,
206 0,
207 0,
208 0,
209 0,
210 0,
211 SDL_SCANCODE_KP_COMMA,
212 SDL_SCANCODE_KP_LEFTPAREN,
213 SDL_SCANCODE_KP_RIGHTPAREN,
214 0,
215 0,
216 0,
217 0,
218};
219
220static const std::array<int, 8> keyboard_mods{
221 SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI,
222 SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
223};
224
68void Config::ReadValues() { 225void Config::ReadValues() {
69 // Controls 226 // Controls
227 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
228 const auto group = fmt::format("ControlsP{}", p);
229 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
230 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
231 Settings::values.players[p].buttons[i] =
232 sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
233 if (Settings::values.players[p].buttons[i].empty())
234 Settings::values.players[p].buttons[i] = default_param;
235 }
236
237 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
238 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
239 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
240 default_analogs[i][3], default_analogs[i][4], 0.5f);
241 Settings::values.players[p].analogs[i] =
242 sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
243 if (Settings::values.players[p].analogs[i].empty())
244 Settings::values.players[p].analogs[i] = default_param;
245 }
246 }
247
248 Settings::values.mouse_enabled =
249 sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false);
250 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
251 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
252 Settings::values.mouse_buttons[i] = sdl2_config->Get(
253 "ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i],
254 default_param);
255 if (Settings::values.mouse_buttons[i].empty())
256 Settings::values.mouse_buttons[i] = default_param;
257 }
258
259 Settings::values.motion_device = sdl2_config->Get(
260 "ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
261
262 Settings::values.keyboard_enabled =
263 sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false);
264
265 Settings::values.debug_pad_enabled =
266 sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false);
70 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 267 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
71 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 268 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
72 Settings::values.buttons[i] = 269 Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
73 sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param); 270 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i],
74 if (Settings::values.buttons[i].empty()) 271 default_param);
75 Settings::values.buttons[i] = default_param; 272 if (Settings::values.debug_pad_buttons[i].empty())
273 Settings::values.debug_pad_buttons[i] = default_param;
76 } 274 }
77 275
78 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 276 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
79 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 277 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
80 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 278 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
81 default_analogs[i][3], default_analogs[i][4], 0.5f); 279 default_analogs[i][3], default_analogs[i][4], 0.5f);
82 Settings::values.analogs[i] = 280 Settings::values.debug_pad_analogs[i] = sdl2_config->Get(
83 sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param); 281 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i],
84 if (Settings::values.analogs[i].empty()) 282 default_param);
85 Settings::values.analogs[i] = default_param; 283 if (Settings::values.debug_pad_analogs[i].empty())
284 Settings::values.debug_pad_analogs[i] = default_param;
86 } 285 }
87 286
88 Settings::values.motion_device = sdl2_config->Get( 287 Settings::values.touchscreen.enabled =
89 "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); 288 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
90 Settings::values.touch_device = 289 Settings::values.touchscreen.device =
91 sdl2_config->Get("Controls", "touch_device", "engine:emu_window"); 290 sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window");
92 291 Settings::values.touchscreen.finger =
93 // Core 292 sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0);
94 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); 293 Settings::values.touchscreen.rotation_angle =
95 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); 294 sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0);
295 Settings::values.touchscreen.diameter_x =
296 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
297 Settings::values.touchscreen.diameter_y =
298 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
96 299
97 // Renderer 300 std::transform(keyboard_keys.begin(), keyboard_keys.end(),
98 Settings::values.resolution_factor = 301 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
99 (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0); 302 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
100 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 303 Settings::values.keyboard_keys.begin() +
101 Settings::values.frame_limit = 304 Settings::NativeKeyboard::LeftControlKey,
102 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 305 InputCommon::GenerateKeyboardParam);
103 Settings::values.use_accurate_gpu_emulation = 306 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
104 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); 307 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
105
106 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
107 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
108 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
109
110 // Audio
111 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
112 Settings::values.enable_audio_stretching =
113 sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
114 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
115 Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
116 308
117 // Data Storage 309 // Data Storage
118 Settings::values.use_virtual_sd = 310 Settings::values.use_virtual_sd =
@@ -139,6 +331,32 @@ void Config::ReadValues() {
139 Settings::values.rng_seed = std::nullopt; 331 Settings::values.rng_seed = std::nullopt;
140 } 332 }
141 333
334 // Core
335 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
336 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
337
338 // Renderer
339 Settings::values.resolution_factor =
340 (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0);
341 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
342 Settings::values.frame_limit =
343 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
344 Settings::values.use_accurate_gpu_emulation =
345 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
346
347 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
348 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
349 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
350
351 // Audio
352 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
353 Settings::values.enable_audio_stretching =
354 sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
355 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
356 Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
357
358 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
359
142 // Miscellaneous 360 // Miscellaneous
143 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 361 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
144 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); 362 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
@@ -148,6 +366,7 @@ void Config::ReadValues() {
148 Settings::values.gdbstub_port = 366 Settings::values.gdbstub_port =
149 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 367 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
150 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); 368 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
369 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
151 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); 370 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
152 371
153 // Web Service 372 // Web Service
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index ecf625e7b..d73669f36 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -206,6 +206,8 @@ log_filter = *:Trace
206# Port for listening to GDB connections. 206# Port for listening to GDB connections.
207use_gdbstub=false 207use_gdbstub=false
208gdbstub_port=24689 208gdbstub_port=24689
209# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
210dump_exefs=false
209# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them 211# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
210dump_nso=false 212dump_nso=false
211 213
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index a9ad92a80..2d6f8cced 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -111,32 +111,18 @@ void EmuWindow_SDL2::Fullscreen() {
111bool EmuWindow_SDL2::SupportsRequiredGLExtensions() { 111bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
112 std::vector<std::string> unsupported_ext; 112 std::vector<std::string> unsupported_ext;
113 113
114 if (!GLAD_GL_ARB_program_interface_query)
115 unsupported_ext.push_back("ARB_program_interface_query");
116 if (!GLAD_GL_ARB_separate_shader_objects)
117 unsupported_ext.push_back("ARB_separate_shader_objects");
118 if (!GLAD_GL_ARB_vertex_attrib_binding)
119 unsupported_ext.push_back("ARB_vertex_attrib_binding");
120 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) 114 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
121 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); 115 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
122 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) 116 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
123 unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); 117 unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
124 if (!GLAD_GL_ARB_base_instance)
125 unsupported_ext.push_back("ARB_base_instance");
126 if (!GLAD_GL_ARB_texture_storage)
127 unsupported_ext.push_back("ARB_texture_storage");
128 if (!GLAD_GL_ARB_multi_bind) 118 if (!GLAD_GL_ARB_multi_bind)
129 unsupported_ext.push_back("ARB_multi_bind"); 119 unsupported_ext.push_back("ARB_multi_bind");
130 if (!GLAD_GL_ARB_copy_image)
131 unsupported_ext.push_back("ARB_copy_image");
132 120
133 // Extensions required to support some texture formats. 121 // Extensions required to support some texture formats.
134 if (!GLAD_GL_EXT_texture_compression_s3tc) 122 if (!GLAD_GL_EXT_texture_compression_s3tc)
135 unsupported_ext.push_back("EXT_texture_compression_s3tc"); 123 unsupported_ext.push_back("EXT_texture_compression_s3tc");
136 if (!GLAD_GL_ARB_texture_compression_rgtc) 124 if (!GLAD_GL_ARB_texture_compression_rgtc)
137 unsupported_ext.push_back("ARB_texture_compression_rgtc"); 125 unsupported_ext.push_back("ARB_texture_compression_rgtc");
138 if (!GLAD_GL_ARB_texture_compression_bptc)
139 unsupported_ext.push_back("ARB_texture_compression_bptc");
140 if (!GLAD_GL_ARB_depth_buffer_float) 126 if (!GLAD_GL_ARB_depth_buffer_float)
141 unsupported_ext.push_back("ARB_depth_buffer_float"); 127 unsupported_ext.push_back("ARB_depth_buffer_float");
142 128
@@ -157,7 +143,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
157 exit(1); 143 exit(1);
158 } 144 }
159 145
160 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 146 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
161 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 147 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
162 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 148 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
163 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 149 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);