summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/fiber.cpp155
-rw-r--r--src/common/fiber.h16
-rw-r--r--src/common/spin_lock.h8
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/arm_interface.cpp12
-rw-r--r--src/core/core.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp33
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp135
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h20
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp17
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h14
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.cpp39
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.h85
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp4
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp4
-rw-r--r--src/core/settings.cpp20
-rw-r--r--src/core/settings.h13
-rw-r--r--src/video_core/command_classes/codecs/codec.h6
-rw-r--r--src/video_core/command_classes/codecs/h264.cpp3
-rw-r--r--src/video_core/command_classes/codecs/h264.h10
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp2
-rw-r--r--src/video_core/command_classes/codecs/vp9.h2
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h5
-rw-r--r--src/video_core/command_classes/nvdec.h4
-rw-r--r--src/video_core/dma_pusher.cpp80
-rw-r--r--src/video_core/dma_pusher.h50
-rw-r--r--src/video_core/gpu.cpp48
-rw-r--r--src/video_core/gpu.h25
-rw-r--r--src/video_core/renderer_opengl/gl_arb_decompiler.cpp41
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp20
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/shader/decode/texture.cpp1
-rw-r--r--src/video_core/texture_cache/surface_params.cpp1
-rw-r--r--src/yuzu/configuration/configure_audio.cpp10
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.cpp8
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp18
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp8
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.cpp10
42 files changed, 668 insertions, 290 deletions
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index b209f52fc..3e3029cd1 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -4,6 +4,8 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/fiber.h" 6#include "common/fiber.h"
7#include "common/spin_lock.h"
8
7#if defined(_WIN32) || defined(WIN32) 9#if defined(_WIN32) || defined(WIN32)
8#include <windows.h> 10#include <windows.h>
9#else 11#else
@@ -14,18 +16,45 @@ namespace Common {
14 16
15constexpr std::size_t default_stack_size = 256 * 1024; // 256kb 17constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
16 18
17#if defined(_WIN32) || defined(WIN32)
18
19struct Fiber::FiberImpl { 19struct Fiber::FiberImpl {
20 SpinLock guard{};
21 std::function<void(void*)> entry_point;
22 std::function<void(void*)> rewind_point;
23 void* rewind_parameter{};
24 void* start_parameter{};
25 std::shared_ptr<Fiber> previous_fiber;
26 bool is_thread_fiber{};
27 bool released{};
28
29#if defined(_WIN32) || defined(WIN32)
20 LPVOID handle = nullptr; 30 LPVOID handle = nullptr;
21 LPVOID rewind_handle = nullptr; 31 LPVOID rewind_handle = nullptr;
32#else
33 alignas(64) std::array<u8, default_stack_size> stack;
34 alignas(64) std::array<u8, default_stack_size> rewind_stack;
35 u8* stack_limit;
36 u8* rewind_stack_limit;
37 boost::context::detail::fcontext_t context;
38 boost::context::detail::fcontext_t rewind_context;
39#endif
22}; 40};
23 41
42void Fiber::SetStartParameter(void* new_parameter) {
43 impl->start_parameter = new_parameter;
44}
45
46void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
47 impl->rewind_point = std::move(rewind_func);
48 impl->rewind_parameter = rewind_param;
49}
50
51#if defined(_WIN32) || defined(WIN32)
52
24void Fiber::Start() { 53void Fiber::Start() {
25 ASSERT(previous_fiber != nullptr); 54 ASSERT(impl->previous_fiber != nullptr);
26 previous_fiber->guard.unlock(); 55 impl->previous_fiber->impl->guard.unlock();
27 previous_fiber.reset(); 56 impl->previous_fiber.reset();
28 entry_point(start_parameter); 57 impl->entry_point(impl->start_parameter);
29 UNREACHABLE(); 58 UNREACHABLE();
30} 59}
31 60
@@ -34,58 +63,54 @@ void Fiber::OnRewind() {
34 DeleteFiber(impl->handle); 63 DeleteFiber(impl->handle);
35 impl->handle = impl->rewind_handle; 64 impl->handle = impl->rewind_handle;
36 impl->rewind_handle = nullptr; 65 impl->rewind_handle = nullptr;
37 rewind_point(rewind_parameter); 66 impl->rewind_point(impl->rewind_parameter);
38 UNREACHABLE(); 67 UNREACHABLE();
39} 68}
40 69
41void Fiber::FiberStartFunc(void* fiber_parameter) { 70void Fiber::FiberStartFunc(void* fiber_parameter) {
42 auto fiber = static_cast<Fiber*>(fiber_parameter); 71 auto* fiber = static_cast<Fiber*>(fiber_parameter);
43 fiber->Start(); 72 fiber->Start();
44} 73}
45 74
46void Fiber::RewindStartFunc(void* fiber_parameter) { 75void Fiber::RewindStartFunc(void* fiber_parameter) {
47 auto fiber = static_cast<Fiber*>(fiber_parameter); 76 auto* fiber = static_cast<Fiber*>(fiber_parameter);
48 fiber->OnRewind(); 77 fiber->OnRewind();
49} 78}
50 79
51Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) 80Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
52 : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { 81 : impl{std::make_unique<FiberImpl>()} {
53 impl = std::make_unique<FiberImpl>(); 82 impl->entry_point = std::move(entry_point_func);
83 impl->start_parameter = start_parameter;
54 impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); 84 impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
55} 85}
56 86
57Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} 87Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
58 88
59Fiber::~Fiber() { 89Fiber::~Fiber() {
60 if (released) { 90 if (impl->released) {
61 return; 91 return;
62 } 92 }
63 // Make sure the Fiber is not being used 93 // Make sure the Fiber is not being used
64 const bool locked = guard.try_lock(); 94 const bool locked = impl->guard.try_lock();
65 ASSERT_MSG(locked, "Destroying a fiber that's still running"); 95 ASSERT_MSG(locked, "Destroying a fiber that's still running");
66 if (locked) { 96 if (locked) {
67 guard.unlock(); 97 impl->guard.unlock();
68 } 98 }
69 DeleteFiber(impl->handle); 99 DeleteFiber(impl->handle);
70} 100}
71 101
72void Fiber::Exit() { 102void Fiber::Exit() {
73 ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); 103 ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
74 if (!is_thread_fiber) { 104 if (!impl->is_thread_fiber) {
75 return; 105 return;
76 } 106 }
77 ConvertFiberToThread(); 107 ConvertFiberToThread();
78 guard.unlock(); 108 impl->guard.unlock();
79 released = true; 109 impl->released = true;
80}
81
82void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
83 rewind_point = std::move(rewind_func);
84 rewind_parameter = rewind_param;
85} 110}
86 111
87void Fiber::Rewind() { 112void Fiber::Rewind() {
88 ASSERT(rewind_point); 113 ASSERT(impl->rewind_point);
89 ASSERT(impl->rewind_handle == nullptr); 114 ASSERT(impl->rewind_handle == nullptr);
90 impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this); 115 impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
91 SwitchToFiber(impl->rewind_handle); 116 SwitchToFiber(impl->rewind_handle);
@@ -94,39 +119,30 @@ void Fiber::Rewind() {
94void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) { 119void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
95 ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); 120 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
96 ASSERT_MSG(to != nullptr, "Next fiber is null!"); 121 ASSERT_MSG(to != nullptr, "Next fiber is null!");
97 to->guard.lock(); 122 to->impl->guard.lock();
98 to->previous_fiber = from; 123 to->impl->previous_fiber = from;
99 SwitchToFiber(to->impl->handle); 124 SwitchToFiber(to->impl->handle);
100 ASSERT(from->previous_fiber != nullptr); 125 ASSERT(from->impl->previous_fiber != nullptr);
101 from->previous_fiber->guard.unlock(); 126 from->impl->previous_fiber->impl->guard.unlock();
102 from->previous_fiber.reset(); 127 from->impl->previous_fiber.reset();
103} 128}
104 129
105std::shared_ptr<Fiber> Fiber::ThreadToFiber() { 130std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
106 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()}; 131 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
107 fiber->guard.lock(); 132 fiber->impl->guard.lock();
108 fiber->impl->handle = ConvertThreadToFiber(nullptr); 133 fiber->impl->handle = ConvertThreadToFiber(nullptr);
109 fiber->is_thread_fiber = true; 134 fiber->impl->is_thread_fiber = true;
110 return fiber; 135 return fiber;
111} 136}
112 137
113#else 138#else
114 139
115struct Fiber::FiberImpl {
116 alignas(64) std::array<u8, default_stack_size> stack;
117 alignas(64) std::array<u8, default_stack_size> rewind_stack;
118 u8* stack_limit;
119 u8* rewind_stack_limit;
120 boost::context::detail::fcontext_t context;
121 boost::context::detail::fcontext_t rewind_context;
122};
123
124void Fiber::Start(boost::context::detail::transfer_t& transfer) { 140void Fiber::Start(boost::context::detail::transfer_t& transfer) {
125 ASSERT(previous_fiber != nullptr); 141 ASSERT(impl->previous_fiber != nullptr);
126 previous_fiber->impl->context = transfer.fctx; 142 impl->previous_fiber->impl->context = transfer.fctx;
127 previous_fiber->guard.unlock(); 143 impl->previous_fiber->impl->guard.unlock();
128 previous_fiber.reset(); 144 impl->previous_fiber.reset();
129 entry_point(start_parameter); 145 impl->entry_point(impl->start_parameter);
130 UNREACHABLE(); 146 UNREACHABLE();
131} 147}
132 148
@@ -137,23 +153,24 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
137 u8* tmp = impl->stack_limit; 153 u8* tmp = impl->stack_limit;
138 impl->stack_limit = impl->rewind_stack_limit; 154 impl->stack_limit = impl->rewind_stack_limit;
139 impl->rewind_stack_limit = tmp; 155 impl->rewind_stack_limit = tmp;
140 rewind_point(rewind_parameter); 156 impl->rewind_point(impl->rewind_parameter);
141 UNREACHABLE(); 157 UNREACHABLE();
142} 158}
143 159
144void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { 160void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
145 auto fiber = static_cast<Fiber*>(transfer.data); 161 auto* fiber = static_cast<Fiber*>(transfer.data);
146 fiber->Start(transfer); 162 fiber->Start(transfer);
147} 163}
148 164
149void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { 165void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
150 auto fiber = static_cast<Fiber*>(transfer.data); 166 auto* fiber = static_cast<Fiber*>(transfer.data);
151 fiber->OnRewind(transfer); 167 fiber->OnRewind(transfer);
152} 168}
153 169
154Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) 170Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
155 : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { 171 : impl{std::make_unique<FiberImpl>()} {
156 impl = std::make_unique<FiberImpl>(); 172 impl->entry_point = std::move(entry_point_func);
173 impl->start_parameter = start_parameter;
157 impl->stack_limit = impl->stack.data(); 174 impl->stack_limit = impl->stack.data();
158 impl->rewind_stack_limit = impl->rewind_stack.data(); 175 impl->rewind_stack_limit = impl->rewind_stack.data();
159 u8* stack_base = impl->stack_limit + default_stack_size; 176 u8* stack_base = impl->stack_limit + default_stack_size;
@@ -161,37 +178,31 @@ Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_paramete
161 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); 178 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
162} 179}
163 180
164void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
165 rewind_point = std::move(rewind_func);
166 rewind_parameter = rewind_param;
167}
168
169Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} 181Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
170 182
171Fiber::~Fiber() { 183Fiber::~Fiber() {
172 if (released) { 184 if (impl->released) {
173 return; 185 return;
174 } 186 }
175 // Make sure the Fiber is not being used 187 // Make sure the Fiber is not being used
176 const bool locked = guard.try_lock(); 188 const bool locked = impl->guard.try_lock();
177 ASSERT_MSG(locked, "Destroying a fiber that's still running"); 189 ASSERT_MSG(locked, "Destroying a fiber that's still running");
178 if (locked) { 190 if (locked) {
179 guard.unlock(); 191 impl->guard.unlock();
180 } 192 }
181} 193}
182 194
183void Fiber::Exit() { 195void Fiber::Exit() {
184 196 ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
185 ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); 197 if (!impl->is_thread_fiber) {
186 if (!is_thread_fiber) {
187 return; 198 return;
188 } 199 }
189 guard.unlock(); 200 impl->guard.unlock();
190 released = true; 201 impl->released = true;
191} 202}
192 203
193void Fiber::Rewind() { 204void Fiber::Rewind() {
194 ASSERT(rewind_point); 205 ASSERT(impl->rewind_point);
195 ASSERT(impl->rewind_context == nullptr); 206 ASSERT(impl->rewind_context == nullptr);
196 u8* stack_base = impl->rewind_stack_limit + default_stack_size; 207 u8* stack_base = impl->rewind_stack_limit + default_stack_size;
197 impl->rewind_context = 208 impl->rewind_context =
@@ -202,19 +213,19 @@ void Fiber::Rewind() {
202void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) { 213void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
203 ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); 214 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
204 ASSERT_MSG(to != nullptr, "Next fiber is null!"); 215 ASSERT_MSG(to != nullptr, "Next fiber is null!");
205 to->guard.lock(); 216 to->impl->guard.lock();
206 to->previous_fiber = from; 217 to->impl->previous_fiber = from;
207 auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); 218 auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
208 ASSERT(from->previous_fiber != nullptr); 219 ASSERT(from->impl->previous_fiber != nullptr);
209 from->previous_fiber->impl->context = transfer.fctx; 220 from->impl->previous_fiber->impl->context = transfer.fctx;
210 from->previous_fiber->guard.unlock(); 221 from->impl->previous_fiber->impl->guard.unlock();
211 from->previous_fiber.reset(); 222 from->impl->previous_fiber.reset();
212} 223}
213 224
214std::shared_ptr<Fiber> Fiber::ThreadToFiber() { 225std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
215 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()}; 226 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
216 fiber->guard.lock(); 227 fiber->impl->guard.lock();
217 fiber->is_thread_fiber = true; 228 fiber->impl->is_thread_fiber = true;
218 return fiber; 229 return fiber;
219} 230}
220 231
diff --git a/src/common/fiber.h b/src/common/fiber.h
index 699286ee2..5323e8579 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -7,9 +7,6 @@
7#include <functional> 7#include <functional>
8#include <memory> 8#include <memory>
9 9
10#include "common/common_types.h"
11#include "common/spin_lock.h"
12
13#if !defined(_WIN32) && !defined(WIN32) 10#if !defined(_WIN32) && !defined(WIN32)
14namespace boost::context::detail { 11namespace boost::context::detail {
15struct transfer_t; 12struct transfer_t;
@@ -57,9 +54,7 @@ public:
57 void Exit(); 54 void Exit();
58 55
59 /// Changes the start parameter of the fiber. Has no effect if the fiber already started 56 /// Changes the start parameter of the fiber. Has no effect if the fiber already started
60 void SetStartParameter(void* new_parameter) { 57 void SetStartParameter(void* new_parameter);
61 start_parameter = new_parameter;
62 }
63 58
64private: 59private:
65 Fiber(); 60 Fiber();
@@ -77,16 +72,7 @@ private:
77#endif 72#endif
78 73
79 struct FiberImpl; 74 struct FiberImpl;
80
81 SpinLock guard{};
82 std::function<void(void*)> entry_point;
83 std::function<void(void*)> rewind_point;
84 void* rewind_parameter{};
85 void* start_parameter{};
86 std::shared_ptr<Fiber> previous_fiber;
87 std::unique_ptr<FiberImpl> impl; 75 std::unique_ptr<FiberImpl> impl;
88 bool is_thread_fiber{};
89 bool released{};
90}; 76};
91 77
92} // namespace Common 78} // namespace Common
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h
index 4f946a258..06ac2f5bb 100644
--- a/src/common/spin_lock.h
+++ b/src/common/spin_lock.h
@@ -15,6 +15,14 @@ namespace Common {
15 */ 15 */
16class SpinLock { 16class SpinLock {
17public: 17public:
18 SpinLock() = default;
19
20 SpinLock(const SpinLock&) = delete;
21 SpinLock& operator=(const SpinLock&) = delete;
22
23 SpinLock(SpinLock&&) = delete;
24 SpinLock& operator=(SpinLock&&) = delete;
25
18 void lock(); 26 void lock();
19 void unlock(); 27 void unlock();
20 [[nodiscard]] bool try_lock(); 28 [[nodiscard]] bool try_lock();
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 19c926662..a1d8dcfa5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -452,6 +452,8 @@ add_library(core STATIC
452 hle/service/nvdrv/nvdrv.h 452 hle/service/nvdrv/nvdrv.h
453 hle/service/nvdrv/nvmemp.cpp 453 hle/service/nvdrv/nvmemp.cpp
454 hle/service/nvdrv/nvmemp.h 454 hle/service/nvdrv/nvmemp.h
455 hle/service/nvdrv/syncpoint_manager.cpp
456 hle/service/nvdrv/syncpoint_manager.h
455 hle/service/nvflinger/buffer_queue.cpp 457 hle/service/nvflinger/buffer_queue.cpp
456 hle/service/nvflinger/buffer_queue.h 458 hle/service/nvflinger/buffer_queue.h
457 hle/service/nvflinger/nvflinger.cpp 459 hle/service/nvflinger/nvflinger.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index d2295ed90..0951e1976 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -147,10 +147,18 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
147 auto fp = ctx.cpu_registers[29]; 147 auto fp = ctx.cpu_registers[29];
148 auto lr = ctx.cpu_registers[30]; 148 auto lr = ctx.cpu_registers[30];
149 while (true) { 149 while (true) {
150 out.push_back({"", 0, lr, 0}); 150 out.push_back({
151 if (!fp) { 151 .module = "",
152 .address = 0,
153 .original_address = lr,
154 .offset = 0,
155 .name = {},
156 });
157
158 if (fp == 0) {
152 break; 159 break;
153 } 160 }
161
154 lr = memory.Read64(fp + 8) - 4; 162 lr = memory.Read64(fp + 8) - 4;
155 fp = memory.Read64(fp); 163 fp = memory.Read64(fp);
156 } 164 }
diff --git a/src/core/core.cpp b/src/core/core.cpp
index fde2ccc09..242796008 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -179,16 +179,18 @@ struct System::Impl {
179 arp_manager.ResetAll(); 179 arp_manager.ResetAll();
180 180
181 telemetry_session = std::make_unique<Core::TelemetrySession>(); 181 telemetry_session = std::make_unique<Core::TelemetrySession>();
182
183 gpu_core = VideoCore::CreateGPU(emu_window, system);
184 if (!gpu_core) {
185 return ResultStatus::ErrorVideoCore;
186 }
187
182 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 188 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
183 189
184 Service::Init(service_manager, system); 190 Service::Init(service_manager, system);
185 GDBStub::DeferStart(); 191 GDBStub::DeferStart();
186 192
187 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); 193 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
188 gpu_core = VideoCore::CreateGPU(emu_window, system);
189 if (!gpu_core) {
190 return ResultStatus::ErrorVideoCore;
191 }
192 194
193 // Initialize time manager, which must happen after kernel is created 195 // Initialize time manager, which must happen after kernel is created
194 time_manager.Initialize(); 196 time_manager.Initialize();
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 75d9191ff..8356a8139 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -15,8 +15,9 @@
15 15
16namespace Service::Nvidia::Devices { 16namespace Service::Nvidia::Devices {
17 17
18nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface) 18nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
19 : nvdevice(system), events_interface{events_interface} {} 19 SyncpointManager& syncpoint_manager)
20 : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
20nvhost_ctrl::~nvhost_ctrl() = default; 21nvhost_ctrl::~nvhost_ctrl() = default;
21 22
22u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 23u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -70,19 +71,33 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
70 return NvResult::BadParameter; 71 return NvResult::BadParameter;
71 } 72 }
72 73
74 if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
75 params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id);
76 std::memcpy(output.data(), &params, sizeof(params));
77 return NvResult::Success;
78 }
79
80 if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id);
81 syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
82 params.value = new_value;
83 std::memcpy(output.data(), &params, sizeof(params));
84 return NvResult::Success;
85 }
86
73 auto event = events_interface.events[event_id]; 87 auto event = events_interface.events[event_id];
74 auto& gpu = system.GPU(); 88 auto& gpu = system.GPU();
89
75 // This is mostly to take into account unimplemented features. As synced 90 // This is mostly to take into account unimplemented features. As synced
76 // gpu is always synced. 91 // gpu is always synced.
77 if (!gpu.IsAsync()) { 92 if (!gpu.IsAsync()) {
78 event.writable->Signal(); 93 event.event.writable->Signal();
79 return NvResult::Success; 94 return NvResult::Success;
80 } 95 }
81 auto lock = gpu.LockSync(); 96 auto lock = gpu.LockSync();
82 const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id); 97 const u32 current_syncpoint_value = event.fence.value;
83 const s32 diff = current_syncpoint_value - params.threshold; 98 const s32 diff = current_syncpoint_value - params.threshold;
84 if (diff >= 0) { 99 if (diff >= 0) {
85 event.writable->Signal(); 100 event.event.writable->Signal();
86 params.value = current_syncpoint_value; 101 params.value = current_syncpoint_value;
87 std::memcpy(output.data(), &params, sizeof(params)); 102 std::memcpy(output.data(), &params, sizeof(params));
88 return NvResult::Success; 103 return NvResult::Success;
@@ -109,7 +124,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
109 params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; 124 params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
110 } 125 }
111 params.value |= event_id; 126 params.value |= event_id;
112 event.writable->Clear(); 127 event.event.writable->Clear();
113 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); 128 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
114 if (!is_async && ctrl.fresh_call) { 129 if (!is_async && ctrl.fresh_call) {
115 ctrl.must_delay = true; 130 ctrl.must_delay = true;
@@ -157,15 +172,19 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
157u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { 172u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
158 IocCtrlEventSignalParams params{}; 173 IocCtrlEventSignalParams params{};
159 std::memcpy(&params, input.data(), sizeof(params)); 174 std::memcpy(&params, input.data(), sizeof(params));
175
160 u32 event_id = params.event_id & 0x00FF; 176 u32 event_id = params.event_id & 0x00FF;
161 LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); 177 LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id);
178
162 if (event_id >= MaxNvEvents) { 179 if (event_id >= MaxNvEvents) {
163 return NvResult::BadParameter; 180 return NvResult::BadParameter;
164 } 181 }
165 if (events_interface.status[event_id] == EventState::Waiting) { 182 if (events_interface.status[event_id] == EventState::Waiting) {
166 events_interface.LiberateEvent(event_id); 183 events_interface.LiberateEvent(event_id);
167 events_interface.events[event_id].writable->Signal();
168 } 184 }
185
186 syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
187
169 return NvResult::Success; 188 return NvResult::Success;
170} 189}
171 190
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index f7b04d9f1..24ad96cb9 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -14,7 +14,8 @@ namespace Service::Nvidia::Devices {
14 14
15class nvhost_ctrl final : public nvdevice { 15class nvhost_ctrl final : public nvdevice {
16public: 16public:
17 explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); 17 explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface,
18 SyncpointManager& syncpoint_manager);
18 ~nvhost_ctrl() override; 19 ~nvhost_ctrl() override;
19 20
20 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 21 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -145,6 +146,7 @@ private:
145 u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); 146 u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
146 147
147 EventInterface& events_interface; 148 EventInterface& events_interface;
149 SyncpointManager& syncpoint_manager;
148}; 150};
149 151
150} // namespace Service::Nvidia::Devices 152} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index f1966ac0e..152019548 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -7,14 +7,20 @@
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
10#include "core/hle/service/nvdrv/syncpoint_manager.h"
10#include "core/memory.h" 11#include "core/memory.h"
11#include "video_core/gpu.h" 12#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
16nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 17nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 18 SyncpointManager& syncpoint_manager)
19 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager{syncpoint_manager} {
20 channel_fence.id = syncpoint_manager.AllocateSyncpoint();
21 channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
22}
23
18nvhost_gpu::~nvhost_gpu() = default; 24nvhost_gpu::~nvhost_gpu() = default;
19 25
20u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 26u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -126,10 +132,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
126 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, 132 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
127 params.unk3); 133 params.unk3);
128 134
129 auto& gpu = system.GPU(); 135 channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
130 params.fence_out.id = assigned_syncpoints; 136
131 params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints); 137 params.fence_out = channel_fence;
132 assigned_syncpoints++; 138
133 std::memcpy(output.data(), &params, output.size()); 139 std::memcpy(output.data(), &params, output.size());
134 return 0; 140 return 0;
135} 141}
@@ -145,39 +151,100 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
145 return 0; 151 return 0;
146} 152}
147 153
148u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { 154static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
149 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 155 return {
150 UNIMPLEMENTED(); 156 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
157 Tegra::SubmissionMode::Increasing),
158 {fence.value},
159 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
160 Tegra::SubmissionMode::Increasing),
161 Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Acquire, fence.id),
162 };
163}
164
165static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) {
166 std::vector<Tegra::CommandHeader> result{
167 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
168 Tegra::SubmissionMode::Increasing),
169 {}};
170
171 for (u32 count = 0; count < add_increment; ++count) {
172 result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
173 Tegra::SubmissionMode::Increasing));
174 result.emplace_back(
175 Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Increment, fence.id));
151 } 176 }
152 IoctlSubmitGpfifo params{}; 177
153 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 178 return result;
179}
180
181static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence,
182 u32 add_increment) {
183 std::vector<Tegra::CommandHeader> result{
184 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
185 Tegra::SubmissionMode::Increasing),
186 {}};
187 const std::vector<Tegra::CommandHeader> increment{
188 BuildIncrementCommandList(fence, add_increment)};
189
190 result.insert(result.end(), increment.begin(), increment.end());
191
192 return result;
193}
194
195u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
196 Tegra::CommandList&& entries) {
154 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, 197 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
155 params.num_entries, params.flags.raw); 198 params.num_entries, params.flags.raw);
156 199
157 ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + 200 auto& gpu = system.GPU();
158 params.num_entries * sizeof(Tegra::CommandListHeader),
159 "Incorrect input size");
160 201
161 Tegra::CommandList entries(params.num_entries); 202 params.fence_out.id = channel_fence.id;
162 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
163 params.num_entries * sizeof(Tegra::CommandListHeader));
164 203
165 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); 204 if (params.flags.add_wait.Value() &&
166 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); 205 !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) {
206 gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)});
207 }
167 208
168 auto& gpu = system.GPU(); 209 if (params.flags.add_increment.Value() || params.flags.increment.Value()) {
169 u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id); 210 const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0;
170 if (params.flags.increment.Value()) { 211 params.fence_out.value = syncpoint_manager.IncreaseSyncpoint(
171 params.fence_out.value += current_syncpoint_value; 212 params.fence_out.id, params.AddIncrementValue() + increment_value);
172 } else { 213 } else {
173 params.fence_out.value = current_syncpoint_value; 214 params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id);
174 } 215 }
216
217 entries.RefreshIntegrityChecks(gpu);
175 gpu.PushGPUEntries(std::move(entries)); 218 gpu.PushGPUEntries(std::move(entries));
176 219
220 if (params.flags.add_increment.Value()) {
221 if (params.flags.suppress_wfi) {
222 gpu.PushGPUEntries(Tegra::CommandList{
223 BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())});
224 } else {
225 gpu.PushGPUEntries(Tegra::CommandList{
226 BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())});
227 }
228 }
229
177 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo)); 230 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
178 return 0; 231 return 0;
179} 232}
180 233
234u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
235 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
236 UNIMPLEMENTED();
237 }
238 IoctlSubmitGpfifo params{};
239 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
240
241 Tegra::CommandList entries(params.num_entries);
242 std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
243 params.num_entries * sizeof(Tegra::CommandListHeader));
244
245 return SubmitGPFIFOImpl(params, output, std::move(entries));
246}
247
181u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, 248u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
182 const std::vector<u8>& input2, IoctlVersion version) { 249 const std::vector<u8>& input2, IoctlVersion version) {
183 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 250 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
@@ -185,31 +252,17 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
185 } 252 }
186 IoctlSubmitGpfifo params{}; 253 IoctlSubmitGpfifo params{};
187 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 254 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
188 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
189 params.num_entries, params.flags.raw);
190 255
191 Tegra::CommandList entries(params.num_entries); 256 Tegra::CommandList entries(params.num_entries);
192 if (version == IoctlVersion::Version2) { 257 if (version == IoctlVersion::Version2) {
193 std::memcpy(entries.data(), input2.data(), 258 std::memcpy(entries.command_lists.data(), input2.data(),
194 params.num_entries * sizeof(Tegra::CommandListHeader)); 259 params.num_entries * sizeof(Tegra::CommandListHeader));
195 } else { 260 } else {
196 system.Memory().ReadBlock(params.address, entries.data(), 261 system.Memory().ReadBlock(params.address, entries.command_lists.data(),
197 params.num_entries * sizeof(Tegra::CommandListHeader)); 262 params.num_entries * sizeof(Tegra::CommandListHeader));
198 } 263 }
199 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
200 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
201
202 auto& gpu = system.GPU();
203 u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
204 if (params.flags.increment.Value()) {
205 params.fence_out.value += current_syncpoint_value;
206 } else {
207 params.fence_out.value = current_syncpoint_value;
208 }
209 gpu.PushGPUEntries(std::move(entries));
210 264
211 std::memcpy(output.data(), &params, output.size()); 265 return SubmitGPFIFOImpl(params, output, std::move(entries));
212 return 0;
213} 266}
214 267
215u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 268u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 2ac74743f..a252fc06d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -11,6 +11,11 @@
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 12#include "core/hle/service/nvdrv/devices/nvdevice.h"
13#include "core/hle/service/nvdrv/nvdata.h" 13#include "core/hle/service/nvdrv/nvdata.h"
14#include "video_core/dma_pusher.h"
15
16namespace Service::Nvidia {
17class SyncpointManager;
18}
14 19
15namespace Service::Nvidia::Devices { 20namespace Service::Nvidia::Devices {
16 21
@@ -21,7 +26,8 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
21 26
22class nvhost_gpu final : public nvdevice { 27class nvhost_gpu final : public nvdevice {
23public: 28public:
24 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 29 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
30 SyncpointManager& syncpoint_manager);
25 ~nvhost_gpu() override; 31 ~nvhost_gpu() override;
26 32
27 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 33 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
@@ -162,10 +168,15 @@ private:
162 u32_le raw; 168 u32_le raw;
163 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list 169 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
164 BitField<1, 1, u32_le> add_increment; // append an increment to the list 170 BitField<1, 1, u32_le> add_increment; // append an increment to the list
165 BitField<2, 1, u32_le> new_hw_format; // Mostly ignored 171 BitField<2, 1, u32_le> new_hw_format; // mostly ignored
172 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
166 BitField<8, 1, u32_le> increment; // increment the returned fence 173 BitField<8, 1, u32_le> increment; // increment the returned fence
167 } flags; 174 } flags;
168 Fence fence_out; // returned new fence object for others to wait on 175 Fence fence_out; // returned new fence object for others to wait on
176
177 u32 AddIncrementValue() const {
178 return flags.add_increment.Value() << 1;
179 }
169 }; 180 };
170 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), 181 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
171 "IoctlSubmitGpfifo is incorrect size"); 182 "IoctlSubmitGpfifo is incorrect size");
@@ -190,6 +201,8 @@ private:
190 u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); 201 u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
191 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); 202 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
192 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); 203 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
204 u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
205 Tegra::CommandList&& entries);
193 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); 206 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
194 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, 207 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
195 const std::vector<u8>& input2, IoctlVersion version); 208 const std::vector<u8>& input2, IoctlVersion version);
@@ -198,7 +211,8 @@ private:
198 u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); 211 u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
199 212
200 std::shared_ptr<nvmap> nvmap_dev; 213 std::shared_ptr<nvmap> nvmap_dev;
201 u32 assigned_syncpoints{}; 214 SyncpointManager& syncpoint_manager;
215 Fence channel_fence;
202}; 216};
203 217
204} // namespace Service::Nvidia::Devices 218} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 803c1a984..a46755cdc 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -21,6 +21,7 @@
21#include "core/hle/service/nvdrv/interface.h" 21#include "core/hle/service/nvdrv/interface.h"
22#include "core/hle/service/nvdrv/nvdrv.h" 22#include "core/hle/service/nvdrv/nvdrv.h"
23#include "core/hle/service/nvdrv/nvmemp.h" 23#include "core/hle/service/nvdrv/nvmemp.h"
24#include "core/hle/service/nvdrv/syncpoint_manager.h"
24#include "core/hle/service/nvflinger/nvflinger.h" 25#include "core/hle/service/nvflinger/nvflinger.h"
25 26
26namespace Service::Nvidia { 27namespace Service::Nvidia {
@@ -36,21 +37,23 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
36 nvflinger.SetNVDrvInstance(module_); 37 nvflinger.SetNVDrvInstance(module_);
37} 38}
38 39
39Module::Module(Core::System& system) { 40Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
40 auto& kernel = system.Kernel(); 41 auto& kernel = system.Kernel();
41 for (u32 i = 0; i < MaxNvEvents; i++) { 42 for (u32 i = 0; i < MaxNvEvents; i++) {
42 std::string event_label = fmt::format("NVDRV::NvEvent_{}", i); 43 std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
43 events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(kernel, event_label); 44 events_interface.events[i] = {Kernel::WritableEvent::CreateEventPair(kernel, event_label)};
44 events_interface.status[i] = EventState::Free; 45 events_interface.status[i] = EventState::Free;
45 events_interface.registered[i] = false; 46 events_interface.registered[i] = false;
46 } 47 }
47 auto nvmap_dev = std::make_shared<Devices::nvmap>(system); 48 auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
48 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); 49 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
49 devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev); 50 devices["/dev/nvhost-gpu"] =
51 std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager);
50 devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); 52 devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
51 devices["/dev/nvmap"] = nvmap_dev; 53 devices["/dev/nvmap"] = nvmap_dev;
52 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); 54 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
53 devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface); 55 devices["/dev/nvhost-ctrl"] =
56 std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager);
54 devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev); 57 devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev);
55 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); 58 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
56 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev); 59 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev);
@@ -95,17 +98,17 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
95 if (events_interface.assigned_syncpt[i] == syncpoint_id && 98 if (events_interface.assigned_syncpt[i] == syncpoint_id &&
96 events_interface.assigned_value[i] == value) { 99 events_interface.assigned_value[i] == value) {
97 events_interface.LiberateEvent(i); 100 events_interface.LiberateEvent(i);
98 events_interface.events[i].writable->Signal(); 101 events_interface.events[i].event.writable->Signal();
99 } 102 }
100 } 103 }
101} 104}
102 105
103std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const { 106std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
104 return events_interface.events[event_id].readable; 107 return events_interface.events[event_id].event.readable;
105} 108}
106 109
107std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const { 110std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
108 return events_interface.events[event_id].writable; 111 return events_interface.events[event_id].event.writable;
109} 112}
110 113
111} // namespace Service::Nvidia 114} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 7706a5590..f3d863dac 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -10,6 +10,7 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/kernel/writable_event.h" 11#include "core/hle/kernel/writable_event.h"
12#include "core/hle/service/nvdrv/nvdata.h" 12#include "core/hle/service/nvdrv/nvdata.h"
13#include "core/hle/service/nvdrv/syncpoint_manager.h"
13#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
14 15
15namespace Core { 16namespace Core {
@@ -22,15 +23,23 @@ class NVFlinger;
22 23
23namespace Service::Nvidia { 24namespace Service::Nvidia {
24 25
26class SyncpointManager;
27
25namespace Devices { 28namespace Devices {
26class nvdevice; 29class nvdevice;
27} 30}
28 31
32/// Represents an Nvidia event
33struct NvEvent {
34 Kernel::EventPair event;
35 Fence fence{};
36};
37
29struct EventInterface { 38struct EventInterface {
30 // Mask representing currently busy events 39 // Mask representing currently busy events
31 u64 events_mask{}; 40 u64 events_mask{};
32 // Each kernel event associated to an NV event 41 // Each kernel event associated to an NV event
33 std::array<Kernel::EventPair, MaxNvEvents> events; 42 std::array<NvEvent, MaxNvEvents> events;
34 // The status of the current NVEvent 43 // The status of the current NVEvent
35 std::array<EventState, MaxNvEvents> status{}; 44 std::array<EventState, MaxNvEvents> status{};
36 // Tells if an NVEvent is registered or not 45 // Tells if an NVEvent is registered or not
@@ -119,6 +128,9 @@ public:
119 std::shared_ptr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const; 128 std::shared_ptr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
120 129
121private: 130private:
131 /// Manages syncpoints on the host
132 SyncpointManager syncpoint_manager;
133
122 /// Id to use for the next open file descriptor. 134 /// Id to use for the next open file descriptor.
123 u32 next_fd = 1; 135 u32 next_fd = 1;
124 136
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
new file mode 100644
index 000000000..0151a03b7
--- /dev/null
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
@@ -0,0 +1,39 @@
1// Copyright 2020 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/hle/service/nvdrv/syncpoint_manager.h"
7#include "video_core/gpu.h"
8
9namespace Service::Nvidia {
10
11SyncpointManager::SyncpointManager(Tegra::GPU& gpu) : gpu{gpu} {}
12
13SyncpointManager::~SyncpointManager() = default;
14
15u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) {
16 syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id);
17 return GetSyncpointMin(syncpoint_id);
18}
19
20u32 SyncpointManager::AllocateSyncpoint() {
21 for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) {
22 if (!syncpoints[syncpoint_id].is_allocated) {
23 syncpoints[syncpoint_id].is_allocated = true;
24 return syncpoint_id;
25 }
26 }
27 UNREACHABLE_MSG("No more available syncpoints!");
28 return {};
29}
30
31u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) {
32 for (u32 index = 0; index < value; ++index) {
33 syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed);
34 }
35
36 return GetSyncpointMax(syncpoint_id);
37}
38
39} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h
new file mode 100644
index 000000000..4168b6c7e
--- /dev/null
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.h
@@ -0,0 +1,85 @@
1// Copyright 2020 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 <atomic>
9
10#include "common/common_types.h"
11#include "core/hle/service/nvdrv/nvdata.h"
12
13namespace Tegra {
14class GPU;
15}
16
17namespace Service::Nvidia {
18
19class SyncpointManager final {
20public:
21 explicit SyncpointManager(Tegra::GPU& gpu);
22 ~SyncpointManager();
23
24 /**
25 * Returns true if the specified syncpoint is expired for the given value.
26 * @param syncpoint_id Syncpoint ID to check.
27 * @param value Value to check against the specified syncpoint.
28 * @returns True if the specified syncpoint is expired for the given value, otherwise False.
29 */
30 bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const {
31 return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value);
32 }
33
34 /**
35 * Gets the lower bound for the specified syncpoint.
36 * @param syncpoint_id Syncpoint ID to get the lower bound for.
37 * @returns The lower bound for the specified syncpoint.
38 */
39 u32 GetSyncpointMin(u32 syncpoint_id) const {
40 return syncpoints[syncpoint_id].min.load(std::memory_order_relaxed);
41 }
42
43 /**
44 * Gets the uper bound for the specified syncpoint.
45 * @param syncpoint_id Syncpoint ID to get the upper bound for.
46 * @returns The upper bound for the specified syncpoint.
47 */
48 u32 GetSyncpointMax(u32 syncpoint_id) const {
49 return syncpoints[syncpoint_id].max.load(std::memory_order_relaxed);
50 }
51
52 /**
53 * Refreshes the minimum value for the specified syncpoint.
54 * @param syncpoint_id Syncpoint ID to be refreshed.
55 * @returns The new syncpoint minimum value.
56 */
57 u32 RefreshSyncpoint(u32 syncpoint_id);
58
59 /**
60 * Allocates a new syncoint.
61 * @returns The syncpoint ID for the newly allocated syncpoint.
62 */
63 u32 AllocateSyncpoint();
64
65 /**
66 * Increases the maximum value for the specified syncpoint.
67 * @param syncpoint_id Syncpoint ID to be increased.
68 * @param value Value to increase the specified syncpoint by.
69 * @returns The new syncpoint maximum value.
70 */
71 u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value);
72
73private:
74 struct Syncpoint {
75 std::atomic<u32> min;
76 std::atomic<u32> max;
77 std::atomic<bool> is_allocated;
78 };
79
80 std::array<Syncpoint, MaxSyncPoints> syncpoints{};
81
82 Tegra::GPU& gpu;
83};
84
85} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 4f1e210b1..b89a2d41b 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -29,6 +29,10 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
29 .slot = slot, 29 .slot = slot,
30 .status = Buffer::Status::Free, 30 .status = Buffer::Status::Free,
31 .igbp_buffer = igbp_buffer, 31 .igbp_buffer = igbp_buffer,
32 .transform = {},
33 .crop_rect = {},
34 .swap_interval = 0,
35 .multi_fence = {},
32 }); 36 });
33 37
34 buffer_wait_event.writable->Signal(); 38 buffer_wait_event.writable->Signal();
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index c64673dba..44aa2bdae 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -242,6 +242,10 @@ void NVFlinger::Compose() {
242 242
243 const auto& igbp_buffer = buffer->get().igbp_buffer; 243 const auto& igbp_buffer = buffer->get().igbp_buffer;
244 244
245 if (!system.IsPoweredOn()) {
246 return; // We are likely shutting down
247 }
248
245 auto& gpu = system.GPU(); 249 auto& gpu = system.GPU();
246 const auto& multi_fence = buffer->get().multi_fence; 250 const auto& multi_fence = buffer->get().multi_fence;
247 guard->unlock(); 251 guard->unlock();
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index e14c02045..a99d3cf5a 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -14,7 +14,7 @@
14namespace Settings { 14namespace Settings {
15 15
16Values values = {}; 16Values values = {};
17bool configuring_global = true; 17static bool configuring_global = true;
18 18
19std::string GetTimeZoneString() { 19std::string GetTimeZoneString() {
20 static constexpr std::array timezones{ 20 static constexpr std::array timezones{
@@ -81,11 +81,12 @@ void LogSettings() {
81 log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local); 81 log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local);
82} 82}
83 83
84float Volume() { 84bool IsConfiguringGlobal() {
85 if (values.audio_muted) { 85 return configuring_global;
86 return 0.0f; 86}
87 } 87
88 return values.volume.GetValue(); 88void SetConfiguringGlobal(bool is_global) {
89 configuring_global = is_global;
89} 90}
90 91
91bool IsGPULevelExtreme() { 92bool IsGPULevelExtreme() {
@@ -97,6 +98,13 @@ bool IsGPULevelHigh() {
97 values.gpu_accuracy.GetValue() == GPUAccuracy::High; 98 values.gpu_accuracy.GetValue() == GPUAccuracy::High;
98} 99}
99 100
101float Volume() {
102 if (values.audio_muted) {
103 return 0.0f;
104 }
105 return values.volume.GetValue();
106}
107
100void RestoreGlobalState() { 108void RestoreGlobalState() {
101 // If a game is running, DO NOT restore the global settings state 109 // If a game is running, DO NOT restore the global settings state
102 if (Core::System::GetInstance().IsPoweredOn()) { 110 if (Core::System::GetInstance().IsPoweredOn()) {
diff --git a/src/core/settings.h b/src/core/settings.h
index 604805615..28616a574 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -33,8 +33,6 @@ enum class CPUAccuracy {
33 DebugMode = 2, 33 DebugMode = 2,
34}; 34};
35 35
36extern bool configuring_global;
37
38template <typename Type> 36template <typename Type>
39class Setting final { 37class Setting final {
40public: 38public:
@@ -103,7 +101,7 @@ struct Values {
103 bool renderer_debug; 101 bool renderer_debug;
104 Setting<int> vulkan_device; 102 Setting<int> vulkan_device;
105 103
106 Setting<u16> resolution_factor = Setting(static_cast<u16>(1)); 104 Setting<u16> resolution_factor{1};
107 Setting<int> aspect_ratio; 105 Setting<int> aspect_ratio;
108 Setting<int> max_anisotropy; 106 Setting<int> max_anisotropy;
109 Setting<bool> use_frame_limit; 107 Setting<bool> use_frame_limit;
@@ -198,13 +196,18 @@ struct Values {
198 196
199 // Add-Ons 197 // Add-Ons
200 std::map<u64, std::vector<std::string>> disabled_addons; 198 std::map<u64, std::vector<std::string>> disabled_addons;
201} extern values; 199};
202 200
203float Volume(); 201extern Values values;
202
203bool IsConfiguringGlobal();
204void SetConfiguringGlobal(bool is_global);
204 205
205bool IsGPULevelExtreme(); 206bool IsGPULevelExtreme();
206bool IsGPULevelHigh(); 207bool IsGPULevelHigh();
207 208
209float Volume();
210
208std::string GetTimeZoneString(); 211std::string GetTimeZoneString();
209 212
210void Apply(); 213void Apply();
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
index cb67094f6..5bbe6a332 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -42,11 +42,11 @@ public:
42 void Decode(); 42 void Decode();
43 43
44 /// Returns most recently decoded frame 44 /// Returns most recently decoded frame
45 AVFrame* GetCurrentFrame(); 45 [[nodiscard]] AVFrame* GetCurrentFrame();
46 const AVFrame* GetCurrentFrame() const; 46 [[nodiscard]] const AVFrame* GetCurrentFrame() const;
47 47
48 /// Returns the value of current_codec 48 /// Returns the value of current_codec
49 NvdecCommon::VideoCodec GetCurrentCodec() const; 49 [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const;
50 50
51private: 51private:
52 bool initialized{}; 52 bool initialized{};
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp
index 549a40f52..33e063e20 100644
--- a/src/video_core/command_classes/codecs/h264.cpp
+++ b/src/video_core/command_classes/codecs/h264.cpp
@@ -43,7 +43,8 @@ H264::H264(GPU& gpu_) : gpu(gpu_) {}
43 43
44H264::~H264() = default; 44H264::~H264() = default;
45 45
46std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state, bool is_first_frame) { 46const std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state,
47 bool is_first_frame) {
47 H264DecoderContext context{}; 48 H264DecoderContext context{};
48 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); 49 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
49 50
diff --git a/src/video_core/command_classes/codecs/h264.h b/src/video_core/command_classes/codecs/h264.h
index f2292fd2f..273449495 100644
--- a/src/video_core/command_classes/codecs/h264.h
+++ b/src/video_core/command_classes/codecs/h264.h
@@ -51,14 +51,14 @@ public:
51 void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count); 51 void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count);
52 52
53 /// Return the bitstream as a vector. 53 /// Return the bitstream as a vector.
54 std::vector<u8>& GetByteArray(); 54 [[nodiscard]] std::vector<u8>& GetByteArray();
55 const std::vector<u8>& GetByteArray() const; 55 [[nodiscard]] const std::vector<u8>& GetByteArray() const;
56 56
57private: 57private:
58 void WriteBits(s32 value, s32 bit_count); 58 void WriteBits(s32 value, s32 bit_count);
59 void WriteExpGolombCodedInt(s32 value); 59 void WriteExpGolombCodedInt(s32 value);
60 void WriteExpGolombCodedUInt(u32 value); 60 void WriteExpGolombCodedUInt(u32 value);
61 s32 GetFreeBufferBits(); 61 [[nodiscard]] s32 GetFreeBufferBits();
62 void Flush(); 62 void Flush();
63 63
64 s32 buffer_size{8}; 64 s32 buffer_size{8};
@@ -74,8 +74,8 @@ public:
74 ~H264(); 74 ~H264();
75 75
76 /// Compose the H264 header of the frame for FFmpeg decoding 76 /// Compose the H264 header of the frame for FFmpeg decoding
77 std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state, 77 [[nodiscard]] const std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state,
78 bool is_first_frame = false); 78 bool is_first_frame = false);
79 79
80private: 80private:
81 struct H264ParameterSet { 81 struct H264ParameterSet {
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index 42520f856..ab44fdc9e 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -854,7 +854,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
854 return uncomp_writer; 854 return uncomp_writer;
855} 855}
856 856
857std::vector<u8>& VP9::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state) { 857const std::vector<u8>& VP9::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state) {
858 std::vector<u8> bitstream; 858 std::vector<u8> bitstream;
859 { 859 {
860 Vp9FrameContainer curr_frame = GetCurrentFrame(state); 860 Vp9FrameContainer curr_frame = GetCurrentFrame(state);
diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/command_classes/codecs/vp9.h
index 05c9682fa..e2504512c 100644
--- a/src/video_core/command_classes/codecs/vp9.h
+++ b/src/video_core/command_classes/codecs/vp9.h
@@ -119,7 +119,7 @@ public:
119 119
120 /// Composes the VP9 frame from the GPU state information. Based on the official VP9 spec 120 /// Composes the VP9 frame from the GPU state information. Based on the official VP9 spec
121 /// documentation 121 /// documentation
122 std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state); 122 [[nodiscard]] const std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state);
123 123
124 /// Returns true if the most recent frame was a hidden frame. 124 /// Returns true if the most recent frame was a hidden frame.
125 [[nodiscard]] bool WasFrameHidden() const { 125 [[nodiscard]] bool WasFrameHidden() const {
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
index a50acf6e8..4f0b05d22 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -231,9 +231,8 @@ struct PictureInfo {
231 u32 surface_params{}; 231 u32 surface_params{};
232 INSERT_PADDING_WORDS(3); 232 INSERT_PADDING_WORDS(3);
233 233
234 Vp9PictureInfo Convert() const { 234 [[nodiscard]] Vp9PictureInfo Convert() const {
235 235 return {
236 return Vp9PictureInfo{
237 .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0, 236 .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0,
238 .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0, 237 .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0,
239 .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0, 238 .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0,
diff --git a/src/video_core/command_classes/nvdec.h b/src/video_core/command_classes/nvdec.h
index af14f9857..eec4443f9 100644
--- a/src/video_core/command_classes/nvdec.h
+++ b/src/video_core/command_classes/nvdec.h
@@ -26,8 +26,8 @@ public:
26 void ProcessMethod(Method method, const std::vector<u32>& arguments); 26 void ProcessMethod(Method method, const std::vector<u32>& arguments);
27 27
28 /// Return most recently decoded frame 28 /// Return most recently decoded frame
29 AVFrame* GetFrame(); 29 [[nodiscard]] AVFrame* GetFrame();
30 const AVFrame* GetFrame() const; 30 [[nodiscard]] const AVFrame* GetFrame() const;
31 31
32private: 32private:
33 /// Invoke codec to decode a frame 33 /// Invoke codec to decode a frame
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index f2f96ac33..105b85a92 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/cityhash.h"
5#include "common/microprofile.h" 6#include "common/microprofile.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/memory.h" 8#include "core/memory.h"
@@ -12,6 +13,20 @@
12 13
13namespace Tegra { 14namespace Tegra {
14 15
16void CommandList::RefreshIntegrityChecks(GPU& gpu) {
17 command_list_hashes.resize(command_lists.size());
18
19 for (std::size_t index = 0; index < command_lists.size(); ++index) {
20 const CommandListHeader command_list_header = command_lists[index];
21 std::vector<CommandHeader> command_headers(command_list_header.size);
22 gpu.MemoryManager().ReadBlockUnsafe(command_list_header.addr, command_headers.data(),
23 command_list_header.size * sizeof(u32));
24 command_list_hashes[index] =
25 Common::CityHash64(reinterpret_cast<char*>(command_headers.data()),
26 command_list_header.size * sizeof(u32));
27 }
28}
29
15DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {} 30DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {}
16 31
17DmaPusher::~DmaPusher() = default; 32DmaPusher::~DmaPusher() = default;
@@ -45,32 +60,51 @@ bool DmaPusher::Step() {
45 return false; 60 return false;
46 } 61 }
47 62
48 const CommandList& command_list{dma_pushbuffer.front()}; 63 CommandList& command_list{dma_pushbuffer.front()};
49 ASSERT_OR_EXECUTE(!command_list.empty(), {
50 // Somehow the command_list is empty, in order to avoid a crash
51 // We ignore it and assume its size is 0.
52 dma_pushbuffer.pop();
53 dma_pushbuffer_subindex = 0;
54 return true;
55 });
56 const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
57 const GPUVAddr dma_get = command_list_header.addr;
58
59 if (dma_pushbuffer_subindex >= command_list.size()) {
60 // We've gone through the current list, remove it from the queue
61 dma_pushbuffer.pop();
62 dma_pushbuffer_subindex = 0;
63 }
64 64
65 if (command_list_header.size == 0) { 65 ASSERT_OR_EXECUTE(
66 return true; 66 command_list.command_lists.size() || command_list.prefetch_command_list.size(), {
67 } 67 // Somehow the command_list is empty, in order to avoid a crash
68 // We ignore it and assume its size is 0.
69 dma_pushbuffer.pop();
70 dma_pushbuffer_subindex = 0;
71 return true;
72 });
68 73
69 // Push buffer non-empty, read a word 74 if (command_list.prefetch_command_list.size()) {
70 command_headers.resize(command_list_header.size); 75 // Prefetched command list from nvdrv, used for things like synchronization
71 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), 76 command_headers = std::move(command_list.prefetch_command_list);
72 command_list_header.size * sizeof(u32)); 77 dma_pushbuffer.pop();
78 } else {
79 const CommandListHeader command_list_header{
80 command_list.command_lists[dma_pushbuffer_subindex]};
81 const u64 next_hash = command_list.command_list_hashes[dma_pushbuffer_subindex++];
82 const GPUVAddr dma_get = command_list_header.addr;
83
84 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
85 // We've gone through the current list, remove it from the queue
86 dma_pushbuffer.pop();
87 dma_pushbuffer_subindex = 0;
88 }
73 89
90 if (command_list_header.size == 0) {
91 return true;
92 }
93
94 // Push buffer non-empty, read a word
95 command_headers.resize(command_list_header.size);
96 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(),
97 command_list_header.size * sizeof(u32));
98
99 // Integrity check
100 const u64 new_hash = Common::CityHash64(reinterpret_cast<char*>(command_headers.data()),
101 command_list_header.size * sizeof(u32));
102 if (new_hash != next_hash) {
103 LOG_CRITICAL(HW_GPU, "CommandList at addr=0x{:X} is corrupt, skipping!", dma_get);
104 dma_pushbuffer.pop();
105 return true;
106 }
107 }
74 for (std::size_t index = 0; index < command_headers.size();) { 108 for (std::size_t index = 0; index < command_headers.size();) {
75 const CommandHeader& command_header = command_headers[index]; 109 const CommandHeader& command_header = command_headers[index];
76 110
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index efa90d170..9d9a750d9 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -18,6 +18,8 @@ class System;
18 18
19namespace Tegra { 19namespace Tegra {
20 20
21class GPU;
22
21enum class SubmissionMode : u32 { 23enum class SubmissionMode : u32 {
22 IncreasingOld = 0, 24 IncreasingOld = 0,
23 Increasing = 1, 25 Increasing = 1,
@@ -27,6 +29,31 @@ enum class SubmissionMode : u32 {
27 IncreaseOnce = 5 29 IncreaseOnce = 5
28}; 30};
29 31
32// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
33// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
34// So the values you see in docs might be multiplied by 4.
35enum class BufferMethods : u32 {
36 BindObject = 0x0,
37 Nop = 0x2,
38 SemaphoreAddressHigh = 0x4,
39 SemaphoreAddressLow = 0x5,
40 SemaphoreSequence = 0x6,
41 SemaphoreTrigger = 0x7,
42 NotifyIntr = 0x8,
43 WrcacheFlush = 0x9,
44 Unk28 = 0xA,
45 UnkCacheFlush = 0xB,
46 RefCnt = 0x14,
47 SemaphoreAcquire = 0x1A,
48 SemaphoreRelease = 0x1B,
49 FenceValue = 0x1C,
50 FenceAction = 0x1D,
51 WaitForInterrupt = 0x1E,
52 Unk7c = 0x1F,
53 Yield = 0x20,
54 NonPullerMethods = 0x40,
55};
56
30struct CommandListHeader { 57struct CommandListHeader {
31 union { 58 union {
32 u64 raw; 59 u64 raw;
@@ -49,9 +76,26 @@ union CommandHeader {
49static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout"); 76static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout");
50static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!"); 77static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
51 78
52class GPU; 79inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, SubmissionMode mode) {
80 CommandHeader result{};
81 result.method.Assign(static_cast<u32>(method));
82 result.arg_count.Assign(arg_count);
83 result.mode.Assign(mode);
84 return result;
85}
86
87struct CommandList final {
88 CommandList() = default;
89 explicit CommandList(std::size_t size) : command_lists(size) {}
90 explicit CommandList(std::vector<Tegra::CommandHeader>&& prefetch_command_list)
91 : prefetch_command_list{std::move(prefetch_command_list)} {}
53 92
54using CommandList = std::vector<Tegra::CommandListHeader>; 93 void RefreshIntegrityChecks(GPU& gpu);
94
95 std::vector<Tegra::CommandListHeader> command_lists;
96 std::vector<u64> command_list_hashes;
97 std::vector<Tegra::CommandHeader> prefetch_command_list;
98};
55 99
56/** 100/**
57 * The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the 101 * The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the
@@ -60,7 +104,7 @@ using CommandList = std::vector<Tegra::CommandListHeader>;
60 * See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for 104 * See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for
61 * details on this implementation. 105 * details on this implementation.
62 */ 106 */
63class DmaPusher { 107class DmaPusher final {
64public: 108public:
65 explicit DmaPusher(Core::System& system, GPU& gpu); 109 explicit DmaPusher(Core::System& system, GPU& gpu);
66 ~DmaPusher(); 110 ~DmaPusher();
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 171f78183..ebd149c3a 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -194,30 +194,6 @@ void GPU::SyncGuestHost() {
194void GPU::OnCommandListEnd() { 194void GPU::OnCommandListEnd() {
195 renderer->Rasterizer().ReleaseFences(); 195 renderer->Rasterizer().ReleaseFences();
196} 196}
197// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
198// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
199// So the values you see in docs might be multiplied by 4.
200enum class BufferMethods {
201 BindObject = 0x0,
202 Nop = 0x2,
203 SemaphoreAddressHigh = 0x4,
204 SemaphoreAddressLow = 0x5,
205 SemaphoreSequence = 0x6,
206 SemaphoreTrigger = 0x7,
207 NotifyIntr = 0x8,
208 WrcacheFlush = 0x9,
209 Unk28 = 0xA,
210 UnkCacheFlush = 0xB,
211 RefCnt = 0x14,
212 SemaphoreAcquire = 0x1A,
213 SemaphoreRelease = 0x1B,
214 FenceValue = 0x1C,
215 FenceAction = 0x1D,
216 Unk78 = 0x1E,
217 Unk7c = 0x1F,
218 Yield = 0x20,
219 NonPullerMethods = 0x40,
220};
221 197
222enum class GpuSemaphoreOperation { 198enum class GpuSemaphoreOperation {
223 AcquireEqual = 0x1, 199 AcquireEqual = 0x1,
@@ -277,7 +253,12 @@ void GPU::CallPullerMethod(const MethodCall& method_call) {
277 case BufferMethods::UnkCacheFlush: 253 case BufferMethods::UnkCacheFlush:
278 case BufferMethods::WrcacheFlush: 254 case BufferMethods::WrcacheFlush:
279 case BufferMethods::FenceValue: 255 case BufferMethods::FenceValue:
256 break;
280 case BufferMethods::FenceAction: 257 case BufferMethods::FenceAction:
258 ProcessFenceActionMethod();
259 break;
260 case BufferMethods::WaitForInterrupt:
261 ProcessWaitForInterruptMethod();
281 break; 262 break;
282 case BufferMethods::SemaphoreTrigger: { 263 case BufferMethods::SemaphoreTrigger: {
283 ProcessSemaphoreTriggerMethod(); 264 ProcessSemaphoreTriggerMethod();
@@ -391,6 +372,25 @@ void GPU::ProcessBindMethod(const MethodCall& method_call) {
391 } 372 }
392} 373}
393 374
375void GPU::ProcessFenceActionMethod() {
376 switch (regs.fence_action.op) {
377 case FenceOperation::Acquire:
378 WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
379 break;
380 case FenceOperation::Increment:
381 IncrementSyncPoint(regs.fence_action.syncpoint_id);
382 break;
383 default:
384 UNIMPLEMENTED_MSG("Unimplemented operation {}",
385 static_cast<u32>(regs.fence_action.op.Value()));
386 }
387}
388
389void GPU::ProcessWaitForInterruptMethod() {
390 // TODO(bunnei) ImplementMe
391 LOG_WARNING(HW_GPU, "(STUBBED) called");
392}
393
394void GPU::ProcessSemaphoreTriggerMethod() { 394void GPU::ProcessSemaphoreTriggerMethod() {
395 const auto semaphoreOperationMask = 0xF; 395 const auto semaphoreOperationMask = 0xF;
396 const auto op = 396 const auto op =
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index b8c613b11..cf5235a79 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -263,6 +263,24 @@ public:
263 return use_nvdec; 263 return use_nvdec;
264 } 264 }
265 265
266 enum class FenceOperation : u32 {
267 Acquire = 0,
268 Increment = 1,
269 };
270
271 union FenceAction {
272 u32 raw;
273 BitField<0, 1, FenceOperation> op;
274 BitField<8, 24, u32> syncpoint_id;
275
276 static CommandHeader Build(FenceOperation op, u32 syncpoint_id) {
277 FenceAction result{};
278 result.op.Assign(op);
279 result.syncpoint_id.Assign(syncpoint_id);
280 return {result.raw};
281 }
282 };
283
266 struct Regs { 284 struct Regs {
267 static constexpr size_t NUM_REGS = 0x40; 285 static constexpr size_t NUM_REGS = 0x40;
268 286
@@ -291,10 +309,7 @@ public:
291 u32 semaphore_acquire; 309 u32 semaphore_acquire;
292 u32 semaphore_release; 310 u32 semaphore_release;
293 u32 fence_value; 311 u32 fence_value;
294 union { 312 FenceAction fence_action;
295 BitField<4, 4, u32> operation;
296 BitField<8, 8, u32> id;
297 } fence_action;
298 INSERT_UNION_PADDING_WORDS(0xE2); 313 INSERT_UNION_PADDING_WORDS(0xE2);
299 314
300 // Puller state 315 // Puller state
@@ -342,6 +357,8 @@ protected:
342 357
343private: 358private:
344 void ProcessBindMethod(const MethodCall& method_call); 359 void ProcessBindMethod(const MethodCall& method_call);
360 void ProcessFenceActionMethod();
361 void ProcessWaitForInterruptMethod();
345 void ProcessSemaphoreTriggerMethod(); 362 void ProcessSemaphoreTriggerMethod();
346 void ProcessSemaphoreRelease(); 363 void ProcessSemaphoreRelease();
347 void ProcessSemaphoreAcquire(); 364 void ProcessSemaphoreAcquire();
diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
index f4db62787..d6120c23e 100644
--- a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
@@ -39,8 +39,8 @@ using Operation = const OperationNode&;
39constexpr std::array INTERNAL_FLAG_NAMES = {"ZERO", "SIGN", "CARRY", "OVERFLOW"}; 39constexpr std::array INTERNAL_FLAG_NAMES = {"ZERO", "SIGN", "CARRY", "OVERFLOW"};
40 40
41char Swizzle(std::size_t component) { 41char Swizzle(std::size_t component) {
42 ASSERT(component < 4); 42 static constexpr std::string_view SWIZZLE{"xyzw"};
43 return component["xyzw"]; 43 return SWIZZLE.at(component);
44} 44}
45 45
46constexpr bool IsGenericAttribute(Attribute::Index index) { 46constexpr bool IsGenericAttribute(Attribute::Index index) {
@@ -224,7 +224,7 @@ private:
224 224
225 std::string Visit(const Node& node); 225 std::string Visit(const Node& node);
226 226
227 std::pair<std::string, std::size_t> BuildCoords(Operation); 227 std::tuple<std::string, std::string, std::size_t> BuildCoords(Operation);
228 std::string BuildAoffi(Operation); 228 std::string BuildAoffi(Operation);
229 std::string GlobalMemoryPointer(const GmemNode& gmem); 229 std::string GlobalMemoryPointer(const GmemNode& gmem);
230 void Exit(); 230 void Exit();
@@ -1416,12 +1416,12 @@ std::string ARBDecompiler::Visit(const Node& node) {
1416 return {}; 1416 return {};
1417} 1417}
1418 1418
1419std::pair<std::string, std::size_t> ARBDecompiler::BuildCoords(Operation operation) { 1419std::tuple<std::string, std::string, std::size_t> ARBDecompiler::BuildCoords(Operation operation) {
1420 const auto& meta = std::get<MetaTexture>(operation.GetMeta()); 1420 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1421 UNIMPLEMENTED_IF(meta.sampler.is_indexed); 1421 UNIMPLEMENTED_IF(meta.sampler.is_indexed);
1422 UNIMPLEMENTED_IF(meta.sampler.is_shadow && meta.sampler.is_array &&
1423 meta.sampler.type == Tegra::Shader::TextureType::TextureCube);
1424 1422
1423 const bool is_extended = meta.sampler.is_shadow && meta.sampler.is_array &&
1424 meta.sampler.type == Tegra::Shader::TextureType::TextureCube;
1425 const std::size_t count = operation.GetOperandsCount(); 1425 const std::size_t count = operation.GetOperandsCount();
1426 std::string temporary = AllocVectorTemporary(); 1426 std::string temporary = AllocVectorTemporary();
1427 std::size_t i = 0; 1427 std::size_t i = 0;
@@ -1429,12 +1429,21 @@ std::pair<std::string, std::size_t> ARBDecompiler::BuildCoords(Operation operati
1429 AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i), Visit(operation[i])); 1429 AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i), Visit(operation[i]));
1430 } 1430 }
1431 if (meta.sampler.is_array) { 1431 if (meta.sampler.is_array) {
1432 AddLine("I2F.S {}.{}, {};", temporary, Swizzle(i++), Visit(meta.array)); 1432 AddLine("I2F.S {}.{}, {};", temporary, Swizzle(i), Visit(meta.array));
1433 ++i;
1433 } 1434 }
1434 if (meta.sampler.is_shadow) { 1435 if (meta.sampler.is_shadow) {
1435 AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i++), Visit(meta.depth_compare)); 1436 std::string compare = Visit(meta.depth_compare);
1437 if (is_extended) {
1438 ASSERT(i == 4);
1439 std::string extra_coord = AllocVectorTemporary();
1440 AddLine("MOV.F {}.x, {};", extra_coord, compare);
1441 return {fmt::format("{}, {}", temporary, extra_coord), extra_coord, 0};
1442 }
1443 AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i), compare);
1444 ++i;
1436 } 1445 }
1437 return {std::move(temporary), i}; 1446 return {temporary, temporary, i};
1438} 1447}
1439 1448
1440std::string ARBDecompiler::BuildAoffi(Operation operation) { 1449std::string ARBDecompiler::BuildAoffi(Operation operation) {
@@ -1859,7 +1868,7 @@ std::string ARBDecompiler::LogicalAddCarry(Operation operation) {
1859std::string ARBDecompiler::Texture(Operation operation) { 1868std::string ARBDecompiler::Texture(Operation operation) {
1860 const auto& meta = std::get<MetaTexture>(operation.GetMeta()); 1869 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1861 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; 1870 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1862 const auto [temporary, swizzle] = BuildCoords(operation); 1871 const auto [coords, temporary, swizzle] = BuildCoords(operation);
1863 1872
1864 std::string_view opcode = "TEX"; 1873 std::string_view opcode = "TEX";
1865 std::string extra; 1874 std::string extra;
@@ -1888,7 +1897,7 @@ std::string ARBDecompiler::Texture(Operation operation) {
1888 } 1897 }
1889 } 1898 }
1890 1899
1891 AddLine("{}.F {}, {},{} texture[{}], {}{};", opcode, temporary, temporary, extra, sampler_id, 1900 AddLine("{}.F {}, {},{} texture[{}], {}{};", opcode, temporary, coords, extra, sampler_id,
1892 TextureType(meta), BuildAoffi(operation)); 1901 TextureType(meta), BuildAoffi(operation));
1893 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element)); 1902 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
1894 return fmt::format("{}.x", temporary); 1903 return fmt::format("{}.x", temporary);
@@ -1897,7 +1906,7 @@ std::string ARBDecompiler::Texture(Operation operation) {
1897std::string ARBDecompiler::TextureGather(Operation operation) { 1906std::string ARBDecompiler::TextureGather(Operation operation) {
1898 const auto& meta = std::get<MetaTexture>(operation.GetMeta()); 1907 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1899 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; 1908 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1900 const auto [temporary, swizzle] = BuildCoords(operation); 1909 const auto [coords, temporary, swizzle] = BuildCoords(operation);
1901 1910
1902 std::string comp; 1911 std::string comp;
1903 if (!meta.sampler.is_shadow) { 1912 if (!meta.sampler.is_shadow) {
@@ -1907,7 +1916,7 @@ std::string ARBDecompiler::TextureGather(Operation operation) {
1907 1916
1908 AddLine("TXG.F {}, {}, texture[{}]{}, {}{};", temporary, temporary, sampler_id, comp, 1917 AddLine("TXG.F {}, {}, texture[{}]{}, {}{};", temporary, temporary, sampler_id, comp,
1909 TextureType(meta), BuildAoffi(operation)); 1918 TextureType(meta), BuildAoffi(operation));
1910 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element)); 1919 AddLine("MOV.U {}.x, {}.{};", temporary, coords, Swizzle(meta.element));
1911 return fmt::format("{}.x", temporary); 1920 return fmt::format("{}.x", temporary);
1912} 1921}
1913 1922
@@ -1945,13 +1954,13 @@ std::string ARBDecompiler::TextureQueryLod(Operation operation) {
1945std::string ARBDecompiler::TexelFetch(Operation operation) { 1954std::string ARBDecompiler::TexelFetch(Operation operation) {
1946 const auto& meta = std::get<MetaTexture>(operation.GetMeta()); 1955 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1947 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; 1956 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1948 const auto [temporary, swizzle] = BuildCoords(operation); 1957 const auto [coords, temporary, swizzle] = BuildCoords(operation);
1949 1958
1950 if (!meta.sampler.is_buffer) { 1959 if (!meta.sampler.is_buffer) {
1951 ASSERT(swizzle < 4); 1960 ASSERT(swizzle < 4);
1952 AddLine("MOV.F {}.w, {};", temporary, Visit(meta.lod)); 1961 AddLine("MOV.F {}.w, {};", temporary, Visit(meta.lod));
1953 } 1962 }
1954 AddLine("TXF.F {}, {}, texture[{}], {}{};", temporary, temporary, sampler_id, TextureType(meta), 1963 AddLine("TXF.F {}, {}, texture[{}], {}{};", temporary, coords, sampler_id, TextureType(meta),
1955 BuildAoffi(operation)); 1964 BuildAoffi(operation));
1956 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element)); 1965 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
1957 return fmt::format("{}.x", temporary); 1966 return fmt::format("{}.x", temporary);
@@ -1962,7 +1971,7 @@ std::string ARBDecompiler::TextureGradient(Operation operation) {
1962 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index; 1971 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1963 const std::string ddx = AllocVectorTemporary(); 1972 const std::string ddx = AllocVectorTemporary();
1964 const std::string ddy = AllocVectorTemporary(); 1973 const std::string ddy = AllocVectorTemporary();
1965 const std::string coord = BuildCoords(operation).first; 1974 const std::string coord = std::get<1>(BuildCoords(operation));
1966 1975
1967 const std::size_t num_components = meta.derivates.size() / 2; 1976 const std::size_t num_components = meta.derivates.size() / 2;
1968 for (std::size_t index = 0; index < num_components; ++index) { 1977 for (std::size_t index = 0; index < num_components; ++index) {
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index bbb8fb095..95ca96c8e 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -2056,15 +2056,19 @@ private:
2056 } 2056 }
2057 2057
2058 Expression Texture(Operation operation) { 2058 Expression Texture(Operation operation) {
2059 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 2059 const auto meta = std::get<MetaTexture>(operation.GetMeta());
2060 ASSERT(meta); 2060 const bool separate_dc = meta.sampler.type == TextureType::TextureCube &&
2061 2061 meta.sampler.is_array && meta.sampler.is_shadow;
2062 std::string expr = GenerateTexture( 2062 // TODO: Replace this with an array and make GenerateTexture use C++20 std::span
2063 operation, "", {TextureOffset{}, TextureArgument{Type::Float, meta->bias}}); 2063 const std::vector<TextureIR> extras{
2064 if (meta->sampler.is_shadow) { 2064 TextureOffset{},
2065 expr = "vec4(" + expr + ')'; 2065 TextureArgument{Type::Float, meta.bias},
2066 };
2067 std::string expr = GenerateTexture(operation, "", extras, separate_dc);
2068 if (meta.sampler.is_shadow) {
2069 expr = fmt::format("vec4({})", expr);
2066 } 2070 }
2067 return {expr + GetSwizzle(meta->element), Type::Float}; 2071 return {expr + GetSwizzle(meta.element), Type::Float};
2068 } 2072 }
2069 2073
2070 Expression TextureLod(Operation operation) { 2074 Expression TextureLod(Operation operation) {
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index e1217ca83..f34ed6735 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -771,13 +771,18 @@ void VKDevice::CollectTelemetryParameters() {
771 VkPhysicalDeviceDriverPropertiesKHR driver{ 771 VkPhysicalDeviceDriverPropertiesKHR driver{
772 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR, 772 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
773 .pNext = nullptr, 773 .pNext = nullptr,
774 .driverID = {},
775 .driverName = {},
776 .driverInfo = {},
777 .conformanceVersion = {},
774 }; 778 };
775 779
776 VkPhysicalDeviceProperties2KHR properties{ 780 VkPhysicalDeviceProperties2KHR device_properties{
777 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, 781 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
778 .pNext = &driver, 782 .pNext = &driver,
783 .properties = {},
779 }; 784 };
780 physical.GetProperties2KHR(properties); 785 physical.GetProperties2KHR(device_properties);
781 786
782 driver_id = driver.driverID; 787 driver_id = driver.driverID;
783 vendor_name = driver.driverName; 788 vendor_name = driver.driverName;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 696eaeb5f..0e8f9c352 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -159,6 +159,7 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
159 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, 159 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
160 .pNext = nullptr, 160 .pNext = nullptr,
161 .flags = 0, 161 .flags = 0,
162 .codeSize = 0,
162 }; 163 };
163 164
164 std::vector<vk::ShaderModule> modules; 165 std::vector<vk::ShaderModule> modules;
@@ -388,6 +389,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
388 .logicOp = VK_LOGIC_OP_COPY, 389 .logicOp = VK_LOGIC_OP_COPY,
389 .attachmentCount = static_cast<u32>(num_attachments), 390 .attachmentCount = static_cast<u32>(num_attachments),
390 .pAttachments = cb_attachments.data(), 391 .pAttachments = cb_attachments.data(),
392 .blendConstants = {},
391 }; 393 };
392 394
393 std::vector dynamic_states{ 395 std::vector dynamic_states{
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index 4e932a4b6..02fdccd86 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -556,7 +556,6 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
556 const bool is_shadow = depth_compare != nullptr; 556 const bool is_shadow = depth_compare != nullptr;
557 const bool is_bindless = bindless_reg.has_value(); 557 const bool is_bindless = bindless_reg.has_value();
558 558
559 UNIMPLEMENTED_IF(texture_type == TextureType::TextureCube && is_array && is_shadow);
560 ASSERT_MSG(texture_type != TextureType::Texture3D || !is_array || !is_shadow, 559 ASSERT_MSG(texture_type != TextureType::Texture3D || !is_array || !is_shadow,
561 "Illegal texture type"); 560 "Illegal texture type");
562 561
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index e8515321b..13dd16356 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -240,6 +240,7 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
240 .is_tiled = is_tiled, 240 .is_tiled = is_tiled,
241 .srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB || 241 .srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB ||
242 config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB, 242 config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB,
243 .is_layered = false,
243 .block_width = is_tiled ? std::min(config.BlockWidth(), 5U) : 0U, 244 .block_width = is_tiled ? std::min(config.BlockWidth(), 5U) : 0U,
244 .block_height = is_tiled ? std::min(config.BlockHeight(), 5U) : 0U, 245 .block_height = is_tiled ? std::min(config.BlockHeight(), 5U) : 0U,
245 .block_depth = is_tiled ? std::min(config.BlockDepth(), 5U) : 0U, 246 .block_depth = is_tiled ? std::min(config.BlockDepth(), 5U) : 0U,
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index fa9124ecf..db9518798 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -25,8 +25,8 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
25 connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, 25 connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
26 &ConfigureAudio::UpdateAudioDevices); 26 &ConfigureAudio::UpdateAudioDevices);
27 27
28 ui->volume_label->setVisible(Settings::configuring_global); 28 ui->volume_label->setVisible(Settings::IsConfiguringGlobal());
29 ui->volume_combo_box->setVisible(!Settings::configuring_global); 29 ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal());
30 30
31 SetupPerGameUI(); 31 SetupPerGameUI();
32 32
@@ -51,7 +51,7 @@ void ConfigureAudio::SetConfiguration() {
51 51
52 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue()); 52 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
53 53
54 if (!Settings::configuring_global) { 54 if (!Settings::IsConfiguringGlobal()) {
55 if (Settings::values.volume.UsingGlobal()) { 55 if (Settings::values.volume.UsingGlobal()) {
56 ui->volume_combo_box->setCurrentIndex(0); 56 ui->volume_combo_box->setCurrentIndex(0);
57 ui->volume_slider->setEnabled(false); 57 ui->volume_slider->setEnabled(false);
@@ -99,7 +99,7 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
99} 99}
100 100
101void ConfigureAudio::ApplyConfiguration() { 101void ConfigureAudio::ApplyConfiguration() {
102 if (Settings::configuring_global) { 102 if (Settings::IsConfiguringGlobal()) {
103 Settings::values.sink_id = 103 Settings::values.sink_id =
104 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) 104 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
105 .toStdString(); 105 .toStdString();
@@ -165,7 +165,7 @@ void ConfigureAudio::RetranslateUI() {
165} 165}
166 166
167void ConfigureAudio::SetupPerGameUI() { 167void ConfigureAudio::SetupPerGameUI() {
168 if (Settings::configuring_global) { 168 if (Settings::IsConfiguringGlobal()) {
169 ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal()); 169 ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
170 ui->toggle_audio_stretching->setEnabled( 170 ui->toggle_audio_stretching->setEnabled(
171 Settings::values.enable_audio_stretching.UsingGlobal()); 171 Settings::values.enable_audio_stretching.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 8186929a6..5041e0bf8 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -15,7 +15,7 @@
15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, 15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
16 InputCommon::InputSubsystem* input_subsystem) 16 InputCommon::InputSubsystem* input_subsystem)
17 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { 17 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
18 Settings::configuring_global = true; 18 Settings::SetConfiguringGlobal(true);
19 19
20 ui->setupUi(this); 20 ui->setupUi(this);
21 ui->hotkeysTab->Populate(registry); 21 ui->hotkeysTab->Populate(registry);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 830096ea0..d4d29d422 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -19,7 +19,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
19 19
20 SetConfiguration(); 20 SetConfiguration();
21 21
22 if (Settings::configuring_global) { 22 if (Settings::IsConfiguringGlobal()) {
23 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, 23 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit,
24 [this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); }); 24 [this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); });
25 } 25 }
@@ -41,7 +41,7 @@ void ConfigureGeneral::SetConfiguration() {
41 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); 41 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
42 ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); 42 ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
43 43
44 if (Settings::configuring_global) { 44 if (Settings::IsConfiguringGlobal()) {
45 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue()); 45 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue());
46 } else { 46 } else {
47 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() && 47 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() &&
@@ -50,7 +50,7 @@ void ConfigureGeneral::SetConfiguration() {
50} 50}
51 51
52void ConfigureGeneral::ApplyConfiguration() { 52void ConfigureGeneral::ApplyConfiguration() {
53 if (Settings::configuring_global) { 53 if (Settings::IsConfiguringGlobal()) {
54 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 54 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
55 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 55 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
56 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); 56 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
@@ -93,7 +93,7 @@ void ConfigureGeneral::RetranslateUI() {
93} 93}
94 94
95void ConfigureGeneral::SetupPerGameUI() { 95void ConfigureGeneral::SetupPerGameUI() {
96 if (Settings::configuring_global) { 96 if (Settings::IsConfiguringGlobal()) {
97 ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal()); 97 ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
98 ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); 98 ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
99 99
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 4f083ecda..6fda0ce35 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -33,7 +33,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
33 33
34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] { 34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
35 UpdateDeviceComboBox(); 35 UpdateDeviceComboBox();
36 if (!Settings::configuring_global) { 36 if (!Settings::IsConfiguringGlobal()) {
37 ConfigurationShared::SetHighlight( 37 ConfigurationShared::SetHighlight(
38 ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX); 38 ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
39 } 39 }
@@ -49,8 +49,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
49 UpdateBackgroundColorButton(new_bg_color); 49 UpdateBackgroundColorButton(new_bg_color);
50 }); 50 });
51 51
52 ui->bg_label->setVisible(Settings::configuring_global); 52 ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
53 ui->bg_combobox->setVisible(!Settings::configuring_global); 53 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
54} 54}
55 55
56void ConfigureGraphics::UpdateDeviceSelection(int device) { 56void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -76,7 +76,7 @@ void ConfigureGraphics::SetConfiguration() {
76 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 76 Settings::values.use_asynchronous_gpu_emulation.GetValue());
77 ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue()); 77 ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue());
78 78
79 if (Settings::configuring_global) { 79 if (Settings::IsConfiguringGlobal()) {
80 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); 80 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
81 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); 81 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
82 } else { 82 } else {
@@ -100,7 +100,7 @@ void ConfigureGraphics::SetConfiguration() {
100} 100}
101 101
102void ConfigureGraphics::ApplyConfiguration() { 102void ConfigureGraphics::ApplyConfiguration() {
103 if (Settings::configuring_global) { 103 if (Settings::IsConfiguringGlobal()) {
104 // Guard if during game and set to game-specific value 104 // Guard if during game and set to game-specific value
105 if (Settings::values.renderer_backend.UsingGlobal()) { 105 if (Settings::values.renderer_backend.UsingGlobal()) {
106 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); 106 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
@@ -194,7 +194,7 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
194 194
195 bool enabled = false; 195 bool enabled = false;
196 196
197 if (!Settings::configuring_global && 197 if (!Settings::IsConfiguringGlobal() &&
198 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { 198 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
199 vulkan_device = Settings::values.vulkan_device.GetValue(); 199 vulkan_device = Settings::values.vulkan_device.GetValue();
200 } 200 }
@@ -212,7 +212,7 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
212 break; 212 break;
213 } 213 }
214 // If in per-game config and use global is selected, don't enable. 214 // If in per-game config and use global is selected, don't enable.
215 enabled &= !(!Settings::configuring_global && 215 enabled &= !(!Settings::IsConfiguringGlobal() &&
216 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX); 216 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);
217 ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); 217 ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
218} 218}
@@ -227,7 +227,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
227} 227}
228 228
229Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 229Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
230 if (Settings::configuring_global) { 230 if (Settings::IsConfiguringGlobal()) {
231 return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); 231 return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
232 } 232 }
233 233
@@ -241,7 +241,7 @@ Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
241} 241}
242 242
243void ConfigureGraphics::SetupPerGameUI() { 243void ConfigureGraphics::SetupPerGameUI() {
244 if (Settings::configuring_global) { 244 if (Settings::IsConfiguringGlobal()) {
245 ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); 245 ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
246 ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal()); 246 ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
247 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); 247 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 73f276949..383c7bac8 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -32,7 +32,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
32 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 32 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
33 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 33 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
34 34
35 if (Settings::configuring_global) { 35 if (Settings::IsConfiguringGlobal()) {
36 ui->gpu_accuracy->setCurrentIndex( 36 ui->gpu_accuracy->setCurrentIndex(
37 static_cast<int>(Settings::values.gpu_accuracy.GetValue())); 37 static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
38 ui->anisotropic_filtering_combobox->setCurrentIndex( 38 ui->anisotropic_filtering_combobox->setCurrentIndex(
@@ -52,9 +52,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
52 // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots) 52 // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
53 const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>( 53 const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
54 ui->gpu_accuracy->currentIndex() - 54 ui->gpu_accuracy->currentIndex() -
55 ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); 55 ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
56 56
57 if (Settings::configuring_global) { 57 if (Settings::IsConfiguringGlobal()) {
58 // Must guard in case of a during-game configuration when set to be game-specific. 58 // Must guard in case of a during-game configuration when set to be game-specific.
59 if (Settings::values.gpu_accuracy.UsingGlobal()) { 59 if (Settings::values.gpu_accuracy.UsingGlobal()) {
60 Settings::values.gpu_accuracy.SetValue(gpu_accuracy); 60 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
@@ -118,7 +118,7 @@ void ConfigureGraphicsAdvanced::RetranslateUI() {
118 118
119void ConfigureGraphicsAdvanced::SetupPerGameUI() { 119void ConfigureGraphicsAdvanced::SetupPerGameUI() {
120 // Disable if not global (only happens during game) 120 // Disable if not global (only happens during game)
121 if (Settings::configuring_global) { 121 if (Settings::IsConfiguringGlobal()) {
122 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 122 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
123 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 123 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
124 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); 124 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 1e49f0787..002db3f93 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -31,7 +31,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
31 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { 31 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
32 game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false); 32 game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false);
33 33
34 Settings::configuring_global = false; 34 Settings::SetConfiguringGlobal(false);
35 35
36 ui->setupUi(this); 36 ui->setupUi(this);
37 setFocusPolicy(Qt::ClickFocus); 37 setFocusPolicy(Qt::ClickFocus);
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 5e8e201dc..59a58d92c 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -37,8 +37,8 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
37 } 37 }
38 }); 38 });
39 39
40 ui->label_console_id->setVisible(Settings::configuring_global); 40 ui->label_console_id->setVisible(Settings::IsConfiguringGlobal());
41 ui->button_regenerate_console_id->setVisible(Settings::configuring_global); 41 ui->button_regenerate_console_id->setVisible(Settings::IsConfiguringGlobal());
42 42
43 SetupPerGameUI(); 43 SetupPerGameUI();
44 44
@@ -78,7 +78,7 @@ void ConfigureSystem::SetConfiguration() {
78 Settings::values.rng_seed.UsingGlobal()); 78 Settings::values.rng_seed.UsingGlobal());
79 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); 79 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
80 80
81 if (Settings::configuring_global) { 81 if (Settings::IsConfiguringGlobal()) {
82 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); 82 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
83 ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue()); 83 ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue());
84 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue()); 84 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue());
@@ -125,7 +125,7 @@ void ConfigureSystem::ApplyConfiguration() {
125 return; 125 return;
126 } 126 }
127 127
128 if (Settings::configuring_global) { 128 if (Settings::IsConfiguringGlobal()) {
129 // Guard if during game and set to game-specific value 129 // Guard if during game and set to game-specific value
130 if (Settings::values.language_index.UsingGlobal()) { 130 if (Settings::values.language_index.UsingGlobal()) {
131 Settings::values.language_index.SetValue(ui->combo_language->currentIndex()); 131 Settings::values.language_index.SetValue(ui->combo_language->currentIndex());
@@ -218,7 +218,7 @@ void ConfigureSystem::RefreshConsoleID() {
218} 218}
219 219
220void ConfigureSystem::SetupPerGameUI() { 220void ConfigureSystem::SetupPerGameUI() {
221 if (Settings::configuring_global) { 221 if (Settings::IsConfiguringGlobal()) {
222 ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal()); 222 ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal());
223 ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal()); 223 ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal());
224 ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal()); 224 ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal());