summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt2
-rw-r--r--externals/CMakeLists.txt9
m---------externals/xbyak0
-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/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.txt4
-rw-r--r--src/core/core.cpp119
-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/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/process.cpp1
-rw-r--r--src/core/hle/kernel/process.h1
-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.cpp4
-rw-r--r--src/core/hle/service/am/am.cpp46
-rw-r--r--src/core/hle/service/am/applets/applets.cpp6
-rw-r--r--src/core/hle/service/am/applets/applets.h50
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp24
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h7
-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/hid/controllers/debug_pad.cpp5
-rw-r--r--src/core/hle/service/ldr/ldr.cpp41
-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/vi/vi.cpp32
-rw-r--r--src/core/settings.h1
-rw-r--r--src/video_core/engines/maxwell_3d.cpp9
-rw-r--r--src/video_core/engines/maxwell_3d.h19
-rw-r--r--src/video_core/engines/shader_bytecode.h8
-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/renderer_opengl/gl_rasterizer.cpp44
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h29
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp31
-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.cpp892
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp29
-rw-r--r--src/video_core/renderer_opengl/gl_state.h9
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h5
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp6
-rw-r--r--src/yuzu/applets/software_keyboard.cpp16
-rw-r--r--src/yuzu/applets/software_keyboard.h7
-rw-r--r--src/yuzu/configuration/config.cpp2
-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_graphics.ui94
-rw-r--r--src/yuzu_cmd/config.cpp1
-rw-r--r--src/yuzu_cmd/default_ini.h2
67 files changed, 1390 insertions, 1442 deletions
diff --git a/.gitmodules b/.gitmodules
index 2c044ed7d..a33a04167 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,9 +13,6 @@
13[submodule "dynarmic"] 13[submodule "dynarmic"]
14 path = externals/dynarmic 14 path = externals/dynarmic
15 url = https://github.com/MerryMage/dynarmic.git 15 url = https://github.com/MerryMage/dynarmic.git
16[submodule "xbyak"]
17 path = externals/xbyak
18 url = https://github.com/herumi/xbyak.git
19[submodule "fmt"] 16[submodule "fmt"]
20 path = externals/fmt 17 path = externals/fmt
21 url = https://github.com/fmtlib/fmt.git 18 url = https://github.com/fmtlib/fmt.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 918cf5372..1f71f9fd9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -377,7 +377,7 @@ if (CLANG_FORMAT)
377 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") 377 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
378 if (WIN32) 378 if (WIN32)
379 add_custom_target(clang-format 379 add_custom_target(clang-format
380 COMMAND powershell.exe -Command "${CLANG_FORMAT} -i @(Get-ChildItem -Recurse ${SRCS}/* -Include \'*.h\', \'*.cpp\')" 380 COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}"
381 COMMENT ${CCOMMENT}) 381 COMMENT ${CCOMMENT})
382 elseif(MINGW) 382 elseif(MINGW)
383 add_custom_target(clang-format 383 add_custom_target(clang-format
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 1261062e8..e156bbece 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -9,7 +9,6 @@ target_include_directories(catch-single-include INTERFACE catch/single_include)
9 9
10# Dynarmic 10# Dynarmic
11if (ARCHITECTURE_x86_64) 11if (ARCHITECTURE_x86_64)
12 add_library(xbyak INTERFACE)
13 set(DYNARMIC_TESTS OFF) 12 set(DYNARMIC_TESTS OFF)
14 set(DYNARMIC_NO_BUNDLED_FMT ON) 13 set(DYNARMIC_NO_BUNDLED_FMT ON)
15 add_subdirectory(dynarmic) 14 add_subdirectory(dynarmic)
@@ -53,14 +52,6 @@ target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
53# SoundTouch 52# SoundTouch
54add_subdirectory(soundtouch) 53add_subdirectory(soundtouch)
55 54
56# Xbyak
57if (ARCHITECTURE_x86_64)
58 # Defined before "dynarmic" above
59 # add_library(xbyak INTERFACE)
60 target_include_directories(xbyak INTERFACE ./xbyak/xbyak)
61 target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
62endif()
63
64# Opus 55# Opus
65add_subdirectory(opus) 56add_subdirectory(opus)
66target_include_directories(opus INTERFACE ./opus/include) 57target_include_directories(opus INTERFACE ./opus/include)
diff --git a/externals/xbyak b/externals/xbyak
deleted file mode 160000
Subproject 1de435ed04c8e74775804da944d176baf0ce56e
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/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 a355eaca6..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
@@ -156,6 +158,8 @@ add_library(core STATIC
156 hle/service/am/applets/applets.h 158 hle/service/am/applets/applets.h
157 hle/service/am/applets/software_keyboard.cpp 159 hle/service/am/applets/software_keyboard.cpp
158 hle/service/am/applets/software_keyboard.h 160 hle/service/am/applets/software_keyboard.h
161 hle/service/am/applets/stub_applet.cpp
162 hle/service/am/applets/stub_applet.h
159 hle/service/am/idle.cpp 163 hle/service/am/idle.cpp
160 hle/service/am/idle.h 164 hle/service/am/idle.h
161 hle/service/am/omm.cpp 165 hle/service/am/omm.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 6c72fdf4a..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"
@@ -28,7 +29,6 @@
28#include "core/hle/service/sm/sm.h" 29#include "core/hle/service/sm/sm.h"
29#include "core/loader/loader.h" 30#include "core/loader/loader.h"
30#include "core/perf_stats.h" 31#include "core/perf_stats.h"
31#include "core/settings.h"
32#include "core/telemetry_session.h" 32#include "core/telemetry_session.h"
33#include "frontend/applets/software_keyboard.h" 33#include "frontend/applets/software_keyboard.h"
34#include "video_core/debug_utils/debug_utils.h" 34#include "video_core/debug_utils/debug_utils.h"
@@ -71,64 +71,22 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
71 71
72 return vfs->OpenFile(path, FileSys::Mode::Read); 72 return vfs->OpenFile(path, FileSys::Mode::Read);
73} 73}
74
75/// Runs a CPU core while the system is powered on
76void RunCpuCore(Cpu& cpu_state) {
77 while (Core::System::GetInstance().IsPoweredOn()) {
78 cpu_state.RunLoop(true);
79 }
80}
81} // Anonymous namespace 74} // Anonymous namespace
82 75
83struct System::Impl { 76struct System::Impl {
84 Cpu& CurrentCpuCore() { 77 Cpu& CurrentCpuCore() {
85 if (Settings::values.use_multi_core) { 78 return cpu_core_manager.GetCurrentCore();
86 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
87 ASSERT(search != thread_to_cpu.end());
88 ASSERT(search->second);
89 return *search->second;
90 }
91
92 // Otherwise, use single-threaded mode active_core variable
93 return *cpu_cores[active_core];
94 } 79 }
95 80
96 ResultStatus RunLoop(bool tight_loop) { 81 ResultStatus RunLoop(bool tight_loop) {
97 status = ResultStatus::Success; 82 status = ResultStatus::Success;
98 83
99 // Update thread_to_cpu in case Core 0 is run from a different host thread 84 cpu_core_manager.RunLoop(tight_loop);
100 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
101
102 if (GDBStub::IsServerEnabled()) {
103 GDBStub::HandlePacket();
104
105 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
106 // execute. Otherwise, get out of the loop function.
107 if (GDBStub::GetCpuHaltFlag()) {
108 if (GDBStub::GetCpuStepFlag()) {
109 tight_loop = false;
110 } else {
111 return ResultStatus::Success;
112 }
113 }
114 }
115
116 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
117 cpu_cores[active_core]->RunLoop(tight_loop);
118 if (Settings::values.use_multi_core) {
119 // Cores 1-3 are run on other threads in this mode
120 break;
121 }
122 }
123
124 if (GDBStub::IsServerEnabled()) {
125 GDBStub::SetCpuStepFlag(false);
126 }
127 85
128 return status; 86 return status;
129 } 87 }
130 88
131 ResultStatus Init(Frontend::EmuWindow& emu_window) { 89 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
132 LOG_DEBUG(HW_Memory, "initialized OK"); 90 LOG_DEBUG(HW_Memory, "initialized OK");
133 91
134 CoreTiming::Init(); 92 CoreTiming::Init();
@@ -145,12 +103,6 @@ struct System::Impl {
145 auto main_process = Kernel::Process::Create(kernel, "main"); 103 auto main_process = Kernel::Process::Create(kernel, "main");
146 kernel.MakeCurrentProcess(main_process.get()); 104 kernel.MakeCurrentProcess(main_process.get());
147 105
148 cpu_barrier = std::make_unique<CpuBarrier>();
149 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
150 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
151 cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
152 }
153
154 telemetry_session = std::make_unique<Core::TelemetrySession>(); 106 telemetry_session = std::make_unique<Core::TelemetrySession>();
155 service_manager = std::make_shared<Service::SM::ServiceManager>(); 107 service_manager = std::make_shared<Service::SM::ServiceManager>();
156 108
@@ -164,17 +116,8 @@ struct System::Impl {
164 116
165 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); 117 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
166 118
167 // Create threads for CPU cores 1-3, and build thread_to_cpu map 119 cpu_core_manager.Initialize(system);
168 // CPU core 0 is run on the main thread 120 is_powered_on = true;
169 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
170 if (Settings::values.use_multi_core) {
171 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
172 cpu_core_threads[index] =
173 std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
174 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
175 }
176 }
177
178 LOG_DEBUG(Core, "Initialized OK"); 121 LOG_DEBUG(Core, "Initialized OK");
179 122
180 // Reset counters and set time origin to current frame 123 // Reset counters and set time origin to current frame
@@ -184,7 +127,8 @@ struct System::Impl {
184 return ResultStatus::Success; 127 return ResultStatus::Success;
185 } 128 }
186 129
187 ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 130 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
131 const std::string& filepath) {
188 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 132 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
189 133
190 if (!app_loader) { 134 if (!app_loader) {
@@ -201,7 +145,7 @@ struct System::Impl {
201 return ResultStatus::ErrorSystemMode; 145 return ResultStatus::ErrorSystemMode;
202 } 146 }
203 147
204 ResultStatus init_result{Init(emu_window)}; 148 ResultStatus init_result{Init(system, emu_window)};
205 if (init_result != ResultStatus::Success) { 149 if (init_result != ResultStatus::Success) {
206 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", 150 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
207 static_cast<int>(init_result)); 151 static_cast<int>(init_result));
@@ -231,6 +175,8 @@ struct System::Impl {
231 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", 175 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
232 perf_results.frametime * 1000.0); 176 perf_results.frametime * 1000.0);
233 177
178 is_powered_on = false;
179
234 // Shutdown emulation session 180 // Shutdown emulation session
235 renderer.reset(); 181 renderer.reset();
236 GDBStub::Shutdown(); 182 GDBStub::Shutdown();
@@ -240,19 +186,7 @@ struct System::Impl {
240 gpu_core.reset(); 186 gpu_core.reset();
241 187
242 // Close all CPU/threading state 188 // Close all CPU/threading state
243 cpu_barrier->NotifyEnd(); 189 cpu_core_manager.Shutdown();
244 if (Settings::values.use_multi_core) {
245 for (auto& thread : cpu_core_threads) {
246 thread->join();
247 thread.reset();
248 }
249 }
250 thread_to_cpu.clear();
251 for (auto& cpu_core : cpu_cores) {
252 cpu_core.reset();
253 }
254 cpu_exclusive_monitor.reset();
255 cpu_barrier.reset();
256 190
257 // Shutdown kernel and core timing 191 // Shutdown kernel and core timing
258 kernel.Shutdown(); 192 kernel.Shutdown();
@@ -289,11 +223,8 @@ struct System::Impl {
289 std::unique_ptr<VideoCore::RendererBase> renderer; 223 std::unique_ptr<VideoCore::RendererBase> renderer;
290 std::unique_ptr<Tegra::GPU> gpu_core; 224 std::unique_ptr<Tegra::GPU> gpu_core;
291 std::shared_ptr<Tegra::DebugContext> debug_context; 225 std::shared_ptr<Tegra::DebugContext> debug_context;
292 std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 226 CpuCoreManager cpu_core_manager;
293 std::unique_ptr<CpuBarrier> cpu_barrier; 227 bool is_powered_on = false;
294 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
295 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
296 std::size_t active_core{}; ///< Active core, only used in single thread mode
297 228
298 /// Frontend applets 229 /// Frontend applets
299 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; 230 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
@@ -307,9 +238,6 @@ struct System::Impl {
307 ResultStatus status = ResultStatus::Success; 238 ResultStatus status = ResultStatus::Success;
308 std::string status_details = ""; 239 std::string status_details = "";
309 240
310 /// Map of guest threads to CPU cores
311 std::map<std::thread::id, Cpu*> thread_to_cpu;
312
313 Core::PerfStats perf_stats; 241 Core::PerfStats perf_stats;
314 Core::FrameLimiter frame_limiter; 242 Core::FrameLimiter frame_limiter;
315}; 243};
@@ -334,17 +262,15 @@ System::ResultStatus System::SingleStep() {
334} 262}
335 263
336void System::InvalidateCpuInstructionCaches() { 264void System::InvalidateCpuInstructionCaches() {
337 for (auto& cpu : impl->cpu_cores) { 265 impl->cpu_core_manager.InvalidateAllInstructionCaches();
338 cpu->ArmInterface().ClearInstructionCache();
339 }
340} 266}
341 267
342System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 268System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
343 return impl->Load(emu_window, filepath); 269 return impl->Load(*this, emu_window, filepath);
344} 270}
345 271
346bool System::IsPoweredOn() const { 272bool System::IsPoweredOn() const {
347 return impl->cpu_barrier && impl->cpu_barrier->IsAlive(); 273 return impl->is_powered_on;
348} 274}
349 275
350void System::PrepareReschedule() { 276void System::PrepareReschedule() {
@@ -408,21 +334,20 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
408} 334}
409 335
410Cpu& System::CpuCore(std::size_t core_index) { 336Cpu& System::CpuCore(std::size_t core_index) {
411 ASSERT(core_index < NUM_CPU_CORES); 337 return impl->cpu_core_manager.GetCore(core_index);
412 return *impl->cpu_cores[core_index];
413} 338}
414 339
415const Cpu& System::CpuCore(std::size_t core_index) const { 340const Cpu& System::CpuCore(std::size_t core_index) const {
416 ASSERT(core_index < NUM_CPU_CORES); 341 ASSERT(core_index < NUM_CPU_CORES);
417 return *impl->cpu_cores[core_index]; 342 return impl->cpu_core_manager.GetCore(core_index);
418} 343}
419 344
420ExclusiveMonitor& System::Monitor() { 345ExclusiveMonitor& System::Monitor() {
421 return *impl->cpu_exclusive_monitor; 346 return impl->cpu_core_manager.GetExclusiveMonitor();
422} 347}
423 348
424const ExclusiveMonitor& System::Monitor() const { 349const ExclusiveMonitor& System::Monitor() const {
425 return *impl->cpu_exclusive_monitor; 350 return impl->cpu_core_manager.GetExclusiveMonitor();
426} 351}
427 352
428Tegra::GPU& System::GPU() { 353Tegra::GPU& System::GPU() {
@@ -506,7 +431,7 @@ const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() cons
506} 431}
507 432
508System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 433System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
509 return impl->Init(emu_window); 434 return impl->Init(*this, emu_window);
510} 435}
511 436
512void System::Shutdown() { 437void System::Shutdown() {
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/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/process.cpp b/src/core/hle/kernel/process.cpp
index 1412257a0..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"
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 230e395ff..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>
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 51c367de7..b8b6b4d49 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -789,7 +789,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
789 return ERR_INVALID_MEMORY_RANGE; 789 return ERR_INVALID_MEMORY_RANGE;
790 } 790 }
791 791
792 return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare); 792 return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare);
793} 793}
794 794
795static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 795static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
@@ -819,7 +819,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
819 return ERR_INVALID_MEMORY_RANGE; 819 return ERR_INVALID_MEMORY_RANGE;
820 } 820 }
821 821
822 return shared_memory->Unmap(current_process, addr); 822 return shared_memory->Unmap(*current_process, addr);
823} 823}
824 824
825/// Query process memory 825/// Query process memory
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index fd14af1e7..4f17b52f9 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -6,8 +6,6 @@
6#include <cinttypes> 6#include <cinttypes>
7#include <cstring> 7#include <cstring>
8#include <stack> 8#include <stack>
9#include "applets/applets.h"
10#include "applets/software_keyboard.h"
11#include "audio_core/audio_renderer.h" 9#include "audio_core/audio_renderer.h"
12#include "core/core.h" 10#include "core/core.h"
13#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
@@ -18,6 +16,9 @@
18#include "core/hle/service/am/am.h" 16#include "core/hle/service/am/am.h"
19#include "core/hle/service/am/applet_ae.h" 17#include "core/hle/service/am/applet_ae.h"
20#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"
21#include "core/hle/service/am/idle.h" 22#include "core/hle/service/am/idle.h"
22#include "core/hle/service/am/omm.h" 23#include "core/hle/service/am/omm.h"
23#include "core/hle/service/am/spsm.h" 24#include "core/hle/service/am/spsm.h"
@@ -482,11 +483,15 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
482 rb.Push(RESULT_SUCCESS); 483 rb.Push(RESULT_SUCCESS);
483 484
484 if (Settings::values.use_docked_mode) { 485 if (Settings::values.use_docked_mode) {
485 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 486 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
486 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));
487 } else { 490 } else {
488 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); 491 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
489 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));
490 } 495 }
491 496
492 LOG_DEBUG(Service_AM, "called"); 497 LOG_DEBUG(Service_AM, "called");
@@ -532,8 +537,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
532class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { 537class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
533public: 538public:
534 explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet) 539 explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet)
535 : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)), 540 : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) {
536 broker(std::make_shared<Applets::AppletDataBroker>()) {
537 // clang-format off 541 // clang-format off
538 static const FunctionInfo functions[] = { 542 static const FunctionInfo functions[] = {
539 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, 543 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
@@ -562,7 +566,7 @@ public:
562 566
563private: 567private:
564 void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { 568 void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
565 const auto event = broker->GetStateChangedEvent(); 569 const auto event = applet->GetBroker().GetStateChangedEvent();
566 event->Signal(); 570 event->Signal();
567 571
568 IPC::ResponseBuilder rb{ctx, 2, 1}; 572 IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -590,7 +594,7 @@ private:
590 void Start(Kernel::HLERequestContext& ctx) { 594 void Start(Kernel::HLERequestContext& ctx) {
591 ASSERT(applet != nullptr); 595 ASSERT(applet != nullptr);
592 596
593 applet->Initialize(broker); 597 applet->Initialize();
594 applet->Execute(); 598 applet->Execute();
595 599
596 IPC::ResponseBuilder rb{ctx, 2}; 600 IPC::ResponseBuilder rb{ctx, 2};
@@ -601,7 +605,7 @@ private:
601 605
602 void PushInData(Kernel::HLERequestContext& ctx) { 606 void PushInData(Kernel::HLERequestContext& ctx) {
603 IPC::RequestParser rp{ctx}; 607 IPC::RequestParser rp{ctx};
604 broker->PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>()); 608 applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
605 609
606 IPC::ResponseBuilder rb{ctx, 2}; 610 IPC::ResponseBuilder rb{ctx, 2};
607 rb.Push(RESULT_SUCCESS); 611 rb.Push(RESULT_SUCCESS);
@@ -612,7 +616,7 @@ private:
612 void PopOutData(Kernel::HLERequestContext& ctx) { 616 void PopOutData(Kernel::HLERequestContext& ctx) {
613 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 617 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
614 618
615 const auto storage = broker->PopNormalDataToGame(); 619 const auto storage = applet->GetBroker().PopNormalDataToGame();
616 if (storage == nullptr) { 620 if (storage == nullptr) {
617 rb.Push(ERR_NO_DATA_IN_CHANNEL); 621 rb.Push(ERR_NO_DATA_IN_CHANNEL);
618 return; 622 return;
@@ -626,7 +630,7 @@ private:
626 630
627 void PushInteractiveInData(Kernel::HLERequestContext& ctx) { 631 void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
628 IPC::RequestParser rp{ctx}; 632 IPC::RequestParser rp{ctx};
629 broker->PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>()); 633 applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
630 634
631 ASSERT(applet->IsInitialized()); 635 ASSERT(applet->IsInitialized());
632 applet->ExecuteInteractive(); 636 applet->ExecuteInteractive();
@@ -641,7 +645,7 @@ private:
641 void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { 645 void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
642 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 646 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
643 647
644 const auto storage = broker->PopInteractiveDataToGame(); 648 const auto storage = applet->GetBroker().PopInteractiveDataToGame();
645 if (storage == nullptr) { 649 if (storage == nullptr) {
646 rb.Push(ERR_NO_DATA_IN_CHANNEL); 650 rb.Push(ERR_NO_DATA_IN_CHANNEL);
647 return; 651 return;
@@ -656,7 +660,7 @@ private:
656 void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { 660 void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
657 IPC::ResponseBuilder rb{ctx, 2, 1}; 661 IPC::ResponseBuilder rb{ctx, 2, 1};
658 rb.Push(RESULT_SUCCESS); 662 rb.Push(RESULT_SUCCESS);
659 rb.PushCopyObjects(broker->GetNormalDataEvent()); 663 rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent());
660 664
661 LOG_DEBUG(Service_AM, "called"); 665 LOG_DEBUG(Service_AM, "called");
662 } 666 }
@@ -664,13 +668,12 @@ private:
664 void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { 668 void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
665 IPC::ResponseBuilder rb{ctx, 2, 1}; 669 IPC::ResponseBuilder rb{ctx, 2, 1};
666 rb.Push(RESULT_SUCCESS); 670 rb.Push(RESULT_SUCCESS);
667 rb.PushCopyObjects(broker->GetInteractiveDataEvent()); 671 rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
668 672
669 LOG_DEBUG(Service_AM, "called"); 673 LOG_DEBUG(Service_AM, "called");
670 } 674 }
671 675
672 std::shared_ptr<Applets::Applet> applet; 676 std::shared_ptr<Applets::Applet> applet;
673 std::shared_ptr<Applets::AppletDataBroker> broker;
674}; 677};
675 678
676void IStorage::Open(Kernel::HLERequestContext& ctx) { 679void IStorage::Open(Kernel::HLERequestContext& ctx) {
@@ -763,8 +766,9 @@ static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
763 case AppletId::SoftwareKeyboard: 766 case AppletId::SoftwareKeyboard:
764 return std::make_shared<Applets::SoftwareKeyboard>(); 767 return std::make_shared<Applets::SoftwareKeyboard>();
765 default: 768 default:
766 UNREACHABLE_MSG("Unimplemented AppletId [{:08X}]!", static_cast<u32>(id)); 769 LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
767 return nullptr; 770 static_cast<u32>(id));
771 return std::make_shared<Applets::StubApplet>();
768 } 772 }
769} 773}
770 774
@@ -820,8 +824,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
820 return; 824 return;
821 } 825 }
822 826
823 const auto mem_begin = shared_mem->backing_block->begin() + shared_mem->backing_block_offset; 827 const u8* mem_begin = shared_mem->GetPointer();
824 const auto mem_end = mem_begin + shared_mem->size; 828 const u8* mem_end = mem_begin + shared_mem->GetSize();
825 std::vector<u8> memory{mem_begin, mem_end}; 829 std::vector<u8> memory{mem_begin, mem_end};
826 830
827 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 831 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 8adb81823..becbadd06 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -98,10 +98,8 @@ Applet::Applet() = default;
98 98
99Applet::~Applet() = default; 99Applet::~Applet() = default;
100 100
101void Applet::Initialize(std::shared_ptr<AppletDataBroker> broker_) { 101void Applet::Initialize() {
102 broker = std::move(broker_); 102 const auto common = broker.PopNormalDataToApplet();
103
104 const auto common = broker->PopNormalDataToApplet();
105 ASSERT(common != nullptr); 103 ASSERT(common != nullptr);
106 104
107 const auto common_data = common->GetData(); 105 const auto common_data = common->GetData();
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 136445649..f65ea119c 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -4,14 +4,17 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
8#include <memory> 7#include <memory>
9#include <queue> 8#include <queue>
10#include "common/swap.h" 9#include "common/swap.h"
11#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/kernel.h"
12 11
13union ResultCode; 12union ResultCode;
14 13
14namespace Kernel {
15class Event;
16}
17
15namespace Service::AM { 18namespace Service::AM {
16 19
17class IStorage; 20class IStorage;
@@ -43,19 +46,26 @@ public:
43 46
44private: 47private:
45 // Queues are named from applet's perspective 48 // Queues are named from applet's perspective
46 std::queue<std::unique_ptr<IStorage>> 49
47 in_channel; // PopNormalDataToApplet and PushNormalDataFromGame 50 // PopNormalDataToApplet and PushNormalDataFromGame
48 std::queue<std::unique_ptr<IStorage>> 51 std::queue<std::unique_ptr<IStorage>> in_channel;
49 out_channel; // PopNormalDataToGame and PushNormalDataFromApplet 52
50 std::queue<std::unique_ptr<IStorage>> 53 // PopNormalDataToGame and PushNormalDataFromApplet
51 in_interactive_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame 54 std::queue<std::unique_ptr<IStorage>> out_channel;
52 std::queue<std::unique_ptr<IStorage>> 55
53 out_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet 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;
54 61
55 Kernel::SharedPtr<Kernel::Event> state_changed_event; 62 Kernel::SharedPtr<Kernel::Event> state_changed_event;
56 Kernel::SharedPtr<Kernel::Event> pop_out_data_event; // Signaled on PushNormalDataFromApplet 63
57 Kernel::SharedPtr<Kernel::Event> 64 // Signaled on PushNormalDataFromApplet
58 pop_interactive_out_data_event; // Signaled on PushInteractiveDataFromApplet 65 Kernel::SharedPtr<Kernel::Event> pop_out_data_event;
66
67 // Signaled on PushInteractiveDataFromApplet
68 Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event;
59}; 69};
60 70
61class Applet { 71class Applet {
@@ -63,7 +73,7 @@ public:
63 Applet(); 73 Applet();
64 virtual ~Applet(); 74 virtual ~Applet();
65 75
66 virtual void Initialize(std::shared_ptr<AppletDataBroker> broker); 76 virtual void Initialize();
67 77
68 virtual bool TransactionComplete() const = 0; 78 virtual bool TransactionComplete() const = 0;
69 virtual ResultCode GetStatus() const = 0; 79 virtual ResultCode GetStatus() const = 0;
@@ -74,6 +84,14 @@ public:
74 return initialized; 84 return initialized;
75 } 85 }
76 86
87 AppletDataBroker& GetBroker() {
88 return broker;
89 }
90
91 const AppletDataBroker& GetBroker() const {
92 return broker;
93 }
94
77protected: 95protected:
78 struct CommonArguments { 96 struct CommonArguments {
79 u32_le arguments_version; 97 u32_le arguments_version;
@@ -85,8 +103,8 @@ protected:
85 }; 103 };
86 static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); 104 static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
87 105
88 CommonArguments common_args; 106 CommonArguments common_args{};
89 std::shared_ptr<AppletDataBroker> broker; 107 AppletDataBroker broker;
90 bool initialized = false; 108 bool initialized = false;
91}; 109};
92 110
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index c4b76a515..981bdec51 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -42,21 +42,21 @@ SoftwareKeyboard::SoftwareKeyboard() = default;
42 42
43SoftwareKeyboard::~SoftwareKeyboard() = default; 43SoftwareKeyboard::~SoftwareKeyboard() = default;
44 44
45void SoftwareKeyboard::Initialize(std::shared_ptr<AppletDataBroker> broker_) { 45void SoftwareKeyboard::Initialize() {
46 complete = false; 46 complete = false;
47 initial_text.clear(); 47 initial_text.clear();
48 final_data.clear(); 48 final_data.clear();
49 49
50 Applet::Initialize(std::move(broker_)); 50 Applet::Initialize();
51 51
52 const auto keyboard_config_storage = broker->PopNormalDataToApplet(); 52 const auto keyboard_config_storage = broker.PopNormalDataToApplet();
53 ASSERT(keyboard_config_storage != nullptr); 53 ASSERT(keyboard_config_storage != nullptr);
54 const auto& keyboard_config = keyboard_config_storage->GetData(); 54 const auto& keyboard_config = keyboard_config_storage->GetData();
55 55
56 ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); 56 ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
57 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); 57 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
58 58
59 const auto work_buffer_storage = broker->PopNormalDataToApplet(); 59 const auto work_buffer_storage = broker.PopNormalDataToApplet();
60 ASSERT(work_buffer_storage != nullptr); 60 ASSERT(work_buffer_storage != nullptr);
61 const auto& work_buffer = work_buffer_storage->GetData(); 61 const auto& work_buffer = work_buffer_storage->GetData();
62 62
@@ -81,7 +81,7 @@ void SoftwareKeyboard::ExecuteInteractive() {
81 if (complete) 81 if (complete)
82 return; 82 return;
83 83
84 const auto storage = broker->PopInteractiveDataToApplet(); 84 const auto storage = broker.PopInteractiveDataToApplet();
85 ASSERT(storage != nullptr); 85 ASSERT(storage != nullptr);
86 const auto data = storage->GetData(); 86 const auto data = storage->GetData();
87 const auto status = static_cast<bool>(data[0]); 87 const auto status = static_cast<bool>(data[0]);
@@ -95,13 +95,13 @@ void SoftwareKeyboard::ExecuteInteractive() {
95 std::memcpy(string.data(), data.data() + 4, string.size() * 2); 95 std::memcpy(string.data(), data.data() + 4, string.size() * 2);
96 frontend.SendTextCheckDialog( 96 frontend.SendTextCheckDialog(
97 Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), 97 Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
98 [this] { broker->SignalStateChanged(); }); 98 [this] { broker.SignalStateChanged(); });
99 } 99 }
100} 100}
101 101
102void SoftwareKeyboard::Execute() { 102void SoftwareKeyboard::Execute() {
103 if (complete) { 103 if (complete) {
104 broker->PushNormalDataFromApplet(IStorage{final_data}); 104 broker.PushNormalDataFromApplet(IStorage{final_data});
105 return; 105 return;
106 } 106 }
107 107
@@ -145,17 +145,17 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
145 final_data = output_main; 145 final_data = output_main;
146 146
147 if (complete) { 147 if (complete) {
148 broker->PushNormalDataFromApplet(IStorage{output_main}); 148 broker.PushNormalDataFromApplet(IStorage{output_main});
149 } else { 149 } else {
150 broker->PushInteractiveDataFromApplet(IStorage{output_sub}); 150 broker.PushInteractiveDataFromApplet(IStorage{output_sub});
151 } 151 }
152 152
153 broker->SignalStateChanged(); 153 broker.SignalStateChanged();
154 } else { 154 } else {
155 output_main[0] = 1; 155 output_main[0] = 1;
156 complete = true; 156 complete = true;
157 broker->PushNormalDataFromApplet(IStorage{output_main}); 157 broker.PushNormalDataFromApplet(IStorage{output_main});
158 broker->SignalStateChanged(); 158 broker.SignalStateChanged();
159 } 159 }
160} 160}
161} // namespace Service::AM::Applets 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
index 16e1fff66..efd5753a1 100644
--- a/src/core/hle/service/am/applets/software_keyboard.h
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -4,7 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <string>
9#include <vector>
10
7#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "common/swap.h"
8#include "core/hle/service/am/am.h" 13#include "core/hle/service/am/am.h"
9#include "core/hle/service/am/applets/applets.h" 14#include "core/hle/service/am/applets/applets.h"
10 15
@@ -50,7 +55,7 @@ public:
50 SoftwareKeyboard(); 55 SoftwareKeyboard();
51 ~SoftwareKeyboard() override; 56 ~SoftwareKeyboard() override;
52 57
53 void Initialize(std::shared_ptr<AppletDataBroker> broker) override; 58 void Initialize() override;
54 59
55 bool TransactionComplete() const override; 60 bool TransactionComplete() const override;
56 ResultCode GetStatus() const override; 61 ResultCode GetStatus() const override;
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/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index e76c83aee..c22357d8c 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -71,8 +71,9 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
71 71
72void Controller_DebugPad::OnLoadInputDevices() { 72void Controller_DebugPad::OnLoadInputDevices() {
73 std::transform(Settings::values.debug_pad_buttons.begin(), 73 std::transform(Settings::values.debug_pad_buttons.begin(),
74 Settings::values.debug_pad_buttons.end(), buttons.begin(), 74 Settings::values.debug_pad_buttons.begin() +
75 Input::CreateDevice<Input::ButtonDevice>); 75 Settings::NativeButton::NUM_BUTTONS_HID,
76 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
76 std::transform(Settings::values.debug_pad_analogs.begin(), 77 std::transform(Settings::values.debug_pad_analogs.begin(),
77 Settings::values.debug_pad_analogs.end(), analogs.begin(), 78 Settings::values.debug_pad_analogs.end(), analogs.begin(),
78 Input::CreateDevice<Input::AnalogDevice>); 79 Input::CreateDevice<Input::AnalogDevice>);
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index b43f1f054..7a9d0d0dd 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -16,35 +16,18 @@
16 16
17namespace Service::LDR { 17namespace Service::LDR {
18 18
19namespace ErrCodes { 19constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
20enum { 20constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
21 InvalidMemoryState = 51, 21constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
22 InvalidNRO = 52, 22constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
23 InvalidNRR = 53, 23constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
24 MissingNRRHash = 54, 24constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
25 MaximumNRO = 55, 25constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
26 MaximumNRR = 56, 26constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
27 AlreadyLoaded = 57, 27constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
28 InvalidAlignment = 81, 28constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
29 InvalidSize = 82, 29constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
30 InvalidNROAddress = 84, 30constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
31 InvalidNRRAddress = 85,
32 NotInitialized = 87,
33};
34}
35
36constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState);
37constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO);
38constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR);
39constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash);
40constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO);
41constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR);
42constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded);
43constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment);
44constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize);
45constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress);
46constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress);
47constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized);
48 31
49constexpr u64 MAXIMUM_LOADED_RO = 0x40; 32constexpr u64 MAXIMUM_LOADED_RO = 0x40;
50 33
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/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index d25fdb1fe..a72416084 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -510,7 +510,11 @@ private:
510 510
511 if (transaction == TransactionId::Connect) { 511 if (transaction == TransactionId::Connect) {
512 IGBPConnectRequestParcel request{ctx.ReadBuffer()}; 512 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
513 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)};
514 ctx.WriteBuffer(response.Serialize()); 518 ctx.WriteBuffer(response.Serialize());
515 } else if (transaction == TransactionId::SetPreallocatedBuffer) { 519 } else if (transaction == TransactionId::SetPreallocatedBuffer) {
516 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; 520 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
@@ -692,11 +696,15 @@ private:
692 rb.Push(RESULT_SUCCESS); 696 rb.Push(RESULT_SUCCESS);
693 697
694 if (Settings::values.use_docked_mode) { 698 if (Settings::values.use_docked_mode) {
695 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 699 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
696 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));
697 } else { 703 } else {
698 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); 704 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
699 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));
700 } 708 }
701 709
702 rb.PushRaw<float>(60.0f); 710 rb.PushRaw<float>(60.0f);
@@ -901,11 +909,15 @@ private:
901 rb.Push(RESULT_SUCCESS); 909 rb.Push(RESULT_SUCCESS);
902 910
903 if (Settings::values.use_docked_mode) { 911 if (Settings::values.use_docked_mode) {
904 rb.Push(static_cast<u64>(DisplayResolution::DockedWidth)); 912 rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) *
905 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));
906 } else { 916 } else {
907 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth)); 917 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
908 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));
909 } 921 }
910 } 922 }
911 923
@@ -922,6 +934,8 @@ private:
922 void ListDisplays(Kernel::HLERequestContext& ctx) { 934 void ListDisplays(Kernel::HLERequestContext& ctx) {
923 IPC::RequestParser rp{ctx}; 935 IPC::RequestParser rp{ctx};
924 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);
925 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 939 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
926 IPC::ResponseBuilder rb{ctx, 4}; 940 IPC::ResponseBuilder rb{ctx, 4};
927 rb.Push(RESULT_SUCCESS); 941 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/settings.h b/src/core/settings.h
index e63134f80..a0c5fd447 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -403,6 +403,7 @@ struct Values {
403 bool use_gdbstub; 403 bool use_gdbstub;
404 u16 gdbstub_port; 404 u16 gdbstub_port;
405 std::string program_args; 405 std::string program_args;
406 bool dump_exefs;
406 bool dump_nso; 407 bool dump_nso;
407 408
408 // WebService 409 // WebService
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index a04e00ecb..2bc534be3 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -69,6 +69,15 @@ void Maxwell3D::InitializeRegisterDefaults() {
69 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a 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). 70 // register carrying a default value. Assume it's OpenGL's default (1).
71 regs.point_size = 1.0f; 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 }
72} 81}
73 82
74void 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 9e480dc39..4f137e693 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 {
@@ -624,7 +631,16 @@ public:
624 } 631 }
625 } zeta; 632 } zeta;
626 633
627 INSERT_PADDING_WORDS(0x5B); 634 INSERT_PADDING_WORDS(0x41);
635
636 union {
637 BitField<0, 4, u32> stencil;
638 BitField<4, 4, u32> unknown;
639 BitField<8, 4, u32> scissor;
640 BitField<12, 4, u32> viewport;
641 } clear_flags;
642
643 INSERT_PADDING_WORDS(0x19);
628 644
629 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; 645 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
630 646
@@ -1127,6 +1143,7 @@ ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
1127ASSERT_REG_POSITION(color_mask_common, 0x3E4); 1143ASSERT_REG_POSITION(color_mask_common, 0x3E4);
1128ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); 1144ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
1129ASSERT_REG_POSITION(zeta, 0x3F8); 1145ASSERT_REG_POSITION(zeta, 0x3F8);
1146ASSERT_REG_POSITION(clear_flags, 0x43E);
1130ASSERT_REG_POSITION(vertex_attrib_format, 0x458); 1147ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
1131ASSERT_REG_POSITION(rt_control, 0x487); 1148ASSERT_REG_POSITION(rt_control, 0x487);
1132ASSERT_REG_POSITION(zeta_width, 0x48a); 1149ASSERT_REG_POSITION(zeta_width, 0x48a);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 83a6fd875..7e8449bc4 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -153,6 +153,7 @@ enum class PredCondition : u64 {
153 NotEqual = 5, 153 NotEqual = 5,
154 GreaterEqual = 6, 154 GreaterEqual = 6,
155 LessThanWithNan = 9, 155 LessThanWithNan = 9,
156 LessEqualWithNan = 11,
156 GreaterThanWithNan = 12, 157 GreaterThanWithNan = 12,
157 NotEqualWithNan = 13, 158 NotEqualWithNan = 13,
158 GreaterEqualWithNan = 14, 159 GreaterEqualWithNan = 14,
@@ -261,7 +262,7 @@ enum class FlowCondition : u64 {
261 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? 262 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for?
262}; 263};
263 264
264enum class ControlCode : u64 { 265enum class ConditionCode : u64 {
265 F = 0, 266 F = 0,
266 LT = 1, 267 LT = 1,
267 EQ = 2, 268 EQ = 2,
@@ -569,7 +570,6 @@ union Instruction {
569 BitField<39, 2, u64> tab5cb8_2; 570 BitField<39, 2, u64> tab5cb8_2;
570 BitField<41, 3, u64> tab5c68_1; 571 BitField<41, 3, u64> tab5c68_1;
571 BitField<44, 2, u64> tab5c68_0; 572 BitField<44, 2, u64> tab5c68_0;
572 BitField<47, 1, u64> cc;
573 BitField<48, 1, u64> negate_b; 573 BitField<48, 1, u64> negate_b;
574 } fmul; 574 } fmul;
575 575
@@ -831,7 +831,7 @@ union Instruction {
831 union { 831 union {
832 BitField<0, 3, u64> pred0; 832 BitField<0, 3, u64> pred0;
833 BitField<3, 3, u64> pred3; 833 BitField<3, 3, u64> pred3;
834 BitField<8, 5, ControlCode> cc; // flag in cc 834 BitField<8, 5, ConditionCode> cc; // flag in cc
835 BitField<39, 3, u64> pred39; 835 BitField<39, 3, u64> pred39;
836 BitField<42, 1, u64> neg_pred39; 836 BitField<42, 1, u64> neg_pred39;
837 BitField<45, 4, PredOperation> op; // op with pred39 837 BitField<45, 4, PredOperation> op; // op with pred39
@@ -1235,7 +1235,7 @@ union Instruction {
1235 BitField<60, 1, u64> is_b_gpr; 1235 BitField<60, 1, u64> is_b_gpr;
1236 BitField<59, 1, u64> is_c_gpr; 1236 BitField<59, 1, u64> is_c_gpr;
1237 BitField<20, 24, s64> smem_imm; 1237 BitField<20, 24, s64> smem_imm;
1238 BitField<0, 5, ControlCode> flow_control_code; 1238 BitField<0, 5, ConditionCode> flow_condition_code;
1239 1239
1240 Attribute attribute; 1240 Attribute attribute;
1241 Sampler sampler; 1241 Sampler sampler;
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/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8e5ca298f..630a58e49 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -537,6 +537,30 @@ void RasterizerOpenGL::Clear() {
537 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); 537 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!");
538 use_stencil = true; 538 use_stencil = true;
539 clear_state.stencil.test_enabled = true; 539 clear_state.stencil.test_enabled = true;
540 if (regs.clear_flags.stencil) {
541 // Stencil affects the clear so fill it with the used masks
542 clear_state.stencil.front.test_func = GL_ALWAYS;
543 clear_state.stencil.front.test_mask = regs.stencil_front_func_mask;
544 clear_state.stencil.front.action_stencil_fail = GL_KEEP;
545 clear_state.stencil.front.action_depth_fail = GL_KEEP;
546 clear_state.stencil.front.action_depth_pass = GL_KEEP;
547 clear_state.stencil.front.write_mask = regs.stencil_front_mask;
548 if (regs.stencil_two_side_enable) {
549 clear_state.stencil.back.test_func = GL_ALWAYS;
550 clear_state.stencil.back.test_mask = regs.stencil_back_func_mask;
551 clear_state.stencil.back.action_stencil_fail = GL_KEEP;
552 clear_state.stencil.back.action_depth_fail = GL_KEEP;
553 clear_state.stencil.back.action_depth_pass = GL_KEEP;
554 clear_state.stencil.back.write_mask = regs.stencil_back_mask;
555 } else {
556 clear_state.stencil.back.test_func = GL_ALWAYS;
557 clear_state.stencil.back.test_mask = 0xFFFFFFFF;
558 clear_state.stencil.back.write_mask = 0xFFFFFFFF;
559 clear_state.stencil.back.action_stencil_fail = GL_KEEP;
560 clear_state.stencil.back.action_depth_fail = GL_KEEP;
561 clear_state.stencil.back.action_depth_pass = GL_KEEP;
562 }
563 }
540 } 564 }
541 565
542 if (!use_color && !use_depth && !use_stencil) { 566 if (!use_color && !use_depth && !use_stencil) {
@@ -548,6 +572,14 @@ void RasterizerOpenGL::Clear() {
548 572
549 ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false, 573 ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
550 regs.clear_buffers.RT.Value()); 574 regs.clear_buffers.RT.Value());
575 if (regs.clear_flags.scissor) {
576 SyncScissorTest(clear_state);
577 }
578
579 if (regs.clear_flags.viewport) {
580 clear_state.EmulateViewportWithScissor();
581 }
582
551 clear_state.Apply(); 583 clear_state.Apply();
552 584
553 if (use_color) { 585 if (use_color) {
@@ -583,7 +615,7 @@ void RasterizerOpenGL::DrawArrays() {
583 SyncLogicOpState(); 615 SyncLogicOpState();
584 SyncCullMode(); 616 SyncCullMode();
585 SyncPrimitiveRestart(); 617 SyncPrimitiveRestart();
586 SyncScissorTest(); 618 SyncScissorTest(state);
587 // Alpha Testing is synced on shaders. 619 // Alpha Testing is synced on shaders.
588 SyncTransformFeedback(); 620 SyncTransformFeedback();
589 SyncPointState(); 621 SyncPointState();
@@ -810,7 +842,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
810 } 842 }
811 const u32 bias = config.mip_lod_bias.Value(); 843 const u32 bias = config.mip_lod_bias.Value();
812 // Sign extend the 13-bit value. 844 // Sign extend the 13-bit value.
813 const u32 mask = 1U << (13 - 1); 845 constexpr u32 mask = 1U << (13 - 1);
814 const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f; 846 const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f;
815 if (lod_bias != bias_lod) { 847 if (lod_bias != bias_lod) {
816 lod_bias = bias_lod; 848 lod_bias = bias_lod;
@@ -942,8 +974,8 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
942 auto& viewport = current_state.viewports[i]; 974 auto& viewport = current_state.viewports[i];
943 viewport.x = viewport_rect.left; 975 viewport.x = viewport_rect.left;
944 viewport.y = viewport_rect.bottom; 976 viewport.y = viewport_rect.bottom;
945 viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth()); 977 viewport.width = viewport_rect.GetWidth();
946 viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight()); 978 viewport.height = viewport_rect.GetHeight();
947 viewport.depth_range_far = regs.viewports[i].depth_range_far; 979 viewport.depth_range_far = regs.viewports[i].depth_range_far;
948 viewport.depth_range_near = regs.viewports[i].depth_range_near; 980 viewport.depth_range_near = regs.viewports[i].depth_range_near;
949 } 981 }
@@ -1115,11 +1147,11 @@ void RasterizerOpenGL::SyncLogicOpState() {
1115 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 1147 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
1116} 1148}
1117 1149
1118void RasterizerOpenGL::SyncScissorTest() { 1150void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
1119 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1151 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1120 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) { 1152 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
1121 const auto& src = regs.scissor_test[i]; 1153 const auto& src = regs.scissor_test[i];
1122 auto& dst = state.viewports[i].scissor; 1154 auto& dst = current_state.viewports[i].scissor;
1123 dst.enabled = (src.enable != 0); 1155 dst.enabled = (src.enable != 0);
1124 if (dst.enabled == 0) { 1156 if (dst.enabled == 0) {
1125 return; 1157 return;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index fd6c8c3c5..f4354289c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -91,19 +91,20 @@ private:
91 void SyncWithConfig(const Tegra::Texture::TSCEntry& 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 float min_lod; 103 GLvec4 border_color = {};
104 float max_lod; 104 float min_lod = 0.0f;
105 float lod_bias; 105 float max_lod = 16.0f;
106 float max_anisotropic; 106 float lod_bias = 0.0f;
107 float max_anisotropic = 1.0f;
107 }; 108 };
108 109
109 /** 110 /**
@@ -171,7 +172,7 @@ private:
171 void SyncMultiSampleState(); 172 void SyncMultiSampleState();
172 173
173 /// Syncs the scissor test state to match the guest state 174 /// Syncs the scissor test state to match the guest state
174 void SyncScissorTest(); 175 void SyncScissorTest(OpenGLState& current_state);
175 176
176 /// Syncs the transform feedback state to match the guest state 177 /// Syncs the transform feedback state to match the guest state
177 void SyncTransformFeedback(); 178 void SyncTransformFeedback();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 83e6a4b50..4f434fc31 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -1275,6 +1275,31 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) {
1275 return surface; 1275 return surface;
1276} 1276}
1277 1277
1278void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
1279 const Surface& dst_surface) {
1280 const auto& init_params{src_surface->GetSurfaceParams()};
1281 const auto& dst_params{dst_surface->GetSurfaceParams()};
1282 VAddr address = init_params.addr;
1283 const std::size_t layer_size = dst_params.LayerMemorySize();
1284 for (u32 layer = 0; layer < dst_params.depth; layer++) {
1285 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
1286 const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap);
1287 const Surface& copy = TryGet(sub_address);
1288 if (!copy)
1289 continue;
1290 const auto& src_params{copy->GetSurfaceParams()};
1291 const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))};
1292 const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))};
1293
1294 glCopyImageSubData(copy->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0,
1295 0, 0, dst_surface->Texture().handle,
1296 SurfaceTargetToGL(dst_params.target), mipmap, 0, 0, layer, width,
1297 height, 1);
1298 }
1299 address += layer_size;
1300 }
1301}
1302
1278void RasterizerCacheOpenGL::FermiCopySurface( 1303void RasterizerCacheOpenGL::FermiCopySurface(
1279 const Tegra::Engines::Fermi2D::Regs::Surface& src_config, 1304 const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
1280 const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) { 1305 const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) {
@@ -1340,11 +1365,13 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1340 CopySurface(old_surface, new_surface, copy_pbo.handle); 1365 CopySurface(old_surface, new_surface, copy_pbo.handle);
1341 } 1366 }
1342 break; 1367 break;
1343 case SurfaceTarget::TextureCubemap:
1344 case SurfaceTarget::Texture3D: 1368 case SurfaceTarget::Texture3D:
1369 AccurateCopySurface(old_surface, new_surface);
1370 break;
1371 case SurfaceTarget::TextureCubemap:
1345 case SurfaceTarget::Texture2DArray: 1372 case SurfaceTarget::Texture2DArray:
1346 case SurfaceTarget::TextureCubeArray: 1373 case SurfaceTarget::TextureCubeArray:
1347 AccurateCopySurface(old_surface, new_surface); 1374 FastLayeredCopySurface(old_surface, new_surface);
1348 break; 1375 break;
1349 default: 1376 default:
1350 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1377 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..97b9028c5 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 /**
@@ -624,8 +636,8 @@ private:
624 636
625 /// Generates declarations for internal flags. 637 /// Generates declarations for internal flags.
626 void GenerateInternalFlags() { 638 void GenerateInternalFlags() {
627 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { 639 for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
628 const InternalFlag code = static_cast<InternalFlag>(ii); 640 const InternalFlag code = static_cast<InternalFlag>(flag);
629 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); 641 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
630 } 642 }
631 declarations.AddNewLine(); 643 declarations.AddNewLine();
@@ -761,8 +773,7 @@ private:
761 u64 dest_num_components, u64 value_num_components, u64 dest_elem, 773 u64 dest_num_components, u64 value_num_components, u64 dest_elem,
762 bool precise) { 774 bool precise) {
763 if (reg == Register::ZeroIndex) { 775 if (reg == Register::ZeroIndex) {
764 LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex"); 776 // Setting RZ is a nop in hardware.
765 UNREACHABLE();
766 return; 777 return;
767 } 778 }
768 779
@@ -847,16 +858,13 @@ private:
847 if (declr_input_attribute.count(attribute) == 0) { 858 if (declr_input_attribute.count(attribute) == 0) {
848 declr_input_attribute[attribute] = input_mode; 859 declr_input_attribute[attribute] = input_mode;
849 } else { 860 } else {
850 if (declr_input_attribute[attribute] != input_mode) { 861 UNIMPLEMENTED_IF_MSG(declr_input_attribute[attribute] != input_mode,
851 LOG_CRITICAL(HW_GPU, "Same Input multiple input modes"); 862 "Multiple input modes for the same attribute");
852 UNREACHABLE();
853 }
854 } 863 }
855 return GeometryPass("input_attribute_" + std::to_string(index)); 864 return GeometryPass("input_attribute_" + std::to_string(index));
856 } 865 }
857 866
858 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); 867 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
859 UNREACHABLE();
860 } 868 }
861 869
862 return "vec4(0, 0, 0, 0)"; 870 return "vec4(0, 0, 0, 0)";
@@ -882,24 +890,20 @@ private:
882 break; 890 break;
883 } 891 }
884 default: { 892 default: {
885 LOG_CRITICAL(HW_GPU, "Unhandled Ipa InterpMode: {}", static_cast<u32>(interp_mode)); 893 UNIMPLEMENTED_MSG("Unhandled IPA interp mode: {}", static_cast<u32>(interp_mode));
886 UNREACHABLE();
887 } 894 }
888 } 895 }
889 switch (sample_mode) { 896 switch (sample_mode) {
890 case Tegra::Shader::IpaSampleMode::Centroid: { 897 case Tegra::Shader::IpaSampleMode::Centroid:
891 // Note not implemented, it can be implemented with the "centroid " keyword in glsl; 898 // It can be implemented with the "centroid " keyword in glsl
892 LOG_CRITICAL(HW_GPU, "Ipa Sampler Mode: centroid, not implemented"); 899 UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode centroid");
893 UNREACHABLE();
894 break; 900 break;
895 } 901 case Tegra::Shader::IpaSampleMode::Default:
896 case Tegra::Shader::IpaSampleMode::Default: {
897 // Default, n/a 902 // Default, n/a
898 break; 903 break;
899 }
900 default: { 904 default: {
901 LOG_CRITICAL(HW_GPU, "Unhandled Ipa SampleMode: {}", static_cast<u32>(sample_mode)); 905 UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode: {}", static_cast<u32>(sample_mode));
902 UNREACHABLE(); 906 break;
903 } 907 }
904 } 908 }
905 return out; 909 return out;
@@ -920,8 +924,7 @@ private:
920 return "output_attribute_" + std::to_string(index); 924 return "output_attribute_" + std::to_string(index);
921 } 925 }
922 926
923 LOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); 927 UNIMPLEMENTED_MSG("Unhandled output attribute={}", index);
924 UNREACHABLE();
925 return {}; 928 return {};
926 } 929 }
927 } 930 }
@@ -951,9 +954,10 @@ private:
951class GLSLGenerator { 954class GLSLGenerator {
952public: 955public:
953 GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code, 956 GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code,
954 u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) 957 u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix,
958 std::size_t shader_length)
955 : subroutines(subroutines), program_code(program_code), main_offset(main_offset), 959 : subroutines(subroutines), program_code(program_code), main_offset(main_offset),
956 stage(stage), suffix(suffix) { 960 stage(stage), suffix(suffix), shader_length(shader_length) {
957 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); 961 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
958 local_memory_size = header.GetLocalMemorySize(); 962 local_memory_size = header.GetLocalMemorySize();
959 regs.SetLocalMemory(local_memory_size); 963 regs.SetLocalMemory(local_memory_size);
@@ -966,7 +970,7 @@ public:
966 970
967 /// Returns entries in the shader that are useful for external functions 971 /// Returns entries in the shader that are useful for external functions
968 ShaderEntries GetEntries() const { 972 ShaderEntries GetEntries() const {
969 return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()}; 973 return {regs.GetConstBuffersDeclarations(), regs.GetSamplers(), shader_length};
970 } 974 }
971 975
972private: 976private:
@@ -1071,19 +1075,26 @@ private:
1071 const std::string& op_a, const std::string& op_b) const { 1075 const std::string& op_a, const std::string& op_b) const {
1072 using Tegra::Shader::PredCondition; 1076 using Tegra::Shader::PredCondition;
1073 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { 1077 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = {
1074 {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, 1078 {PredCondition::LessThan, "<"},
1075 {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, 1079 {PredCondition::Equal, "=="},
1076 {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, 1080 {PredCondition::LessEqual, "<="},
1077 {PredCondition::LessThanWithNan, "<"}, {PredCondition::NotEqualWithNan, "!="}, 1081 {PredCondition::GreaterThan, ">"},
1078 {PredCondition::GreaterThanWithNan, ">"}, {PredCondition::GreaterEqualWithNan, ">="}}; 1082 {PredCondition::NotEqual, "!="},
1083 {PredCondition::GreaterEqual, ">="},
1084 {PredCondition::LessThanWithNan, "<"},
1085 {PredCondition::NotEqualWithNan, "!="},
1086 {PredCondition::LessEqualWithNan, "<="},
1087 {PredCondition::GreaterThanWithNan, ">"},
1088 {PredCondition::GreaterEqualWithNan, ">="}};
1079 1089
1080 const auto& comparison{PredicateComparisonStrings.find(condition)}; 1090 const auto& comparison{PredicateComparisonStrings.find(condition)};
1081 ASSERT_MSG(comparison != PredicateComparisonStrings.end(), 1091 UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonStrings.end(),
1082 "Unknown predicate comparison operation"); 1092 "Unknown predicate comparison operation");
1083 1093
1084 std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; 1094 std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'};
1085 if (condition == PredCondition::LessThanWithNan || 1095 if (condition == PredCondition::LessThanWithNan ||
1086 condition == PredCondition::NotEqualWithNan || 1096 condition == PredCondition::NotEqualWithNan ||
1097 condition == PredCondition::LessEqualWithNan ||
1087 condition == PredCondition::GreaterThanWithNan || 1098 condition == PredCondition::GreaterThanWithNan ||
1088 condition == PredCondition::GreaterEqualWithNan) { 1099 condition == PredCondition::GreaterEqualWithNan) {
1089 predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; 1100 predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')';
@@ -1107,7 +1118,7 @@ private:
1107 }; 1118 };
1108 1119
1109 auto op = PredicateOperationStrings.find(operation); 1120 auto op = PredicateOperationStrings.find(operation);
1110 ASSERT_MSG(op != PredicateOperationStrings.end(), "Unknown predicate operation"); 1121 UNIMPLEMENTED_IF_MSG(op == PredicateOperationStrings.end(), "Unknown predicate operation");
1111 return op->second; 1122 return op->second;
1112 } 1123 }
1113 1124
@@ -1205,8 +1216,7 @@ private:
1205 break; 1216 break;
1206 } 1217 }
1207 default: 1218 default:
1208 LOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op)); 1219 UNIMPLEMENTED_MSG("Unimplemented logic operation={}", static_cast<u32>(logic_op));
1209 UNREACHABLE();
1210 } 1220 }
1211 1221
1212 if (dest != Tegra::Shader::Register::ZeroIndex) { 1222 if (dest != Tegra::Shader::Register::ZeroIndex) {
@@ -1224,9 +1234,8 @@ private:
1224 SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0"); 1234 SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0");
1225 break; 1235 break;
1226 default: 1236 default:
1227 LOG_CRITICAL(HW_GPU, "Unimplemented predicate result mode: {}", 1237 UNIMPLEMENTED_MSG("Unimplemented predicate result mode: {}",
1228 static_cast<u32>(predicate_mode)); 1238 static_cast<u32>(predicate_mode));
1229 UNREACHABLE();
1230 } 1239 }
1231 } 1240 }
1232 1241
@@ -1257,14 +1266,7 @@ private:
1257 regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); 1266 regs.SetRegisterToInteger(dest, true, 0, result, 1, 1);
1258 } 1267 }
1259 1268
1260 void WriteTexsInstruction(const Instruction& instr, const std::string& coord, 1269 void WriteTexsInstruction(const Instruction& instr, const std::string& texture) {
1261 const std::string& texture) {
1262 // Add an extra scope and declare the texture coords inside to prevent
1263 // overwriting them in case they are used as outputs of the texs instruction.
1264 shader.AddLine('{');
1265 ++shader.scope;
1266 shader.AddLine(coord);
1267
1268 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle 1270 // 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 1271 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
1270 1272
@@ -1287,26 +1289,19 @@ private:
1287 1289
1288 ++written_components; 1290 ++written_components;
1289 } 1291 }
1290
1291 --shader.scope;
1292 shader.AddLine('}');
1293 } 1292 }
1294 1293
1295 static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { 1294 static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) {
1296 switch (texture_type) { 1295 switch (texture_type) {
1297 case Tegra::Shader::TextureType::Texture1D: { 1296 case Tegra::Shader::TextureType::Texture1D:
1298 return 1; 1297 return 1;
1299 } 1298 case Tegra::Shader::TextureType::Texture2D:
1300 case Tegra::Shader::TextureType::Texture2D: {
1301 return 2; 1299 return 2;
1302 }
1303 case Tegra::Shader::TextureType::Texture3D: 1300 case Tegra::Shader::TextureType::Texture3D:
1304 case Tegra::Shader::TextureType::TextureCube: { 1301 case Tegra::Shader::TextureType::TextureCube:
1305 return 3; 1302 return 3;
1306 }
1307 default: 1303 default:
1308 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", static_cast<u32>(texture_type)); 1304 UNIMPLEMENTED_MSG("Unhandled texture type: {}", static_cast<u32>(texture_type));
1309 UNREACHABLE();
1310 return 0; 1305 return 0;
1311 } 1306 }
1312 } 1307 }
@@ -1342,7 +1337,7 @@ private:
1342 void EmitFragmentOutputsWrite() { 1337 void EmitFragmentOutputsWrite() {
1343 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); 1338 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
1344 1339
1345 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); 1340 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Samplemask write is unimplemented");
1346 1341
1347 shader.AddLine("if (alpha_test[0] != 0) {"); 1342 shader.AddLine("if (alpha_test[0] != 0) {");
1348 ++shader.scope; 1343 ++shader.scope;
@@ -1408,7 +1403,7 @@ private:
1408 case Tegra::Shader::VideoType::Size32: 1403 case Tegra::Shader::VideoType::Size32:
1409 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when 1404 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
1410 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better 1405 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
1411 // explanation is found: assert. 1406 // explanation is found: abort.
1412 UNIMPLEMENTED(); 1407 UNIMPLEMENTED();
1413 return zero; 1408 return zero;
1414 case Tegra::Shader::VideoType::Invalid: 1409 case Tegra::Shader::VideoType::Invalid:
@@ -1464,8 +1459,7 @@ private:
1464 1459
1465 // Decoding failure 1460 // Decoding failure
1466 if (!opcode) { 1461 if (!opcode) {
1467 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); 1462 UNIMPLEMENTED_MSG("Unhandled instruction: {0:x}", instr.value);
1468 UNREACHABLE();
1469 return offset + 1; 1463 return offset + 1;
1470 } 1464 }
1471 1465
@@ -1473,8 +1467,8 @@ private:
1473 fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value)); 1467 fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value));
1474 1468
1475 using Tegra::Shader::Pred; 1469 using Tegra::Shader::Pred;
1476 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, 1470 UNIMPLEMENTED_IF_MSG(instr.pred.full_pred == Pred::NeverExecute,
1477 "NeverExecute predicate not implemented"); 1471 "NeverExecute predicate not implemented");
1478 1472
1479 // Some instructions (like SSY) don't have a predicate field, they are always 1473 // Some instructions (like SSY) don't have a predicate field, they are always
1480 // unconditionally executed. 1474 // unconditionally executed.
@@ -1517,37 +1511,36 @@ private:
1517 case OpCode::Id::FMUL_R: 1511 case OpCode::Id::FMUL_R:
1518 case OpCode::Id::FMUL_IMM: { 1512 case OpCode::Id::FMUL_IMM: {
1519 // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. 1513 // 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", 1514 UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0,
1521 instr.fmul.tab5cb8_2.Value()); 1515 "FMUL tab5cb8_2({}) is not implemented",
1522 ASSERT_MSG(instr.fmul.tab5c68_1 == 0, "FMUL tab5cb8_1({}) is not implemented", 1516 instr.fmul.tab5cb8_2.Value());
1523 instr.fmul.tab5c68_1.Value()); 1517 UNIMPLEMENTED_IF_MSG(instr.fmul.tab5c68_1 != 0,
1524 ASSERT_MSG(instr.fmul.tab5c68_0 == 1, "FMUL tab5cb8_0({}) is not implemented", 1518 "FMUL tab5cb8_1({}) is not implemented",
1525 instr.fmul.tab5c68_0 1519 instr.fmul.tab5c68_1.Value());
1526 .Value()); // SMO typical sends 1 here which seems to be the default 1520 UNIMPLEMENTED_IF_MSG(
1527 ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented"); 1521 instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented",
1522 instr.fmul.tab5c68_0
1523 .Value()); // SMO typical sends 1 here which seems to be the default
1524 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1525 "Condition codes generation in FMUL is not implemented");
1528 1526
1529 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); 1527 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
1530 1528
1531 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, 1529 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
1532 instr.alu.saturate_d, 0, true); 1530 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; 1531 break;
1538 } 1532 }
1539 case OpCode::Id::FADD_C: 1533 case OpCode::Id::FADD_C:
1540 case OpCode::Id::FADD_R: 1534 case OpCode::Id::FADD_R:
1541 case OpCode::Id::FADD_IMM: { 1535 case OpCode::Id::FADD_IMM: {
1536 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1537 "Condition codes generation in FADD is not implemented");
1538
1542 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); 1539 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); 1540 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
1544 1541
1545 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, 1542 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
1546 instr.alu.saturate_d, 0, true); 1543 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; 1544 break;
1552 } 1545 }
1553 case OpCode::Id::MUFU: { 1546 case OpCode::Id::MUFU: {
@@ -1582,15 +1575,17 @@ private:
1582 instr.alu.saturate_d, 0, true); 1575 instr.alu.saturate_d, 0, true);
1583 break; 1576 break;
1584 default: 1577 default:
1585 LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", 1578 UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}",
1586 static_cast<unsigned>(instr.sub_op.Value())); 1579 static_cast<unsigned>(instr.sub_op.Value()));
1587 UNREACHABLE();
1588 } 1580 }
1589 break; 1581 break;
1590 } 1582 }
1591 case OpCode::Id::FMNMX_C: 1583 case OpCode::Id::FMNMX_C:
1592 case OpCode::Id::FMNMX_R: 1584 case OpCode::Id::FMNMX_R:
1593 case OpCode::Id::FMNMX_IMM: { 1585 case OpCode::Id::FMNMX_IMM: {
1586 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1587 "Condition codes generation in FMNMX is not implemented");
1588
1594 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); 1589 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); 1590 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
1596 1591
@@ -1601,10 +1596,6 @@ private:
1601 '(' + condition + ") ? min(" + parameters + ") : max(" + 1596 '(' + condition + ") ? min(" + parameters + ") : max(" +
1602 parameters + ')', 1597 parameters + ')',
1603 1, 1, false, 0, true); 1598 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; 1599 break;
1609 } 1600 }
1610 case OpCode::Id::RRO_C: 1601 case OpCode::Id::RRO_C:
@@ -1617,9 +1608,7 @@ private:
1617 break; 1608 break;
1618 } 1609 }
1619 default: { 1610 default: {
1620 LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", 1611 UNIMPLEMENTED_MSG("Unhandled arithmetic instruction: {}", opcode->get().GetName());
1621 opcode->get().GetName());
1622 UNREACHABLE();
1623 } 1612 }
1624 } 1613 }
1625 break; 1614 break;
@@ -1631,17 +1620,19 @@ private:
1631 break; 1620 break;
1632 } 1621 }
1633 case OpCode::Id::FMUL32_IMM: { 1622 case OpCode::Id::FMUL32_IMM: {
1623 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1624 "Condition codes generation in FMUL32 is not implemented");
1625
1634 regs.SetRegisterToFloat(instr.gpr0, 0, 1626 regs.SetRegisterToFloat(instr.gpr0, 0,
1635 regs.GetRegisterAsFloat(instr.gpr8) + " * " + 1627 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1636 GetImmediate32(instr), 1628 GetImmediate32(instr),
1637 1, 1, instr.fmul32.saturate, 0, true); 1629 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; 1630 break;
1643 } 1631 }
1644 case OpCode::Id::FADD32I: { 1632 case OpCode::Id::FADD32I: {
1633 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1634 "Condition codes generation in FADD32I is not implemented");
1635
1645 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1636 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1646 std::string op_b = GetImmediate32(instr); 1637 std::string op_b = GetImmediate32(instr);
1647 1638
@@ -1662,23 +1653,22 @@ private:
1662 } 1653 }
1663 1654
1664 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); 1655 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; 1656 break;
1670 } 1657 }
1671 } 1658 }
1672 break; 1659 break;
1673 } 1660 }
1674 case OpCode::Type::Bfe: { 1661 case OpCode::Type::Bfe: {
1675 ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented"); 1662 UNIMPLEMENTED_IF(instr.bfe.negate_b);
1676 1663
1677 std::string op_a = instr.bfe.negate_a ? "-" : ""; 1664 std::string op_a = instr.bfe.negate_a ? "-" : "";
1678 op_a += regs.GetRegisterAsInteger(instr.gpr8); 1665 op_a += regs.GetRegisterAsInteger(instr.gpr8);
1679 1666
1680 switch (opcode->get().GetId()) { 1667 switch (opcode->get().GetId()) {
1681 case OpCode::Id::BFE_IMM: { 1668 case OpCode::Id::BFE_IMM: {
1669 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1670 "Condition codes generation in BFE is not implemented");
1671
1682 std::string inner_shift = 1672 std::string inner_shift =
1683 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; 1673 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
1684 std::string outer_shift = 1674 std::string outer_shift =
@@ -1686,15 +1676,10 @@ private:
1686 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; 1676 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
1687 1677
1688 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); 1678 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; 1679 break;
1694 } 1680 }
1695 default: { 1681 default: {
1696 LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->get().GetName()); 1682 UNIMPLEMENTED_MSG("Unhandled BFE instruction: {}", opcode->get().GetName());
1697 UNREACHABLE();
1698 } 1683 }
1699 } 1684 }
1700 1685
@@ -1719,6 +1704,9 @@ private:
1719 case OpCode::Id::SHR_C: 1704 case OpCode::Id::SHR_C:
1720 case OpCode::Id::SHR_R: 1705 case OpCode::Id::SHR_R:
1721 case OpCode::Id::SHR_IMM: { 1706 case OpCode::Id::SHR_IMM: {
1707 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1708 "Condition codes generation in SHR is not implemented");
1709
1722 if (!instr.shift.is_signed) { 1710 if (!instr.shift.is_signed) {
1723 // Logical shift right 1711 // Logical shift right
1724 op_a = "uint(" + op_a + ')'; 1712 op_a = "uint(" + op_a + ')';
@@ -1727,24 +1715,17 @@ private:
1727 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift 1715 // 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 + ')', 1716 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
1729 1, 1); 1717 1, 1);
1730 if (instr.generates_cc) {
1731 LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code");
1732 UNREACHABLE();
1733 }
1734 break; 1718 break;
1735 } 1719 }
1736 case OpCode::Id::SHL_C: 1720 case OpCode::Id::SHL_C:
1737 case OpCode::Id::SHL_R: 1721 case OpCode::Id::SHL_R:
1738 case OpCode::Id::SHL_IMM: 1722 case OpCode::Id::SHL_IMM:
1723 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1724 "Condition codes generation in SHL is not implemented");
1739 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); 1725 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; 1726 break;
1745 default: { 1727 default: {
1746 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName()); 1728 UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName());
1747 UNREACHABLE();
1748 } 1729 }
1749 } 1730 }
1750 break; 1731 break;
@@ -1755,17 +1736,19 @@ private:
1755 1736
1756 switch (opcode->get().GetId()) { 1737 switch (opcode->get().GetId()) {
1757 case OpCode::Id::IADD32I: 1738 case OpCode::Id::IADD32I:
1739 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1740 "Condition codes generation in IADD32I is not implemented");
1741
1758 if (instr.iadd32i.negate_a) 1742 if (instr.iadd32i.negate_a)
1759 op_a = "-(" + op_a + ')'; 1743 op_a = "-(" + op_a + ')';
1760 1744
1761 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1745 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1762 instr.iadd32i.saturate != 0); 1746 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; 1747 break;
1768 case OpCode::Id::LOP32I: { 1748 case OpCode::Id::LOP32I: {
1749 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1750 "Condition codes generation in LOP32I is not implemented");
1751
1769 if (instr.alu.lop32i.invert_a) 1752 if (instr.alu.lop32i.invert_a)
1770 op_a = "~(" + op_a + ')'; 1753 op_a = "~(" + op_a + ')';
1771 1754
@@ -1775,16 +1758,11 @@ private:
1775 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, 1758 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
1776 Tegra::Shader::PredicateResultMode::None, 1759 Tegra::Shader::PredicateResultMode::None,
1777 Tegra::Shader::Pred::UnusedIndex); 1760 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; 1761 break;
1783 } 1762 }
1784 default: { 1763 default: {
1785 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}", 1764 UNIMPLEMENTED_MSG("Unhandled ArithmeticIntegerImmediate instruction: {}",
1786 opcode->get().GetName()); 1765 opcode->get().GetName());
1787 UNREACHABLE();
1788 } 1766 }
1789 } 1767 }
1790 break; 1768 break;
@@ -1807,6 +1785,9 @@ private:
1807 case OpCode::Id::IADD_C: 1785 case OpCode::Id::IADD_C:
1808 case OpCode::Id::IADD_R: 1786 case OpCode::Id::IADD_R:
1809 case OpCode::Id::IADD_IMM: { 1787 case OpCode::Id::IADD_IMM: {
1788 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1789 "Condition codes generation in IADD is not implemented");
1790
1810 if (instr.alu_integer.negate_a) 1791 if (instr.alu_integer.negate_a)
1811 op_a = "-(" + op_a + ')'; 1792 op_a = "-(" + op_a + ')';
1812 1793
@@ -1815,15 +1796,14 @@ private:
1815 1796
1816 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1797 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1817 instr.alu.saturate_d); 1798 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; 1799 break;
1823 } 1800 }
1824 case OpCode::Id::IADD3_C: 1801 case OpCode::Id::IADD3_C:
1825 case OpCode::Id::IADD3_R: 1802 case OpCode::Id::IADD3_R:
1826 case OpCode::Id::IADD3_IMM: { 1803 case OpCode::Id::IADD3_IMM: {
1804 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1805 "Condition codes generation in IADD3 is not implemented");
1806
1827 std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); 1807 std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
1828 1808
1829 auto apply_height = [](auto height, auto& oprand) { 1809 auto apply_height = [](auto height, auto& oprand) {
@@ -1837,9 +1817,8 @@ private:
1837 oprand = "((" + oprand + ") >> 16)"; 1817 oprand = "((" + oprand + ") >> 16)";
1838 break; 1818 break;
1839 default: 1819 default:
1840 LOG_CRITICAL(HW_GPU, "Unhandled IADD3 height: {}", 1820 UNIMPLEMENTED_MSG("Unhandled IADD3 height: {}",
1841 static_cast<u32>(height.Value())); 1821 static_cast<u32>(height.Value()));
1842 UNREACHABLE();
1843 } 1822 }
1844 }; 1823 };
1845 1824
@@ -1880,16 +1859,14 @@ private:
1880 } 1859 }
1881 1860
1882 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); 1861 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; 1862 break;
1889 } 1863 }
1890 case OpCode::Id::ISCADD_C: 1864 case OpCode::Id::ISCADD_C:
1891 case OpCode::Id::ISCADD_R: 1865 case OpCode::Id::ISCADD_R:
1892 case OpCode::Id::ISCADD_IMM: { 1866 case OpCode::Id::ISCADD_IMM: {
1867 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1868 "Condition codes generation in ISCADD is not implemented");
1869
1893 if (instr.alu_integer.negate_a) 1870 if (instr.alu_integer.negate_a)
1894 op_a = "-(" + op_a + ')'; 1871 op_a = "-(" + op_a + ')';
1895 1872
@@ -1900,10 +1877,6 @@ private:
1900 1877
1901 regs.SetRegisterToInteger(instr.gpr0, true, 0, 1878 regs.SetRegisterToInteger(instr.gpr0, true, 0,
1902 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); 1879 "((" + 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; 1880 break;
1908 } 1881 }
1909 case OpCode::Id::POPC_C: 1882 case OpCode::Id::POPC_C:
@@ -1927,6 +1900,9 @@ private:
1927 case OpCode::Id::LOP_C: 1900 case OpCode::Id::LOP_C:
1928 case OpCode::Id::LOP_R: 1901 case OpCode::Id::LOP_R:
1929 case OpCode::Id::LOP_IMM: { 1902 case OpCode::Id::LOP_IMM: {
1903 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1904 "Condition codes generation in LOP is not implemented");
1905
1930 if (instr.alu.lop.invert_a) 1906 if (instr.alu.lop.invert_a)
1931 op_a = "~(" + op_a + ')'; 1907 op_a = "~(" + op_a + ')';
1932 1908
@@ -1935,15 +1911,14 @@ private:
1935 1911
1936 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, 1912 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
1937 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); 1913 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; 1914 break;
1943 } 1915 }
1944 case OpCode::Id::LOP3_C: 1916 case OpCode::Id::LOP3_C:
1945 case OpCode::Id::LOP3_R: 1917 case OpCode::Id::LOP3_R:
1946 case OpCode::Id::LOP3_IMM: { 1918 case OpCode::Id::LOP3_IMM: {
1919 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1920 "Condition codes generation in LOP3 is not implemented");
1921
1947 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); 1922 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
1948 std::string lut; 1923 std::string lut;
1949 1924
@@ -1954,17 +1929,15 @@ private:
1954 } 1929 }
1955 1930
1956 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); 1931 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; 1932 break;
1962 } 1933 }
1963 case OpCode::Id::IMNMX_C: 1934 case OpCode::Id::IMNMX_C:
1964 case OpCode::Id::IMNMX_R: 1935 case OpCode::Id::IMNMX_R:
1965 case OpCode::Id::IMNMX_IMM: { 1936 case OpCode::Id::IMNMX_IMM: {
1966 ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, 1937 UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None);
1967 "Unimplemented"); 1938 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1939 "Condition codes generation in IMNMX is not implemented");
1940
1968 const std::string condition = 1941 const std::string condition =
1969 GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); 1942 GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
1970 const std::string parameters = op_a + ',' + op_b; 1943 const std::string parameters = op_a + ',' + op_b;
@@ -1972,10 +1945,6 @@ private:
1972 '(' + condition + ") ? min(" + parameters + ") : max(" + 1945 '(' + condition + ") ? min(" + parameters + ") : max(" +
1973 parameters + ')', 1946 parameters + ')',
1974 1, 1); 1947 1, 1);
1975 if (instr.generates_cc) {
1976 LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code");
1977 UNREACHABLE();
1978 }
1979 break; 1948 break;
1980 } 1949 }
1981 case OpCode::Id::LEA_R2: 1950 case OpCode::Id::LEA_R2:
@@ -2030,24 +1999,19 @@ private:
2030 op_b = regs.GetRegisterAsInteger(instr.gpr8); 1999 op_b = regs.GetRegisterAsInteger(instr.gpr8);
2031 op_a = std::to_string(instr.lea.imm.entry_a); 2000 op_a = std::to_string(instr.lea.imm.entry_a);
2032 op_c = std::to_string(instr.lea.imm.entry_b); 2001 op_c = std::to_string(instr.lea.imm.entry_b);
2033 LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", 2002 UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName());
2034 opcode->get().GetName());
2035 UNREACHABLE();
2036 }
2037 } 2003 }
2038 if (instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex)) {
2039 LOG_ERROR(HW_GPU, "Unhandled LEA Predicate");
2040 UNREACHABLE();
2041 } 2004 }
2005 UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
2006 "Unhandled LEA Predicate");
2042 const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))"; 2007 const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))";
2043 regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1); 2008 regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1);
2044 2009
2045 break; 2010 break;
2046 } 2011 }
2047 default: { 2012 default: {
2048 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", 2013 UNIMPLEMENTED_MSG("Unhandled ArithmeticInteger instruction: {}",
2049 opcode->get().GetName()); 2014 opcode->get().GetName());
2050 UNREACHABLE();
2051 } 2015 }
2052 } 2016 }
2053 2017
@@ -2056,7 +2020,7 @@ private:
2056 case OpCode::Type::ArithmeticHalf: { 2020 case OpCode::Type::ArithmeticHalf: {
2057 if (opcode->get().GetId() == OpCode::Id::HADD2_C || 2021 if (opcode->get().GetId() == OpCode::Id::HADD2_C ||
2058 opcode->get().GetId() == OpCode::Id::HADD2_R) { 2022 opcode->get().GetId() == OpCode::Id::HADD2_R) {
2059 ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented"); 2023 UNIMPLEMENTED_IF(instr.alu_half.ftz != 0);
2060 } 2024 }
2061 const bool negate_a = 2025 const bool negate_a =
2062 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; 2026 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
@@ -2094,9 +2058,8 @@ private:
2094 case OpCode::Id::HMUL2_R: 2058 case OpCode::Id::HMUL2_R:
2095 return '(' + op_a + " * " + op_b + ')'; 2059 return '(' + op_a + " * " + op_b + ')';
2096 default: 2060 default:
2097 LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", 2061 UNIMPLEMENTED_MSG("Unhandled half float instruction: {}",
2098 opcode->get().GetName()); 2062 opcode->get().GetName());
2099 UNREACHABLE();
2100 return std::string("0"); 2063 return std::string("0");
2101 } 2064 }
2102 }(); 2065 }();
@@ -2107,10 +2070,10 @@ private:
2107 } 2070 }
2108 case OpCode::Type::ArithmeticHalfImmediate: { 2071 case OpCode::Type::ArithmeticHalfImmediate: {
2109 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { 2072 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) {
2110 ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented"); 2073 UNIMPLEMENTED_IF(instr.alu_half_imm.ftz != 0);
2111 } else { 2074 } else {
2112 ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None, 2075 UNIMPLEMENTED_IF(instr.alu_half_imm.precision !=
2113 "Unimplemented"); 2076 Tegra::Shader::HalfPrecision::None);
2114 } 2077 }
2115 2078
2116 const std::string op_a = GetHalfFloat( 2079 const std::string op_a = GetHalfFloat(
@@ -2140,11 +2103,14 @@ private:
2140 std::string op_b = instr.ffma.negate_b ? "-" : ""; 2103 std::string op_b = instr.ffma.negate_b ? "-" : "";
2141 std::string op_c = instr.ffma.negate_c ? "-" : ""; 2104 std::string op_c = instr.ffma.negate_c ? "-" : "";
2142 2105
2143 ASSERT_MSG(instr.ffma.cc == 0, "FFMA cc not implemented"); 2106 UNIMPLEMENTED_IF_MSG(instr.ffma.cc != 0, "FFMA cc not implemented");
2144 ASSERT_MSG(instr.ffma.tab5980_0 == 1, "FFMA tab5980_0({}) not implemented", 2107 UNIMPLEMENTED_IF_MSG(
2145 instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO 2108 instr.ffma.tab5980_0 != 1, "FFMA tab5980_0({}) not implemented",
2146 ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented", 2109 instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO
2147 instr.ffma.tab5980_1.Value()); 2110 UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented",
2111 instr.ffma.tab5980_1.Value());
2112 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2113 "Condition codes generation in FFMA is not implemented");
2148 2114
2149 switch (opcode->get().GetId()) { 2115 switch (opcode->get().GetId()) {
2150 case OpCode::Id::FFMA_CR: { 2116 case OpCode::Id::FFMA_CR: {
@@ -2170,27 +2136,19 @@ private:
2170 break; 2136 break;
2171 } 2137 }
2172 default: { 2138 default: {
2173 LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->get().GetName()); 2139 UNIMPLEMENTED_MSG("Unhandled FFMA instruction: {}", opcode->get().GetName());
2174 UNREACHABLE();
2175 } 2140 }
2176 } 2141 }
2177 2142
2178 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', 2143 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
2179 1, 1, instr.alu.saturate_d, 0, true); 2144 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; 2145 break;
2186 } 2146 }
2187 case OpCode::Type::Hfma2: { 2147 case OpCode::Type::Hfma2: {
2188 if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) { 2148 if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) {
2189 ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None, 2149 UNIMPLEMENTED_IF(instr.hfma2.rr.precision != Tegra::Shader::HalfPrecision::None);
2190 "Unimplemented");
2191 } else { 2150 } else {
2192 ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None, 2151 UNIMPLEMENTED_IF(instr.hfma2.precision != Tegra::Shader::HalfPrecision::None);
2193 "Unimplemented");
2194 } 2152 }
2195 const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR 2153 const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR
2196 ? instr.hfma2.rr.saturate != 0 2154 ? instr.hfma2.rr.saturate != 0
@@ -2240,7 +2198,7 @@ private:
2240 case OpCode::Type::Conversion: { 2198 case OpCode::Type::Conversion: {
2241 switch (opcode->get().GetId()) { 2199 switch (opcode->get().GetId()) {
2242 case OpCode::Id::I2I_R: { 2200 case OpCode::Id::I2I_R: {
2243 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 2201 UNIMPLEMENTED_IF(instr.conversion.selector);
2244 2202
2245 std::string op_a = regs.GetRegisterAsInteger( 2203 std::string op_a = regs.GetRegisterAsInteger(
2246 instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size); 2204 instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size);
@@ -2260,8 +2218,10 @@ private:
2260 } 2218 }
2261 case OpCode::Id::I2F_R: 2219 case OpCode::Id::I2F_R:
2262 case OpCode::Id::I2F_C: { 2220 case OpCode::Id::I2F_C: {
2263 ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); 2221 UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
2264 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 2222 UNIMPLEMENTED_IF(instr.conversion.selector);
2223 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2224 "Condition codes generation in I2F is not implemented");
2265 2225
2266 std::string op_a{}; 2226 std::string op_a{};
2267 2227
@@ -2286,16 +2246,13 @@ private:
2286 } 2246 }
2287 2247
2288 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 2248 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; 2249 break;
2295 } 2250 }
2296 case OpCode::Id::F2F_R: { 2251 case OpCode::Id::F2F_R: {
2297 ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); 2252 UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
2298 ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); 2253 UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
2254 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2255 "Condition codes generation in F2F is not implemented");
2299 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); 2256 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
2300 2257
2301 if (instr.conversion.abs_a) { 2258 if (instr.conversion.abs_a) {
@@ -2322,23 +2279,19 @@ private:
2322 op_a = "trunc(" + op_a + ')'; 2279 op_a = "trunc(" + op_a + ')';
2323 break; 2280 break;
2324 default: 2281 default:
2325 LOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}", 2282 UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}",
2326 static_cast<u32>(instr.conversion.f2f.rounding.Value())); 2283 static_cast<u32>(instr.conversion.f2f.rounding.Value()));
2327 UNREACHABLE();
2328 break; 2284 break;
2329 } 2285 }
2330 2286
2331 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); 2287 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; 2288 break;
2338 } 2289 }
2339 case OpCode::Id::F2I_R: 2290 case OpCode::Id::F2I_R:
2340 case OpCode::Id::F2I_C: { 2291 case OpCode::Id::F2I_C: {
2341 ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); 2292 UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
2293 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2294 "Condition codes generation in F2I is not implemented");
2342 std::string op_a{}; 2295 std::string op_a{};
2343 2296
2344 if (instr.is_b_gpr) { 2297 if (instr.is_b_gpr) {
@@ -2369,9 +2322,8 @@ private:
2369 op_a = "trunc(" + op_a + ')'; 2322 op_a = "trunc(" + op_a + ')';
2370 break; 2323 break;
2371 default: 2324 default:
2372 LOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", 2325 UNIMPLEMENTED_MSG("Unimplemented F2I rounding mode {}",
2373 static_cast<u32>(instr.conversion.f2i.rounding.Value())); 2326 static_cast<u32>(instr.conversion.f2i.rounding.Value()));
2374 UNREACHABLE();
2375 break; 2327 break;
2376 } 2328 }
2377 2329
@@ -2383,16 +2335,10 @@ private:
2383 2335
2384 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 2336 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
2385 1, false, 0, instr.conversion.dest_size); 2337 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; 2338 break;
2391 } 2339 }
2392 default: { 2340 default: {
2393 LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", 2341 UNIMPLEMENTED_MSG("Unhandled conversion instruction: {}", opcode->get().GetName());
2394 opcode->get().GetName());
2395 UNREACHABLE();
2396 } 2342 }
2397 } 2343 }
2398 break; 2344 break;
@@ -2401,10 +2347,10 @@ private:
2401 switch (opcode->get().GetId()) { 2347 switch (opcode->get().GetId()) {
2402 case OpCode::Id::LD_A: { 2348 case OpCode::Id::LD_A: {
2403 // Note: Shouldn't this be interp mode flat? As in no interpolation made. 2349 // Note: Shouldn't this be interp mode flat? As in no interpolation made.
2404 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2350 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
2405 "Indirect attribute loads are not supported"); 2351 "Indirect attribute loads are not supported");
2406 ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, 2352 UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
2407 "Unaligned attribute loads are not supported"); 2353 "Unaligned attribute loads are not supported");
2408 2354
2409 Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, 2355 Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective,
2410 Tegra::Shader::IpaSampleMode::Default}; 2356 Tegra::Shader::IpaSampleMode::Default};
@@ -2431,7 +2377,7 @@ private:
2431 break; 2377 break;
2432 } 2378 }
2433 case OpCode::Id::LD_C: { 2379 case OpCode::Id::LD_C: {
2434 ASSERT_MSG(instr.ld_c.unknown == 0, "Unimplemented"); 2380 UNIMPLEMENTED_IF(instr.ld_c.unknown != 0);
2435 2381
2436 // Add an extra scope and declare the index register inside to prevent 2382 // 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. 2383 // overwriting it in case it is used as an output of the LD instruction.
@@ -2459,9 +2405,8 @@ private:
2459 break; 2405 break;
2460 } 2406 }
2461 default: 2407 default:
2462 LOG_CRITICAL(HW_GPU, "Unhandled type: {}", 2408 UNIMPLEMENTED_MSG("Unhandled type: {}",
2463 static_cast<unsigned>(instr.ld_c.type.Value())); 2409 static_cast<unsigned>(instr.ld_c.type.Value()));
2464 UNREACHABLE();
2465 } 2410 }
2466 2411
2467 --shader.scope; 2412 --shader.scope;
@@ -2469,6 +2414,9 @@ private:
2469 break; 2414 break;
2470 } 2415 }
2471 case OpCode::Id::LD_L: { 2416 case OpCode::Id::LD_L: {
2417 UNIMPLEMENTED_IF_MSG(instr.ld_l.unknown == 1, "LD_L Unhandled mode: {}",
2418 static_cast<unsigned>(instr.ld_l.unknown.Value()));
2419
2472 // Add an extra scope and declare the index register inside to prevent 2420 // 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. 2421 // overwriting it in case it is used as an output of the LD instruction.
2474 shader.AddLine('{'); 2422 shader.AddLine('{');
@@ -2481,20 +2429,13 @@ private:
2481 2429
2482 const std::string op_a = regs.GetLocalMemoryAsFloat("index"); 2430 const std::string op_a = regs.GetLocalMemoryAsFloat("index");
2483 2431
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()) { 2432 switch (instr.ldst_sl.type.Value()) {
2491 case Tegra::Shader::StoreType::Bytes32: 2433 case Tegra::Shader::StoreType::Bytes32:
2492 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 2434 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2493 break; 2435 break;
2494 default: 2436 default:
2495 LOG_CRITICAL(HW_GPU, "LD_L Unhandled type: {}", 2437 UNIMPLEMENTED_MSG("LD_L Unhandled type: {}",
2496 static_cast<unsigned>(instr.ldst_sl.type.Value())); 2438 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2497 UNREACHABLE();
2498 } 2439 }
2499 2440
2500 --shader.scope; 2441 --shader.scope;
@@ -2502,10 +2443,10 @@ private:
2502 break; 2443 break;
2503 } 2444 }
2504 case OpCode::Id::ST_A: { 2445 case OpCode::Id::ST_A: {
2505 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2446 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
2506 "Indirect attribute loads are not supported"); 2447 "Indirect attribute loads are not supported");
2507 ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, 2448 UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
2508 "Unaligned attribute loads are not supported"); 2449 "Unaligned attribute loads are not supported");
2509 2450
2510 u64 next_element = instr.attribute.fmt20.element; 2451 u64 next_element = instr.attribute.fmt20.element;
2511 u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); 2452 u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
@@ -2530,6 +2471,9 @@ private:
2530 break; 2471 break;
2531 } 2472 }
2532 case OpCode::Id::ST_L: { 2473 case OpCode::Id::ST_L: {
2474 UNIMPLEMENTED_IF_MSG(instr.st_l.unknown == 0, "ST_L Unhandled mode: {}",
2475 static_cast<unsigned>(instr.st_l.unknown.Value()));
2476
2533 // 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
2534 // 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.
2535 shader.AddLine('{'); 2479 shader.AddLine('{');
@@ -2540,20 +2484,13 @@ private:
2540 2484
2541 shader.AddLine("uint index = (" + op + " / 4);"); 2485 shader.AddLine("uint index = (" + op + " / 4);");
2542 2486
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()) { 2487 switch (instr.ldst_sl.type.Value()) {
2550 case Tegra::Shader::StoreType::Bytes32: 2488 case Tegra::Shader::StoreType::Bytes32:
2551 regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0)); 2489 regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0));
2552 break; 2490 break;
2553 default: 2491 default:
2554 LOG_CRITICAL(HW_GPU, "ST_L Unhandled type: {}", 2492 UNIMPLEMENTED_MSG("ST_L Unhandled type: {}",
2555 static_cast<unsigned>(instr.ldst_sl.type.Value())); 2493 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2556 UNREACHABLE();
2557 } 2494 }
2558 2495
2559 --shader.scope; 2496 --shader.scope;
@@ -2565,10 +2502,10 @@ private:
2565 std::string coord; 2502 std::string coord;
2566 const bool is_array = instr.tex.array != 0; 2503 const bool is_array = instr.tex.array != 0;
2567 2504
2568 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2505 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2569 "NODEP is not implemented"); 2506 "NODEP is not implemented");
2570 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2507 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2571 "AOFFI is not implemented"); 2508 "AOFFI is not implemented");
2572 2509
2573 const bool depth_compare = 2510 const bool depth_compare =
2574 instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2511 instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
@@ -2634,9 +2571,8 @@ private:
2634 break; 2571 break;
2635 } 2572 }
2636 default: 2573 default:
2637 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", 2574 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2638 static_cast<u32>(num_coordinates)); 2575 static_cast<u32>(num_coordinates));
2639 UNREACHABLE();
2640 2576
2641 // Fallback to interpreting as a 2D texture for now 2577 // Fallback to interpreting as a 2D texture for now
2642 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2578 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
@@ -2646,7 +2582,6 @@ private:
2646 } 2582 }
2647 // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias 2583 // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias
2648 // or lod. 2584 // or lod.
2649 std::string op_c;
2650 2585
2651 const std::string sampler = 2586 const std::string sampler =
2652 GetSampler(instr.sampler, texture_type, is_array, depth_compare); 2587 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
@@ -2669,34 +2604,41 @@ private:
2669 } 2604 }
2670 case Tegra::Shader::TextureProcessMode::LB: 2605 case Tegra::Shader::TextureProcessMode::LB:
2671 case Tegra::Shader::TextureProcessMode::LBA: { 2606 case Tegra::Shader::TextureProcessMode::LBA: {
2672 if (depth_compare) { 2607 const std::string bias = [&]() {
2673 if (is_array) 2608 if (depth_compare) {
2674 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); 2609 if (is_array)
2675 else 2610 return regs.GetRegisterAsFloat(instr.gpr20.Value() + 2);
2676 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2611 else
2677 } else { 2612 return regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2678 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2613 } else {
2679 } 2614 return regs.GetRegisterAsFloat(instr.gpr20);
2615 }
2616 }();
2617 shader.AddLine("float bias = " + bias + ';');
2618
2680 // TODO: Figure if A suffix changes the equation at all. 2619 // TODO: Figure if A suffix changes the equation at all.
2681 texture = "texture(" + sampler + ", coords, " + op_c + ')'; 2620 texture = "texture(" + sampler + ", coords, bias)";
2682 break; 2621 break;
2683 } 2622 }
2684 case Tegra::Shader::TextureProcessMode::LL: 2623 case Tegra::Shader::TextureProcessMode::LL:
2685 case Tegra::Shader::TextureProcessMode::LLA: { 2624 case Tegra::Shader::TextureProcessMode::LLA: {
2686 if (num_coordinates <= 2) { 2625 const std::string lod = [&]() {
2687 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2626 if (num_coordinates <= 2) {
2688 } else { 2627 return regs.GetRegisterAsFloat(instr.gpr20);
2689 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2628 } else {
2690 } 2629 return regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2630 }
2631 }();
2632 shader.AddLine("float lod = " + lod + ';');
2633
2691 // TODO: Figure if A suffix changes the equation at all. 2634 // TODO: Figure if A suffix changes the equation at all.
2692 texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; 2635 texture = "textureLod(" + sampler + ", coords, lod)";
2693 break; 2636 break;
2694 } 2637 }
2695 default: { 2638 default: {
2696 texture = "texture(" + sampler + ", coords)"; 2639 texture = "texture(" + sampler + ", coords)";
2697 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2640 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2698 static_cast<u32>(instr.tex.GetTextureProcessMode())); 2641 static_cast<u32>(instr.tex.GetTextureProcessMode()));
2699 UNREACHABLE();
2700 } 2642 }
2701 } 2643 }
2702 if (!depth_compare) { 2644 if (!depth_compare) {
@@ -2717,12 +2659,11 @@ private:
2717 break; 2659 break;
2718 } 2660 }
2719 case OpCode::Id::TEXS: { 2661 case OpCode::Id::TEXS: {
2720 std::string coord;
2721 Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; 2662 Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
2722 bool is_array{instr.texs.IsArrayTexture()}; 2663 bool is_array{instr.texs.IsArrayTexture()};
2723 2664
2724 ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2665 UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2725 "NODEP is not implemented"); 2666 "NODEP is not implemented");
2726 2667
2727 const bool depth_compare = 2668 const bool depth_compare =
2728 instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2669 instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
@@ -2730,17 +2671,21 @@ private:
2730 if (depth_compare) 2671 if (depth_compare)
2731 num_coordinates += 1; 2672 num_coordinates += 1;
2732 2673
2674 // Scope to avoid variable name overlaps.
2675 shader.AddLine('{');
2676 ++shader.scope;
2677
2733 switch (num_coordinates) { 2678 switch (num_coordinates) {
2734 case 2: { 2679 case 2: {
2735 if (is_array) { 2680 if (is_array) {
2736 const std::string index = regs.GetRegisterAsInteger(instr.gpr8); 2681 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2737 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2682 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2738 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2683 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2739 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; 2684 shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + index + ");");
2740 } else { 2685 } else {
2741 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2686 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2742 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2687 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2743 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2688 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2744 } 2689 }
2745 break; 2690 break;
2746 } 2691 }
@@ -2750,25 +2695,24 @@ private:
2750 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2695 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2751 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); 2696 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2752 const std::string z = regs.GetRegisterAsFloat(instr.gpr20); 2697 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2753 coord = 2698 shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " +
2754 "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");"; 2699 index + ");");
2755 } else { 2700 } else {
2756 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2701 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2757 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2702 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2758 const std::string z = regs.GetRegisterAsFloat(instr.gpr20); 2703 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2759 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2704 shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");");
2760 } 2705 }
2761 break; 2706 break;
2762 } 2707 }
2763 default: 2708 default:
2764 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", 2709 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2765 static_cast<u32>(num_coordinates)); 2710 static_cast<u32>(num_coordinates));
2766 UNREACHABLE();
2767 2711
2768 // Fallback to interpreting as a 2D texture for now 2712 // Fallback to interpreting as a 2D texture for now
2769 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2713 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2770 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2714 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2771 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2715 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2772 texture_type = Tegra::Shader::TextureType::Texture2D; 2716 texture_type = Tegra::Shader::TextureType::Texture2D;
2773 is_array = false; 2717 is_array = false;
2774 } 2718 }
@@ -2795,57 +2739,57 @@ private:
2795 } 2739 }
2796 default: { 2740 default: {
2797 texture = "texture(" + sampler + ", coords)"; 2741 texture = "texture(" + sampler + ", coords)";
2798 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2742 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2799 static_cast<u32>(instr.texs.GetTextureProcessMode())); 2743 static_cast<u32>(instr.texs.GetTextureProcessMode()));
2800 UNREACHABLE();
2801 } 2744 }
2802 } 2745 }
2803 if (!depth_compare) { 2746 if (!depth_compare) {
2804 WriteTexsInstruction(instr, coord, texture); 2747 WriteTexsInstruction(instr, texture);
2805 } else { 2748 } else {
2806 WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); 2749 WriteTexsInstruction(instr, "vec4(" + texture + ')');
2807 } 2750 }
2751
2752 shader.AddLine('}');
2753 --shader.scope;
2808 break; 2754 break;
2809 } 2755 }
2810 case OpCode::Id::TLDS: { 2756 case OpCode::Id::TLDS: {
2811 std::string coord;
2812 const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; 2757 const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()};
2813 const bool is_array{instr.tlds.IsArrayTexture()}; 2758 const bool is_array{instr.tlds.IsArrayTexture()};
2814 2759
2815 ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D); 2760 ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D);
2816 ASSERT(is_array == false); 2761 ASSERT(is_array == false);
2817 2762
2818 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2763 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2819 "NODEP is not implemented"); 2764 "NODEP is not implemented");
2820 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2765 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2821 "AOFFI is not implemented"); 2766 "AOFFI is not implemented");
2822 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), 2767 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2823 "MZ is not implemented"); 2768 "MZ is not implemented");
2824 2769
2825 u32 op_c_offset = 0; 2770 u32 extra_op_offset = 0;
2771
2772 // Scope to avoid variable name overlaps.
2773 shader.AddLine('{');
2774 ++shader.scope;
2826 2775
2827 switch (texture_type) { 2776 switch (texture_type) {
2828 case Tegra::Shader::TextureType::Texture1D: { 2777 case Tegra::Shader::TextureType::Texture1D: {
2829 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2778 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2830 coord = "int coords = " + x + ';'; 2779 shader.AddLine("int coords = " + x + ';');
2831 break; 2780 break;
2832 } 2781 }
2833 case Tegra::Shader::TextureType::Texture2D: { 2782 case Tegra::Shader::TextureType::Texture2D: {
2834 if (is_array) { 2783 UNIMPLEMENTED_IF_MSG(is_array, "Unhandled 2d array texture");
2835 LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture"); 2784
2836 UNREACHABLE(); 2785 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2837 } else { 2786 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2838 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2787 shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");");
2839 const std::string y = regs.GetRegisterAsInteger(instr.gpr20); 2788 extra_op_offset = 1;
2840 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
2841 op_c_offset = 1;
2842 }
2843 break; 2789 break;
2844 } 2790 }
2845 default: 2791 default:
2846 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", 2792 UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type));
2847 static_cast<u32>(texture_type));
2848 UNREACHABLE();
2849 } 2793 }
2850 const std::string sampler = 2794 const std::string sampler =
2851 GetSampler(instr.sampler, texture_type, is_array, false); 2795 GetSampler(instr.sampler, texture_type, is_array, false);
@@ -2856,19 +2800,22 @@ private:
2856 break; 2800 break;
2857 } 2801 }
2858 case Tegra::Shader::TextureProcessMode::LL: { 2802 case Tegra::Shader::TextureProcessMode::LL: {
2859 const std::string op_c = 2803 shader.AddLine(
2860 regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset); 2804 "float lod = " +
2861 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; 2805 regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';');
2806 texture = "texelFetch(" + sampler + ", coords, lod)";
2862 break; 2807 break;
2863 } 2808 }
2864 default: { 2809 default: {
2865 texture = "texelFetch(" + sampler + ", coords, 0)"; 2810 texture = "texelFetch(" + sampler + ", coords, 0)";
2866 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2811 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2867 static_cast<u32>(instr.tlds.GetTextureProcessMode())); 2812 static_cast<u32>(instr.tlds.GetTextureProcessMode()));
2868 UNREACHABLE();
2869 } 2813 }
2870 } 2814 }
2871 WriteTexsInstruction(instr, coord, texture); 2815 WriteTexsInstruction(instr, texture);
2816
2817 --shader.scope;
2818 shader.AddLine('}');
2872 break; 2819 break;
2873 } 2820 }
2874 case OpCode::Id::TLD4: { 2821 case OpCode::Id::TLD4: {
@@ -2876,14 +2823,14 @@ private:
2876 ASSERT(instr.tld4.array == 0); 2823 ASSERT(instr.tld4.array == 0);
2877 std::string coord; 2824 std::string coord;
2878 2825
2879 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2826 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2880 "NODEP is not implemented"); 2827 "NODEP is not implemented");
2881 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2828 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2882 "AOFFI is not implemented"); 2829 "AOFFI is not implemented");
2883 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 2830 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
2884 "NDV is not implemented"); 2831 "NDV is not implemented");
2885 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), 2832 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP),
2886 "PTP is not implemented"); 2833 "PTP is not implemented");
2887 const bool depth_compare = 2834 const bool depth_compare =
2888 instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2835 instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2889 auto texture_type = instr.tld4.texture_type.Value(); 2836 auto texture_type = instr.tld4.texture_type.Value();
@@ -2891,37 +2838,37 @@ private:
2891 if (depth_compare) 2838 if (depth_compare)
2892 num_coordinates += 1; 2839 num_coordinates += 1;
2893 2840
2841 // Add an extra scope and declare the texture coords inside to prevent
2842 // overwriting them in case they are used as outputs of the texs instruction.
2843 shader.AddLine('{');
2844 ++shader.scope;
2845
2894 switch (num_coordinates) { 2846 switch (num_coordinates) {
2895 case 2: { 2847 case 2: {
2896 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2848 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2897 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2849 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2898 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2850 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2899 break; 2851 break;
2900 } 2852 }
2901 case 3: { 2853 case 3: {
2902 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2854 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2903 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2855 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2904 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); 2856 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2905 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2857 shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");");
2906 break; 2858 break;
2907 } 2859 }
2908 default: 2860 default:
2909 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", 2861 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2910 static_cast<u32>(num_coordinates)); 2862 static_cast<u32>(num_coordinates));
2911 UNREACHABLE();
2912 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2863 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2913 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2864 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2914 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2865 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2915 texture_type = Tegra::Shader::TextureType::Texture2D; 2866 texture_type = Tegra::Shader::TextureType::Texture2D;
2916 } 2867 }
2917 2868
2918 const std::string sampler = 2869 const std::string sampler =
2919 GetSampler(instr.sampler, texture_type, false, depth_compare); 2870 GetSampler(instr.sampler, texture_type, false, depth_compare);
2920 // Add an extra scope and declare the texture coords inside to prevent 2871
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, " + 2872 const std::string texture = "textureGather(" + sampler + ", coords, " +
2926 std::to_string(instr.tld4.component) + ')'; 2873 std::to_string(instr.tld4.component) + ')';
2927 if (!depth_compare) { 2874 if (!depth_compare) {
@@ -2938,14 +2885,20 @@ private:
2938 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); 2885 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
2939 } 2886 }
2940 --shader.scope; 2887 --shader.scope;
2941 shader.AddLine("}"); 2888 shader.AddLine('}');
2942 break; 2889 break;
2943 } 2890 }
2944 case OpCode::Id::TLD4S: { 2891 case OpCode::Id::TLD4S: {
2945 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2892 UNIMPLEMENTED_IF_MSG(
2946 "NODEP is not implemented"); 2893 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2947 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2894 "NODEP is not implemented");
2948 "AOFFI is not implemented"); 2895 UNIMPLEMENTED_IF_MSG(
2896 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2897 "AOFFI is not implemented");
2898
2899 // Scope to avoid variable name overlaps.
2900 shader.AddLine('{');
2901 ++shader.scope;
2949 2902
2950 const bool depth_compare = 2903 const bool depth_compare =
2951 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2904 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
@@ -2954,28 +2907,33 @@ private:
2954 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. 2907 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
2955 const std::string sampler = GetSampler( 2908 const std::string sampler = GetSampler(
2956 instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); 2909 instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare);
2957 std::string coord;
2958 if (!depth_compare) { 2910 if (!depth_compare) {
2959 coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; 2911 shader.AddLine("vec2 coords = vec2(" + op_a + ", " + op_b + ");");
2960 } else { 2912 } else {
2961 // Note: TLD4S coordinate encoding works just like TEXS's 2913 // Note: TLD4S coordinate encoding works just like TEXS's
2962 const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2914 shader.AddLine(
2963 coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");"; 2915 "float op_y = " + regs.GetRegisterAsFloat(instr.gpr8.Value() + 1) + ';');
2916 shader.AddLine("vec3 coords = vec3(" + op_a + ", op_y, " + op_b + ");");
2964 } 2917 }
2965 const std::string texture = "textureGather(" + sampler + ", coords, " + 2918 const std::string texture = "textureGather(" + sampler + ", coords, " +
2966 std::to_string(instr.tld4s.component) + ')'; 2919 std::to_string(instr.tld4s.component) + ')';
2967 2920
2968 if (!depth_compare) { 2921 if (!depth_compare) {
2969 WriteTexsInstruction(instr, coord, texture); 2922 WriteTexsInstruction(instr, texture);
2970 } else { 2923 } else {
2971 WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); 2924 WriteTexsInstruction(instr, "vec4(" + texture + ')');
2972 } 2925 }
2926
2927 --shader.scope;
2928 shader.AddLine('}');
2973 break; 2929 break;
2974 } 2930 }
2975 case OpCode::Id::TXQ: { 2931 case OpCode::Id::TXQ: {
2976 ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2932 UNIMPLEMENTED_IF_MSG(instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2977 "NODEP is not implemented"); 2933 "NODEP is not implemented");
2978 2934
2935 ++shader.scope;
2936 shader.AddLine('{');
2979 // TODO: the new commits on the texture refactor, change the way samplers work. 2937 // 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 2938 // Sadly, not all texture instructions specify the type of texture their sampler
2981 // uses. This must be fixed at a later instance. 2939 // uses. This must be fixed at a later instance.
@@ -2983,23 +2941,30 @@ private:
2983 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); 2941 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false);
2984 switch (instr.txq.query_type) { 2942 switch (instr.txq.query_type) {
2985 case Tegra::Shader::TextureQueryType::Dimension: { 2943 case Tegra::Shader::TextureQueryType::Dimension: {
2986 const std::string texture = "textureQueryLevels(" + sampler + ')'; 2944 const std::string texture = "textureSize(" + sampler + ", " +
2987 regs.SetRegisterToInteger(instr.gpr0, true, 0, texture, 1, 1); 2945 regs.GetRegisterAsInteger(instr.gpr8) + ')';
2946 const std::string mip_level = "textureQueryLevels(" + sampler + ')';
2947 shader.AddLine("ivec2 sizes = " + texture + ';');
2948 regs.SetRegisterToInteger(instr.gpr0, true, 0, "sizes.x", 1, 1);
2949 regs.SetRegisterToInteger(instr.gpr0.Value() + 1, true, 0, "sizes.y", 1, 1);
2950 regs.SetRegisterToInteger(instr.gpr0.Value() + 2, true, 0, "0", 1, 1);
2951 regs.SetRegisterToInteger(instr.gpr0.Value() + 3, true, 0, mip_level, 1, 1);
2988 break; 2952 break;
2989 } 2953 }
2990 default: { 2954 default: {
2991 LOG_CRITICAL(HW_GPU, "Unhandled texture query type: {}", 2955 UNIMPLEMENTED_MSG("Unhandled texture query type: {}",
2992 static_cast<u32>(instr.txq.query_type.Value())); 2956 static_cast<u32>(instr.txq.query_type.Value()));
2993 UNREACHABLE();
2994 } 2957 }
2995 } 2958 }
2959 --shader.scope;
2960 shader.AddLine('}');
2996 break; 2961 break;
2997 } 2962 }
2998 case OpCode::Id::TMML: { 2963 case OpCode::Id::TMML: {
2999 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2964 UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
3000 "NODEP is not implemented"); 2965 "NODEP is not implemented");
3001 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 2966 UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
3002 "NDV is not implemented"); 2967 "NDV is not implemented");
3003 2968
3004 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2969 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
3005 const bool is_array = instr.tmml.array != 0; 2970 const bool is_array = instr.tmml.array != 0;
@@ -3021,9 +2986,7 @@ private:
3021 break; 2986 break;
3022 } 2987 }
3023 default: 2988 default:
3024 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", 2989 UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type));
3025 static_cast<u32>(texture_type));
3026 UNREACHABLE();
3027 2990
3028 // Fallback to interpreting as a 2D texture for now 2991 // Fallback to interpreting as a 2D texture for now
3029 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2992 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
@@ -3046,8 +3009,7 @@ private:
3046 break; 3009 break;
3047 } 3010 }
3048 default: { 3011 default: {
3049 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->get().GetName()); 3012 UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
3050 UNREACHABLE();
3051 } 3013 }
3052 } 3014 }
3053 break; 3015 break;
@@ -3133,7 +3095,7 @@ private:
3133 break; 3095 break;
3134 } 3096 }
3135 case OpCode::Type::HalfSetPredicate: { 3097 case OpCode::Type::HalfSetPredicate: {
3136 ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented"); 3098 UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0);
3137 3099
3138 const std::string op_a = 3100 const std::string op_a =
3139 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a, 3101 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a,
@@ -3178,6 +3140,9 @@ private:
3178 break; 3140 break;
3179 } 3141 }
3180 case OpCode::Type::PredicateSetRegister: { 3142 case OpCode::Type::PredicateSetRegister: {
3143 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
3144 "Condition codes generation in PSET is not implemented");
3145
3181 const std::string op_a = 3146 const std::string op_a =
3182 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); 3147 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
3183 const std::string op_b = 3148 const std::string op_b =
@@ -3198,12 +3163,6 @@ private:
3198 const std::string value = '(' + result + ") ? 1.0 : 0.0"; 3163 const std::string value = '(' + result + ") ? 1.0 : 0.0";
3199 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); 3164 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
3200 } 3165 }
3201
3202 if (instr.generates_cc) {
3203 LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code");
3204 UNREACHABLE();
3205 }
3206
3207 break; 3166 break;
3208 } 3167 }
3209 case OpCode::Type::PredicateSetPredicate: { 3168 case OpCode::Type::PredicateSetPredicate: {
@@ -3241,21 +3200,19 @@ private:
3241 const std::string pred = 3200 const std::string pred =
3242 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 3201 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
3243 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 3202 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
3244 const std::string control_code = regs.GetControlCode(instr.csetp.cc); 3203 const std::string condition_code = regs.GetConditionCode(instr.csetp.cc);
3245 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 3204 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
3246 SetPredicate(instr.csetp.pred3, 3205 SetPredicate(instr.csetp.pred3,
3247 '(' + control_code + ") " + combiner + " (" + pred + ')'); 3206 '(' + condition_code + ") " + combiner + " (" + pred + ')');
3248 } 3207 }
3249 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 3208 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
3250 SetPredicate(instr.csetp.pred0, 3209 SetPredicate(instr.csetp.pred0,
3251 "!(" + control_code + ") " + combiner + " (" + pred + ')'); 3210 "!(" + condition_code + ") " + combiner + " (" + pred + ')');
3252 } 3211 }
3253 break; 3212 break;
3254 } 3213 }
3255 default: { 3214 default: {
3256 LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", 3215 UNIMPLEMENTED_MSG("Unhandled predicate instruction: {}", opcode->get().GetName());
3257 opcode->get().GetName());
3258 UNREACHABLE();
3259 } 3216 }
3260 } 3217 }
3261 break; 3218 break;
@@ -3335,7 +3292,7 @@ private:
3335 break; 3292 break;
3336 } 3293 }
3337 case OpCode::Type::HalfSet: { 3294 case OpCode::Type::HalfSet: {
3338 ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented"); 3295 UNIMPLEMENTED_IF(instr.hset2.ftz != 0);
3339 3296
3340 const std::string op_a = 3297 const std::string op_a =
3341 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a, 3298 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a,
@@ -3379,15 +3336,17 @@ private:
3379 break; 3336 break;
3380 } 3337 }
3381 case OpCode::Type::Xmad: { 3338 case OpCode::Type::Xmad: {
3382 ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); 3339 UNIMPLEMENTED_IF(instr.xmad.sign_a);
3383 ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); 3340 UNIMPLEMENTED_IF(instr.xmad.sign_b);
3341 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
3342 "Condition codes generation in XMAD is not implemented");
3384 3343
3385 std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; 3344 std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)};
3386 std::string op_b; 3345 std::string op_b;
3387 std::string op_c; 3346 std::string op_c;
3388 3347
3389 // TODO(bunnei): Needs to be fixed once op_a or op_b is signed 3348 // 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"); 3349 UNIMPLEMENTED_IF(instr.xmad.sign_a != instr.xmad.sign_b);
3391 const bool is_signed{instr.xmad.sign_a == 1}; 3350 const bool is_signed{instr.xmad.sign_a == 1};
3392 3351
3393 bool is_merge{}; 3352 bool is_merge{};
@@ -3420,8 +3379,7 @@ private:
3420 break; 3379 break;
3421 } 3380 }
3422 default: { 3381 default: {
3423 LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->get().GetName()); 3382 UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName());
3424 UNREACHABLE();
3425 } 3383 }
3426 } 3384 }
3427 3385
@@ -3457,9 +3415,8 @@ private:
3457 op_c = "((" + op_c + ") + (" + src2 + "<< 16))"; 3415 op_c = "((" + op_c + ") + (" + src2 + "<< 16))";
3458 break; 3416 break;
3459 default: { 3417 default: {
3460 LOG_CRITICAL(HW_GPU, "Unhandled XMAD mode: {}", 3418 UNIMPLEMENTED_MSG("Unhandled XMAD mode: {}",
3461 static_cast<u32>(instr.xmad.mode.Value())); 3419 static_cast<u32>(instr.xmad.mode.Value()));
3462 UNREACHABLE();
3463 } 3420 }
3464 } 3421 }
3465 3422
@@ -3469,25 +3426,19 @@ private:
3469 } 3426 }
3470 3427
3471 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); 3428 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; 3429 break;
3477 } 3430 }
3478 default: { 3431 default: {
3479 switch (opcode->get().GetId()) { 3432 switch (opcode->get().GetId()) {
3480 case OpCode::Id::EXIT: { 3433 case OpCode::Id::EXIT: {
3434 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3435 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3436 "EXIT condition code used: {}", static_cast<u32>(cc));
3437
3481 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { 3438 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
3482 EmitFragmentOutputsWrite(); 3439 EmitFragmentOutputsWrite();
3483 } 3440 }
3484 3441
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) { 3442 switch (instr.flow.cond) {
3492 case Tegra::Shader::FlowCondition::Always: 3443 case Tegra::Shader::FlowCondition::Always:
3493 shader.AddLine("return true;"); 3444 shader.AddLine("return true;");
@@ -3502,26 +3453,24 @@ private:
3502 case Tegra::Shader::FlowCondition::Fcsm_Tr: 3453 case Tegra::Shader::FlowCondition::Fcsm_Tr:
3503 // TODO(bunnei): What is this used for? If we assume this conditon is not 3454 // 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 3455 // satisifed, dual vertex shaders in Farming Simulator make more sense
3505 LOG_CRITICAL(HW_GPU, "Skipping unknown FlowCondition::Fcsm_Tr"); 3456 UNIMPLEMENTED_MSG("Skipping unknown FlowCondition::Fcsm_Tr");
3506 break; 3457 break;
3507 3458
3508 default: 3459 default:
3509 LOG_CRITICAL(HW_GPU, "Unhandled flow condition: {}", 3460 UNIMPLEMENTED_MSG("Unhandled flow condition: {}",
3510 static_cast<u32>(instr.flow.cond.Value())); 3461 static_cast<u32>(instr.flow.cond.Value()));
3511 UNREACHABLE();
3512 } 3462 }
3513 break; 3463 break;
3514 } 3464 }
3515 case OpCode::Id::KIL: { 3465 case OpCode::Id::KIL: {
3516 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3466 UNIMPLEMENTED_IF(instr.flow.cond != Tegra::Shader::FlowCondition::Always);
3467
3468 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3469 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3470 "KIL condition code used: {}", static_cast<u32>(cc));
3517 3471
3518 // Enclose "discard" in a conditional, so that GLSL compilation does not complain 3472 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
3519 // about unexecuted instructions that may follow this. 3473 // 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) {"); 3474 shader.AddLine("if (true) {");
3526 ++shader.scope; 3475 ++shader.scope;
3527 shader.AddLine("discard;"); 3476 shader.AddLine("discard;");
@@ -3531,7 +3480,8 @@ private:
3531 break; 3480 break;
3532 } 3481 }
3533 case OpCode::Id::OUT_R: { 3482 case OpCode::Id::OUT_R: {
3534 ASSERT(instr.gpr20.Value() == Register::ZeroIndex); 3483 UNIMPLEMENTED_IF_MSG(instr.gpr20.Value() != Register::ZeroIndex,
3484 "Stream buffer is not supported");
3535 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, 3485 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
3536 "OUT is expected to be used in a geometry shader."); 3486 "OUT is expected to be used in a geometry shader.");
3537 3487
@@ -3558,18 +3508,17 @@ private:
3558 break; 3508 break;
3559 } 3509 }
3560 default: { 3510 default: {
3561 LOG_CRITICAL(HW_GPU, "Unhandled system move: {}", 3511 UNIMPLEMENTED_MSG("Unhandled system move: {}",
3562 static_cast<u32>(instr.sys20.Value())); 3512 static_cast<u32>(instr.sys20.Value()));
3563 UNREACHABLE();
3564 } 3513 }
3565 } 3514 }
3566 break; 3515 break;
3567 } 3516 }
3568 case OpCode::Id::ISBERD: { 3517 case OpCode::Id::ISBERD: {
3569 ASSERT(instr.isberd.o == 0); 3518 UNIMPLEMENTED_IF(instr.isberd.o != 0);
3570 ASSERT(instr.isberd.skew == 0); 3519 UNIMPLEMENTED_IF(instr.isberd.skew != 0);
3571 ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None); 3520 UNIMPLEMENTED_IF(instr.isberd.shift != Tegra::Shader::IsberdShift::None);
3572 ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None); 3521 UNIMPLEMENTED_IF(instr.isberd.mode != Tegra::Shader::IsberdMode::None);
3573 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, 3522 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
3574 "ISBERD is expected to be used in a geometry shader."); 3523 "ISBERD is expected to be used in a geometry shader.");
3575 LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete"); 3524 LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete");
@@ -3577,13 +3526,13 @@ private:
3577 break; 3526 break;
3578 } 3527 }
3579 case OpCode::Id::BRA: { 3528 case OpCode::Id::BRA: {
3580 ASSERT_MSG(instr.bra.constant_buffer == 0, 3529 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
3581 "BRA with constant buffers are not implemented"); 3530 "BRA with constant buffers are not implemented");
3582 const Tegra::Shader::ControlCode cc = instr.flow_control_code; 3531
3583 if (cc != Tegra::Shader::ControlCode::T) { 3532 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3584 LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc)); 3533 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3585 UNREACHABLE(); 3534 "BRA condition code used: {}", static_cast<u32>(cc));
3586 } 3535
3587 const u32 target = offset + instr.bra.GetBranchTarget(); 3536 const u32 target = offset + instr.bra.GetBranchTarget();
3588 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); 3537 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
3589 break; 3538 break;
@@ -3606,7 +3555,8 @@ private:
3606 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it 3555 // 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 3556 // sets the target of the jump that the SYNC instruction will make. The SSY opcode
3608 // has a similar structure to the BRA opcode. 3557 // has a similar structure to the BRA opcode.
3609 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported"); 3558 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
3559 "Constant buffer flow is not supported");
3610 3560
3611 const u32 target = offset + instr.bra.GetBranchTarget(); 3561 const u32 target = offset + instr.bra.GetBranchTarget();
3612 EmitPushToFlowStack(target); 3562 EmitPushToFlowStack(target);
@@ -3616,29 +3566,28 @@ private:
3616 // PBK pushes to a stack the address where BRK will jump to. This shares stack with 3566 // 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 3567 // 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. 3568 // 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"); 3569 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
3570 "Constant buffer PBK is not supported");
3620 3571
3621 const u32 target = offset + instr.bra.GetBranchTarget(); 3572 const u32 target = offset + instr.bra.GetBranchTarget();
3622 EmitPushToFlowStack(target); 3573 EmitPushToFlowStack(target);
3623 break; 3574 break;
3624 } 3575 }
3625 case OpCode::Id::SYNC: { 3576 case OpCode::Id::SYNC: {
3577 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3578 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3579 "SYNC condition code used: {}", static_cast<u32>(cc));
3580
3626 // The SYNC opcode jumps to the address previously set by the SSY opcode 3581 // 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(); 3582 EmitPopFromFlowStack();
3633 break; 3583 break;
3634 } 3584 }
3635 case OpCode::Id::BRK: { 3585 case OpCode::Id::BRK: {
3636 // The BRK opcode jumps to the address previously set by the PBK opcode 3586 // The BRK opcode jumps to the address previously set by the PBK opcode
3637 const Tegra::Shader::ControlCode cc = instr.flow_control_code; 3587 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3638 if (cc != Tegra::Shader::ControlCode::T) { 3588 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3639 LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc)); 3589 "BRK condition code used: {}", static_cast<u32>(cc));
3640 UNREACHABLE(); 3590
3641 }
3642 EmitPopFromFlowStack(); 3591 EmitPopFromFlowStack();
3643 break; 3592 break;
3644 } 3593 }
@@ -3649,6 +3598,9 @@ private:
3649 break; 3598 break;
3650 } 3599 }
3651 case OpCode::Id::VMAD: { 3600 case OpCode::Id::VMAD: {
3601 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
3602 "Condition codes generation in VMAD is not implemented");
3603
3652 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1; 3604 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
3653 const std::string op_a = GetVideoOperandA(instr); 3605 const std::string op_a = GetVideoOperandA(instr);
3654 const std::string op_b = GetVideoOperandB(instr); 3606 const std::string op_b = GetVideoOperandB(instr);
@@ -3668,11 +3620,6 @@ private:
3668 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, 3620 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3669 instr.vmad.saturate == 1, 0, Register::Size::Word, 3621 instr.vmad.saturate == 1, 0, Register::Size::Word,
3670 instr.vmad.cc); 3622 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; 3623 break;
3677 } 3624 }
3678 case OpCode::Id::VSETP: { 3625 case OpCode::Id::VSETP: {
@@ -3699,10 +3646,7 @@ private:
3699 } 3646 }
3700 break; 3647 break;
3701 } 3648 }
3702 default: { 3649 default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); }
3703 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->get().GetName());
3704 UNREACHABLE();
3705 }
3706 } 3650 }
3707 3651
3708 break; 3652 break;
@@ -3827,6 +3771,7 @@ private:
3827 Maxwell3D::Regs::ShaderStage stage; 3771 Maxwell3D::Regs::ShaderStage stage;
3828 const std::string& suffix; 3772 const std::string& suffix;
3829 u64 local_memory_size; 3773 u64 local_memory_size;
3774 std::size_t shader_length;
3830 3775
3831 ShaderWriter shader; 3776 ShaderWriter shader;
3832 ShaderWriter declarations; 3777 ShaderWriter declarations;
@@ -3845,9 +3790,10 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u
3845 Maxwell3D::Regs::ShaderStage stage, 3790 Maxwell3D::Regs::ShaderStage stage,
3846 const std::string& suffix) { 3791 const std::string& suffix) {
3847 try { 3792 try {
3848 const auto subroutines = 3793 ControlFlowAnalyzer analyzer(program_code, main_offset, suffix);
3849 ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); 3794 const auto subroutines = analyzer.GetSubroutines();
3850 GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix); 3795 GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix,
3796 analyzer.GetShaderLength());
3851 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; 3797 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
3852 } catch (const DecompileFail& exception) { 3798 } catch (const DecompileFail& exception) {
3853 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); 3799 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what());
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_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 9f96b2745..934f4db78 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -233,6 +233,28 @@ void OpenGLState::ApplyStencilTest() const {
233 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); 233 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
234 } 234 }
235} 235}
236// Viewport does not affects glClearBuffer so emulate viewport using scissor test
237void OpenGLState::EmulateViewportWithScissor() {
238 auto& current = viewports[0];
239 if (current.scissor.enabled) {
240 const GLint left = std::max(current.x, current.scissor.x);
241 const GLint right =
242 std::max(current.x + current.width, current.scissor.x + current.scissor.width);
243 const GLint bottom = std::max(current.y, current.scissor.y);
244 const GLint top =
245 std::max(current.y + current.height, current.scissor.y + current.scissor.height);
246 current.scissor.x = std::max(left, 0);
247 current.scissor.y = std::max(bottom, 0);
248 current.scissor.width = std::max(right - left, 0);
249 current.scissor.height = std::max(top - bottom, 0);
250 } else {
251 current.scissor.enabled = true;
252 current.scissor.x = current.x;
253 current.scissor.y = current.y;
254 current.scissor.width = current.width;
255 current.scissor.height = current.height;
256 }
257}
236 258
237void OpenGLState::ApplyViewport() const { 259void OpenGLState::ApplyViewport() const {
238 if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) { 260 if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
@@ -242,7 +264,9 @@ void OpenGLState::ApplyViewport() const {
242 const auto& updated = viewports[i]; 264 const auto& updated = viewports[i];
243 if (updated.x != current.x || updated.y != current.y || 265 if (updated.x != current.x || updated.y != current.y ||
244 updated.width != current.width || updated.height != current.height) { 266 updated.width != current.width || updated.height != current.height) {
245 glViewportIndexedf(i, updated.x, updated.y, updated.width, updated.height); 267 glViewportIndexedf(
268 i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y),
269 static_cast<GLfloat>(updated.width), static_cast<GLfloat>(updated.height));
246 } 270 }
247 if (updated.depth_range_near != current.depth_range_near || 271 if (updated.depth_range_near != current.depth_range_near ||
248 updated.depth_range_far != current.depth_range_far) { 272 updated.depth_range_far != current.depth_range_far) {
@@ -270,8 +294,7 @@ void OpenGLState::ApplyViewport() const {
270 const auto& updated = viewports[0]; 294 const auto& updated = viewports[0];
271 if (updated.x != current.x || updated.y != current.y || updated.width != current.width || 295 if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
272 updated.height != current.height) { 296 updated.height != current.height) {
273 glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y), 297 glViewport(updated.x, updated.y, updated.width, updated.height);
274 static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height));
275 } 298 }
276 if (updated.depth_range_near != current.depth_range_near || 299 if (updated.depth_range_near != current.depth_range_near ||
277 updated.depth_range_far != current.depth_range_far) { 300 updated.depth_range_far != current.depth_range_far) {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index bdc743b0f..032fc43f0 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -156,10 +156,10 @@ public:
156 } draw; 156 } draw;
157 157
158 struct viewport { 158 struct viewport {
159 GLfloat x; 159 GLint x;
160 GLfloat y; 160 GLint y;
161 GLfloat width; 161 GLint width;
162 GLfloat height; 162 GLint height;
163 GLfloat depth_range_near; // GL_DEPTH_RANGE 163 GLfloat depth_range_near; // GL_DEPTH_RANGE
164 GLfloat depth_range_far; // GL_DEPTH_RANGE 164 GLfloat depth_range_far; // GL_DEPTH_RANGE
165 struct { 165 struct {
@@ -206,6 +206,7 @@ public:
206 OpenGLState& ResetBuffer(GLuint handle); 206 OpenGLState& ResetBuffer(GLuint handle);
207 OpenGLState& ResetVertexArray(GLuint handle); 207 OpenGLState& ResetVertexArray(GLuint handle);
208 OpenGLState& ResetFramebuffer(GLuint handle); 208 OpenGLState& ResetFramebuffer(GLuint handle);
209 void EmulateViewportWithScissor();
209 210
210private: 211private:
211 static OpenGLState cur_state; 212 static OpenGLState cur_state;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 065b3929c..a8833c06e 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -218,14 +218,19 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
218inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { 218inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
219 switch (equation) { 219 switch (equation) {
220 case Maxwell::Blend::Equation::Add: 220 case Maxwell::Blend::Equation::Add:
221 case Maxwell::Blend::Equation::AddGL:
221 return GL_FUNC_ADD; 222 return GL_FUNC_ADD;
222 case Maxwell::Blend::Equation::Subtract: 223 case Maxwell::Blend::Equation::Subtract:
224 case Maxwell::Blend::Equation::SubtractGL:
223 return GL_FUNC_SUBTRACT; 225 return GL_FUNC_SUBTRACT;
224 case Maxwell::Blend::Equation::ReverseSubtract: 226 case Maxwell::Blend::Equation::ReverseSubtract:
227 case Maxwell::Blend::Equation::ReverseSubtractGL:
225 return GL_FUNC_REVERSE_SUBTRACT; 228 return GL_FUNC_REVERSE_SUBTRACT;
226 case Maxwell::Blend::Equation::Min: 229 case Maxwell::Blend::Equation::Min:
230 case Maxwell::Blend::Equation::MinGL:
227 return GL_MIN; 231 return GL_MIN;
228 case Maxwell::Blend::Equation::Max: 232 case Maxwell::Blend::Equation::Max:
233 case Maxwell::Blend::Equation::MaxGL:
229 return GL_MAX; 234 return GL_MAX;
230 } 235 }
231 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 aad0b07ca..1492e063a 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -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
diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp
index efefb1f99..8a26fdff1 100644
--- a/src/yuzu/applets/software_keyboard.cpp
+++ b/src/yuzu/applets/software_keyboard.cpp
@@ -82,8 +82,8 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
82 : QString::fromStdU16String(parameters.submit_text), 82 : QString::fromStdU16String(parameters.submit_text),
83 QDialogButtonBox::AcceptRole); 83 QDialogButtonBox::AcceptRole);
84 84
85 connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::Submit); 85 connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept);
86 connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::Reject); 86 connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject);
87 layout->addWidget(header_label); 87 layout->addWidget(header_label);
88 layout->addWidget(sub_label); 88 layout->addWidget(sub_label);
89 layout->addWidget(guide_label); 89 layout->addWidget(guide_label);
@@ -96,16 +96,16 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
96 96
97QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; 97QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
98 98
99void QtSoftwareKeyboardDialog::Submit() { 99void QtSoftwareKeyboardDialog::accept() {
100 ok = true; 100 ok = true;
101 text = line_edit->text().toStdU16String(); 101 text = line_edit->text().toStdU16String();
102 accept(); 102 QDialog::accept();
103} 103}
104 104
105void QtSoftwareKeyboardDialog::Reject() { 105void QtSoftwareKeyboardDialog::reject() {
106 ok = false; 106 ok = false;
107 text.clear(); 107 text.clear();
108 accept(); 108 QDialog::reject();
109} 109}
110 110
111std::u16string QtSoftwareKeyboardDialog::GetText() const { 111std::u16string QtSoftwareKeyboardDialog::GetText() const {
@@ -129,13 +129,13 @@ QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
129 129
130void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out, 130void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out,
131 Core::Frontend::SoftwareKeyboardParameters parameters) const { 131 Core::Frontend::SoftwareKeyboardParameters parameters) const {
132 text_output = out; 132 text_output = std::move(out);
133 emit MainWindowGetText(parameters); 133 emit MainWindowGetText(parameters);
134} 134}
135 135
136void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, 136void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
137 std::function<void()> finished_check) const { 137 std::function<void()> finished_check) const {
138 this->finished_check = finished_check; 138 this->finished_check = std::move(finished_check);
139 emit MainWindowTextCheckDialog(error_message); 139 emit MainWindowTextCheckDialog(error_message);
140} 140}
141 141
diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h
index 73f56714f..c63720ba4 100644
--- a/src/yuzu/applets/software_keyboard.h
+++ b/src/yuzu/applets/software_keyboard.h
@@ -33,8 +33,8 @@ public:
33 Core::Frontend::SoftwareKeyboardParameters parameters); 33 Core::Frontend::SoftwareKeyboardParameters parameters);
34 ~QtSoftwareKeyboardDialog() override; 34 ~QtSoftwareKeyboardDialog() override;
35 35
36 void Submit(); 36 void accept() override;
37 void Reject(); 37 void reject() override;
38 38
39 std::u16string GetText() const; 39 std::u16string GetText() const;
40 bool GetStatus() const; 40 bool GetStatus() const;
@@ -70,11 +70,10 @@ signals:
70 void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; 70 void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const;
71 void MainWindowTextCheckDialog(std::u16string error_message) const; 71 void MainWindowTextCheckDialog(std::u16string error_message) const;
72 72
73public slots: 73private:
74 void MainWindowFinishedText(std::optional<std::u16string> text); 74 void MainWindowFinishedText(std::optional<std::u16string> text);
75 void MainWindowFinishedCheckDialog(); 75 void MainWindowFinishedCheckDialog();
76 76
77private:
78 mutable std::function<void(std::optional<std::u16string>)> text_output; 77 mutable std::function<void(std::optional<std::u16string>)> text_output;
79 mutable std::function<void()> finished_check; 78 mutable std::function<void()> finished_check;
80}; 79};
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index e24ed5f2b..83ebbd1fe 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -432,6 +432,7 @@ void Config::ReadValues() {
432 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 432 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
433 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 433 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
434 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();
435 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); 436 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool();
436 qt_config->endGroup(); 437 qt_config->endGroup();
437 438
@@ -638,6 +639,7 @@ void Config::SaveValues() {
638 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 639 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
639 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 640 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
640 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);
641 qt_config->setValue("dump_nso", Settings::values.dump_nso); 643 qt_config->setValue("dump_nso", Settings::values.dump_nso);
642 qt_config->endGroup(); 644 qt_config->endGroup();
643 645
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_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_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index c66353a65..097c1fbe3 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -366,6 +366,7 @@ void Config::ReadValues() {
366 Settings::values.gdbstub_port = 366 Settings::values.gdbstub_port =
367 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 367 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
368 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);
369 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); 370 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
370 371
371 // 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