summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt6
-rw-r--r--src/audio_core/sink/sink_stream.cpp11
-rw-r--r--src/audio_core/sink/sink_stream.h5
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.cpp46
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.h2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp44
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.h2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp2
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.cpp6
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.h16
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp3
-rw-r--r--src/core/hle/service/nvflinger/producer_listener.h1
-rw-r--r--src/input_common/CMakeLists.txt6
-rw-r--r--src/input_common/drivers/sdl_driver.cpp15
-rw-r--r--src/input_common/drivers/sdl_driver.h3
-rw-r--r--src/input_common/main.cpp10
-rw-r--r--src/input_common/main.h3
-rw-r--r--src/video_core/engines/engine_upload.cpp7
-rw-r--r--src/video_core/engines/engine_upload.h2
-rw-r--r--src/video_core/engines/fermi_2d.h2
-rw-r--r--src/video_core/engines/kepler_compute.cpp6
-rw-r--r--src/video_core/engines/kepler_compute.h14
-rw-r--r--src/video_core/engines/kepler_memory.cpp6
-rw-r--r--src/video_core/engines/maxwell_3d.cpp12
-rw-r--r--src/video_core/engines/maxwell_3d.h79
-rw-r--r--src/video_core/engines/maxwell_dma.cpp14
-rw-r--r--src/video_core/engines/puller.cpp6
-rw-r--r--src/video_core/host1x/syncpoint_manager.cpp6
-rw-r--r--src/video_core/host1x/syncpoint_manager.h12
-rw-r--r--src/video_core/surface.cpp15
-rw-r--r--src/yuzu/CMakeLists.txt6
-rw-r--r--src/yuzu/main.cpp12
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/startup_checks.cpp82
-rw-r--r--src/yuzu/startup_checks.h4
37 files changed, 265 insertions, 216 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 8e3a8f5a8..75416c53a 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -226,6 +226,10 @@ if(ENABLE_CUBEB)
226 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1) 226 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
227endif() 227endif()
228if(ENABLE_SDL2) 228if(ENABLE_SDL2)
229 target_link_libraries(audio_core PRIVATE SDL2) 229 if (YUZU_USE_EXTERNAL_SDL2)
230 target_link_libraries(audio_core PRIVATE SDL2-static)
231 else()
232 target_link_libraries(audio_core PRIVATE SDL2)
233 endif()
230 target_compile_definitions(audio_core PRIVATE HAVE_SDL2) 234 target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
231endif() 235endif()
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index 849f862b0..67e194e3c 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -266,19 +266,20 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
266} 266}
267 267
268void SinkStream::Stall() { 268void SinkStream::Stall() {
269 if (stalled) { 269 std::scoped_lock lk{stall_guard};
270 if (stalled_lock) {
270 return; 271 return;
271 } 272 }
272 stalled = true; 273 stalled_lock = system.StallProcesses();
273 system.StallProcesses();
274} 274}
275 275
276void SinkStream::Unstall() { 276void SinkStream::Unstall() {
277 if (!stalled) { 277 std::scoped_lock lk{stall_guard};
278 if (!stalled_lock) {
278 return; 279 return;
279 } 280 }
280 system.UnstallProcesses(); 281 system.UnstallProcesses();
281 stalled = false; 282 stalled_lock.unlock();
282} 283}
283 284
284} // namespace AudioCore::Sink 285} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h
index 38a4b2f51..5fea72ab7 100644
--- a/src/audio_core/sink/sink_stream.h
+++ b/src/audio_core/sink/sink_stream.h
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include <atomic> 7#include <atomic>
8#include <memory> 8#include <memory>
9#include <mutex>
9#include <span> 10#include <span>
10#include <vector> 11#include <vector>
11 12
@@ -240,8 +241,8 @@ private:
240 f32 system_volume{1.0f}; 241 f32 system_volume{1.0f};
241 /// Set via IAudioDevice service calls 242 /// Set via IAudioDevice service calls
242 f32 device_volume{1.0f}; 243 f32 device_volume{1.0f};
243 /// True if coretiming has been stalled 244 std::mutex stall_guard;
244 bool stalled{false}; 245 std::unique_lock<std::mutex> stalled_lock;
245}; 246};
246 247
247using SinkStreamPtr = std::unique_ptr<SinkStream>; 248using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d8934be52..94d4e2212 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -189,7 +189,7 @@ struct System::Impl {
189 189
190 kernel.Suspend(false); 190 kernel.Suspend(false);
191 core_timing.SyncPause(false); 191 core_timing.SyncPause(false);
192 is_paused = false; 192 is_paused.store(false, std::memory_order_relaxed);
193 193
194 return status; 194 return status;
195 } 195 }
@@ -200,14 +200,13 @@ struct System::Impl {
200 200
201 core_timing.SyncPause(true); 201 core_timing.SyncPause(true);
202 kernel.Suspend(true); 202 kernel.Suspend(true);
203 is_paused = true; 203 is_paused.store(true, std::memory_order_relaxed);
204 204
205 return status; 205 return status;
206 } 206 }
207 207
208 bool IsPaused() const { 208 bool IsPaused() const {
209 std::unique_lock lk(suspend_guard); 209 return is_paused.load(std::memory_order_relaxed);
210 return is_paused;
211 } 210 }
212 211
213 std::unique_lock<std::mutex> StallProcesses() { 212 std::unique_lock<std::mutex> StallProcesses() {
@@ -218,7 +217,7 @@ struct System::Impl {
218 } 217 }
219 218
220 void UnstallProcesses() { 219 void UnstallProcesses() {
221 if (!is_paused) { 220 if (!IsPaused()) {
222 core_timing.SyncPause(false); 221 core_timing.SyncPause(false);
223 kernel.Suspend(false); 222 kernel.Suspend(false);
224 } 223 }
@@ -465,7 +464,7 @@ struct System::Impl {
465 } 464 }
466 465
467 mutable std::mutex suspend_guard; 466 mutable std::mutex suspend_guard;
468 bool is_paused{}; 467 std::atomic_bool is_paused{};
469 std::atomic<bool> is_shutting_down{}; 468 std::atomic<bool> is_shutting_down{};
470 469
471 Timing::CoreTiming core_timing; 470 Timing::CoreTiming core_timing;
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp
index eda2041a0..aba51d280 100644
--- a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp
+++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp
@@ -28,13 +28,15 @@ SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host
28SyncpointManager::~SyncpointManager() = default; 28SyncpointManager::~SyncpointManager() = default;
29 29
30u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) { 30u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) {
31 if (syncpoints.at(id).reserved) { 31 auto& syncpoint = syncpoints.at(id);
32
33 if (syncpoint.reserved) {
32 ASSERT_MSG(false, "Requested syncpoint is in use"); 34 ASSERT_MSG(false, "Requested syncpoint is in use");
33 return 0; 35 return 0;
34 } 36 }
35 37
36 syncpoints.at(id).reserved = true; 38 syncpoint.reserved = true;
37 syncpoints.at(id).interface_managed = client_managed; 39 syncpoint.interface_managed = client_managed;
38 40
39 return id; 41 return id;
40} 42}
@@ -56,11 +58,12 @@ u32 SyncpointManager::AllocateSyncpoint(bool client_managed) {
56 58
57void SyncpointManager::FreeSyncpoint(u32 id) { 59void SyncpointManager::FreeSyncpoint(u32 id) {
58 std::lock_guard lock(reservation_lock); 60 std::lock_guard lock(reservation_lock);
59 ASSERT(syncpoints.at(id).reserved); 61 auto& syncpoint = syncpoints.at(id);
60 syncpoints.at(id).reserved = false; 62 ASSERT(syncpoint.reserved);
63 syncpoint.reserved = false;
61} 64}
62 65
63bool SyncpointManager::IsSyncpointAllocated(u32 id) { 66bool SyncpointManager::IsSyncpointAllocated(u32 id) const {
64 return (id <= SyncpointCount) && syncpoints[id].reserved; 67 return (id <= SyncpointCount) && syncpoints[id].reserved;
65} 68}
66 69
@@ -69,7 +72,7 @@ bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const {
69 72
70 if (!syncpoint.reserved) { 73 if (!syncpoint.reserved) {
71 ASSERT(false); 74 ASSERT(false);
72 return 0; 75 return false;
73 } 76 }
74 77
75 // If the interface manages counters then we don't keep track of the maximum value as it handles 78 // If the interface manages counters then we don't keep track of the maximum value as it handles
@@ -82,40 +85,51 @@ bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const {
82} 85}
83 86
84u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) { 87u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) {
85 if (!syncpoints.at(id).reserved) { 88 auto& syncpoint = syncpoints.at(id);
89
90 if (!syncpoint.reserved) {
86 ASSERT(false); 91 ASSERT(false);
87 return 0; 92 return 0;
88 } 93 }
89 94
90 return syncpoints.at(id).counter_max += amount; 95 return syncpoint.counter_max += amount;
91} 96}
92 97
93u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { 98u32 SyncpointManager::ReadSyncpointMinValue(u32 id) {
94 if (!syncpoints.at(id).reserved) { 99 auto& syncpoint = syncpoints.at(id);
100
101 if (!syncpoint.reserved) {
95 ASSERT(false); 102 ASSERT(false);
96 return 0; 103 return 0;
97 } 104 }
98 105
99 return syncpoints.at(id).counter_min; 106 return syncpoint.counter_min;
100} 107}
101 108
102u32 SyncpointManager::UpdateMin(u32 id) { 109u32 SyncpointManager::UpdateMin(u32 id) {
103 if (!syncpoints.at(id).reserved) { 110 auto& syncpoint = syncpoints.at(id);
111
112 if (!syncpoint.reserved) {
104 ASSERT(false); 113 ASSERT(false);
105 return 0; 114 return 0;
106 } 115 }
107 116
108 syncpoints.at(id).counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id); 117 syncpoint.counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id);
109 return syncpoints.at(id).counter_min; 118 return syncpoint.counter_min;
110} 119}
111 120
112NvFence SyncpointManager::GetSyncpointFence(u32 id) { 121NvFence SyncpointManager::GetSyncpointFence(u32 id) {
113 if (!syncpoints.at(id).reserved) { 122 auto& syncpoint = syncpoints.at(id);
123
124 if (!syncpoint.reserved) {
114 ASSERT(false); 125 ASSERT(false);
115 return NvFence{}; 126 return NvFence{};
116 } 127 }
117 128
118 return {.id = static_cast<s32>(id), .value = syncpoints.at(id).counter_max}; 129 return {
130 .id = static_cast<s32>(id),
131 .value = syncpoint.counter_max,
132 };
119} 133}
120 134
121} // namespace Service::Nvidia::NvCore 135} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
index b76ef9032..4f2cefae5 100644
--- a/src/core/hle/service/nvdrv/core/syncpoint_manager.h
+++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
@@ -44,7 +44,7 @@ public:
44 /** 44 /**
45 * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints 45 * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints
46 */ 46 */
47 bool IsSyncpointAllocated(u32 id); 47 bool IsSyncpointAllocated(u32 id) const;
48 48
49 /** 49 /**
50 * @brief Finds a free syncpoint and reserves it 50 * @brief Finds a free syncpoint and reserves it
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 9f4c7c99a..6fc8565c0 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -55,48 +55,40 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
55Module::Module(Core::System& system) 55Module::Module(Core::System& system)
56 : container{system.Host1x()}, service_context{system, "nvdrv"}, events_interface{*this} { 56 : container{system.Host1x()}, service_context{system, "nvdrv"}, events_interface{*this} {
57 builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { 57 builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) {
58 std::shared_ptr<Devices::nvdevice> device = 58 auto device = std::make_shared<Devices::nvhost_as_gpu>(system, *this, container);
59 std::make_shared<Devices::nvhost_as_gpu>(system, *this, container); 59 return open_files.emplace(fd, std::move(device)).first;
60 return open_files.emplace(fd, device).first;
61 }; 60 };
62 builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) { 61 builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) {
63 std::shared_ptr<Devices::nvdevice> device = 62 auto device = std::make_shared<Devices::nvhost_gpu>(system, events_interface, container);
64 std::make_shared<Devices::nvhost_gpu>(system, events_interface, container); 63 return open_files.emplace(fd, std::move(device)).first;
65 return open_files.emplace(fd, device).first;
66 }; 64 };
67 builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) { 65 builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) {
68 std::shared_ptr<Devices::nvdevice> device = 66 auto device = std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface);
69 std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface); 67 return open_files.emplace(fd, std::move(device)).first;
70 return open_files.emplace(fd, device).first;
71 }; 68 };
72 builders["/dev/nvmap"] = [this, &system](DeviceFD fd) { 69 builders["/dev/nvmap"] = [this, &system](DeviceFD fd) {
73 std::shared_ptr<Devices::nvdevice> device = 70 auto device = std::make_shared<Devices::nvmap>(system, container);
74 std::make_shared<Devices::nvmap>(system, container); 71 return open_files.emplace(fd, std::move(device)).first;
75 return open_files.emplace(fd, device).first;
76 }; 72 };
77 builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) { 73 builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) {
78 std::shared_ptr<Devices::nvdevice> device = 74 auto device = std::make_shared<Devices::nvdisp_disp0>(system, container);
79 std::make_shared<Devices::nvdisp_disp0>(system, container); 75 return open_files.emplace(fd, std::move(device)).first;
80 return open_files.emplace(fd, device).first;
81 }; 76 };
82 builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) { 77 builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) {
83 std::shared_ptr<Devices::nvdevice> device = 78 auto device = std::make_shared<Devices::nvhost_ctrl>(system, events_interface, container);
84 std::make_shared<Devices::nvhost_ctrl>(system, events_interface, container); 79 return open_files.emplace(fd, std::move(device)).first;
85 return open_files.emplace(fd, device).first;
86 }; 80 };
87 builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) { 81 builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) {
88 std::shared_ptr<Devices::nvdevice> device = 82 auto device = std::make_shared<Devices::nvhost_nvdec>(system, container);
89 std::make_shared<Devices::nvhost_nvdec>(system, container); 83 return open_files.emplace(fd, std::move(device)).first;
90 return open_files.emplace(fd, device).first;
91 }; 84 };
92 builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) { 85 builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) {
93 std::shared_ptr<Devices::nvdevice> device = std::make_shared<Devices::nvhost_nvjpg>(system); 86 auto device = std::make_shared<Devices::nvhost_nvjpg>(system);
94 return open_files.emplace(fd, device).first; 87 return open_files.emplace(fd, std::move(device)).first;
95 }; 88 };
96 builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) { 89 builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) {
97 std::shared_ptr<Devices::nvdevice> device = 90 auto device = std::make_shared<Devices::nvhost_vic>(system, container);
98 std::make_shared<Devices::nvhost_vic>(system, container); 91 return open_files.emplace(fd, std::move(device)).first;
99 return open_files.emplace(fd, device).first;
100 }; 92 };
101} 93}
102 94
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
index 6d2c92a2c..152bb5bdf 100644
--- a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
@@ -39,7 +39,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco
39 return Status::NoError; 39 return Status::NoError;
40} 40}
41 41
42Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) { 42Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, const Fence& release_fence) {
43 std::scoped_lock lock{mutex}; 43 std::scoped_lock lock{mutex};
44 44
45 if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); 45 if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h
index 69046233d..a5c655d9e 100644
--- a/src/core/hle/service/nvflinger/buffer_item_consumer.h
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h
@@ -22,7 +22,7 @@ public:
22 explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer); 22 explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
23 Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, 23 Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
24 bool wait_for_fence = true); 24 bool wait_for_fence = true);
25 Status ReleaseBuffer(const BufferItem& item, Fence& release_fence); 25 Status ReleaseBuffer(const BufferItem& item, const Fence& release_fence);
26}; 26};
27 27
28} // namespace Service::android 28} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
index 1ce67c771..0767e548d 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -169,7 +169,7 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
169 return Status::NoInit; 169 return Status::NoInit;
170 } 170 }
171 171
172 core->consumer_listener = consumer_listener; 172 core->consumer_listener = std::move(consumer_listener);
173 core->consumer_controlled_by_app = controlled_by_app; 173 core->consumer_controlled_by_app = controlled_by_app;
174 174
175 return Status::NoError; 175 return Status::NoError;
diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp
index 5b9995854..982531e2d 100644
--- a/src/core/hle/service/nvflinger/consumer_base.cpp
+++ b/src/core/hle/service/nvflinger/consumer_base.cpp
@@ -83,7 +83,7 @@ Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseco
83} 83}
84 84
85Status ConsumerBase::AddReleaseFenceLocked(s32 slot, 85Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
86 const std::shared_ptr<GraphicBuffer> graphic_buffer, 86 const std::shared_ptr<GraphicBuffer>& graphic_buffer,
87 const Fence& fence) { 87 const Fence& fence) {
88 LOG_DEBUG(Service_NVFlinger, "slot={}", slot); 88 LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
89 89
@@ -100,7 +100,7 @@ Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
100} 100}
101 101
102Status ConsumerBase::ReleaseBufferLocked(s32 slot, 102Status ConsumerBase::ReleaseBufferLocked(s32 slot,
103 const std::shared_ptr<GraphicBuffer> graphic_buffer) { 103 const std::shared_ptr<GraphicBuffer>& graphic_buffer) {
104 // If consumer no longer tracks this graphic_buffer (we received a new 104 // If consumer no longer tracks this graphic_buffer (we received a new
105 // buffer on the same slot), the buffer producer is definitely no longer 105 // buffer on the same slot), the buffer producer is definitely no longer
106 // tracking it. 106 // tracking it.
@@ -121,7 +121,7 @@ Status ConsumerBase::ReleaseBufferLocked(s32 slot,
121} 121}
122 122
123bool ConsumerBase::StillTracking(s32 slot, 123bool ConsumerBase::StillTracking(s32 slot,
124 const std::shared_ptr<GraphicBuffer> graphic_buffer) const { 124 const std::shared_ptr<GraphicBuffer>& graphic_buffer) const {
125 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { 125 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
126 return false; 126 return false;
127 } 127 }
diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h
index 90ba07f45..9a8a5f6bb 100644
--- a/src/core/hle/service/nvflinger/consumer_base.h
+++ b/src/core/hle/service/nvflinger/consumer_base.h
@@ -27,18 +27,18 @@ public:
27 27
28protected: 28protected:
29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); 29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
30 virtual ~ConsumerBase(); 30 ~ConsumerBase() override;
31 31
32 virtual void OnFrameAvailable(const BufferItem& item) override; 32 void OnFrameAvailable(const BufferItem& item) override;
33 virtual void OnFrameReplaced(const BufferItem& item) override; 33 void OnFrameReplaced(const BufferItem& item) override;
34 virtual void OnBuffersReleased() override; 34 void OnBuffersReleased() override;
35 virtual void OnSidebandStreamChanged() override; 35 void OnSidebandStreamChanged() override;
36 36
37 void FreeBufferLocked(s32 slot_index); 37 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); 38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer); 39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer);
40 bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const; 40 bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer) const;
41 Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer, 41 Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer,
42 const Fence& fence); 42 const Fence& fence);
43 43
44 struct Slot final { 44 struct Slot final {
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index c3af12c90..d1cbadde4 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -307,8 +307,7 @@ void NVFlinger::Compose() {
307 307
308 swap_interval = buffer.swap_interval; 308 swap_interval = buffer.swap_interval;
309 309
310 auto fence = android::Fence::NoFence(); 310 layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence());
311 layer.GetConsumer().ReleaseBuffer(buffer, fence);
312 } 311 }
313} 312}
314 313
diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h
index 1c4d5db0e..6bf8aaf1e 100644
--- a/src/core/hle/service/nvflinger/producer_listener.h
+++ b/src/core/hle/service/nvflinger/producer_listener.h
@@ -10,6 +10,7 @@ namespace Service::android {
10 10
11class IProducerListener { 11class IProducerListener {
12public: 12public:
13 virtual ~IProducerListener() = default;
13 virtual void OnBufferReleased() = 0; 14 virtual void OnBufferReleased() = 0;
14}; 15};
15 16
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index cc6f0ffc0..193127d0a 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -55,7 +55,11 @@ if (ENABLE_SDL2)
55 drivers/sdl_driver.cpp 55 drivers/sdl_driver.cpp
56 drivers/sdl_driver.h 56 drivers/sdl_driver.h
57 ) 57 )
58 target_link_libraries(input_common PRIVATE SDL2) 58 if (YUZU_USE_EXTERNAL_SDL2)
59 target_link_libraries(input_common PRIVATE SDL2-static)
60 else()
61 target_link_libraries(input_common PRIVATE SDL2)
62 endif()
59 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 63 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
60endif() 64endif()
61 65
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 45ce588f0..8de86b61e 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -361,6 +361,12 @@ void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
361 } 361 }
362} 362}
363 363
364void SDLDriver::PumpEvents() const {
365 if (initialized) {
366 SDL_PumpEvents();
367 }
368}
369
364void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { 370void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
365 switch (event.type) { 371 switch (event.type) {
366 case SDL_JOYBUTTONUP: { 372 case SDL_JOYBUTTONUP: {
@@ -451,14 +457,6 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
451 457
452 initialized = true; 458 initialized = true;
453 if (start_thread) { 459 if (start_thread) {
454 poll_thread = std::thread([this] {
455 Common::SetCurrentThreadName("SDL_MainLoop");
456 using namespace std::chrono_literals;
457 while (initialized) {
458 SDL_PumpEvents();
459 std::this_thread::sleep_for(1ms);
460 }
461 });
462 vibration_thread = std::thread([this] { 460 vibration_thread = std::thread([this] {
463 Common::SetCurrentThreadName("SDL_Vibration"); 461 Common::SetCurrentThreadName("SDL_Vibration");
464 using namespace std::chrono_literals; 462 using namespace std::chrono_literals;
@@ -481,7 +479,6 @@ SDLDriver::~SDLDriver() {
481 479
482 initialized = false; 480 initialized = false;
483 if (start_thread) { 481 if (start_thread) {
484 poll_thread.join();
485 vibration_thread.join(); 482 vibration_thread.join();
486 SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); 483 SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
487 } 484 }
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index d1b4471cf..366bcc496 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -36,6 +36,8 @@ public:
36 /// Unregisters SDL device factories and shut them down. 36 /// Unregisters SDL device factories and shut them down.
37 ~SDLDriver() override; 37 ~SDLDriver() override;
38 38
39 void PumpEvents() const;
40
39 /// Handle SDL_Events for joysticks from SDL_PollEvent 41 /// Handle SDL_Events for joysticks from SDL_PollEvent
40 void HandleGameControllerEvent(const SDL_Event& event); 42 void HandleGameControllerEvent(const SDL_Event& event);
41 43
@@ -128,7 +130,6 @@ private:
128 bool start_thread = false; 130 bool start_thread = false;
129 std::atomic<bool> initialized = false; 131 std::atomic<bool> initialized = false;
130 132
131 std::thread poll_thread;
132 std::thread vibration_thread; 133 std::thread vibration_thread;
133}; 134};
134} // namespace InputCommon 135} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index baeed2e02..942a13535 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -318,6 +318,12 @@ struct InputSubsystem::Impl {
318#endif 318#endif
319 } 319 }
320 320
321 void PumpEvents() const {
322#ifdef HAVE_SDL2
323 sdl->PumpEvents();
324#endif
325 }
326
321 void RegisterInput(const MappingData& data) { 327 void RegisterInput(const MappingData& data) {
322 mapping_factory->RegisterInput(data); 328 mapping_factory->RegisterInput(data);
323 } 329 }
@@ -466,6 +472,10 @@ void InputSubsystem::StopMapping() const {
466 impl->mapping_factory->StopMapping(); 472 impl->mapping_factory->StopMapping();
467} 473}
468 474
475void InputSubsystem::PumpEvents() const {
476 impl->PumpEvents();
477}
478
469std::string GenerateKeyboardParam(int key_code) { 479std::string GenerateKeyboardParam(int key_code) {
470 Common::ParamPackage param; 480 Common::ParamPackage param;
471 param.Set("engine", "keyboard"); 481 param.Set("engine", "keyboard");
diff --git a/src/input_common/main.h b/src/input_common/main.h
index ced252383..6218c37f6 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -147,6 +147,9 @@ public:
147 /// Stop polling from all backends. 147 /// Stop polling from all backends.
148 void StopMapping() const; 148 void StopMapping() const;
149 149
150 /// Signals SDL driver for new input events
151 void PumpEvents() const;
152
150private: 153private:
151 struct Impl; 154 struct Impl;
152 std::unique_ptr<Impl> impl; 155 std::unique_ptr<Impl> impl;
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp
index 28aa85f32..e4f8331ab 100644
--- a/src/video_core/engines/engine_upload.cpp
+++ b/src/video_core/engines/engine_upload.cpp
@@ -49,10 +49,9 @@ void State::ProcessData(std::span<const u8> read_buffer) {
49 if (regs.line_count == 1) { 49 if (regs.line_count == 1) {
50 rasterizer->AccelerateInlineToMemory(address, copy_size, read_buffer); 50 rasterizer->AccelerateInlineToMemory(address, copy_size, read_buffer);
51 } else { 51 } else {
52 for (u32 line = 0; line < regs.line_count; ++line) { 52 for (size_t line = 0; line < regs.line_count; ++line) {
53 const GPUVAddr dest_line = address + static_cast<size_t>(line) * regs.dest.pitch; 53 const GPUVAddr dest_line = address + line * regs.dest.pitch;
54 std::span<const u8> buffer(read_buffer.data() + 54 std::span<const u8> buffer(read_buffer.data() + line * regs.line_length_in,
55 static_cast<size_t>(line) * regs.line_length_in,
56 regs.line_length_in); 55 regs.line_length_in);
57 rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer); 56 rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer);
58 } 57 }
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h
index f08f6e36a..94fafd9dc 100644
--- a/src/video_core/engines/engine_upload.h
+++ b/src/video_core/engines/engine_upload.h
@@ -39,7 +39,7 @@ struct Registers {
39 u32 y; 39 u32 y;
40 40
41 GPUVAddr Address() const { 41 GPUVAddr Address() const {
42 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); 42 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
43 } 43 }
44 44
45 u32 BlockWidth() const { 45 u32 BlockWidth() const {
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 24b518cb5..100b21bac 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -97,7 +97,7 @@ public:
97 u32 addr_lower; 97 u32 addr_lower;
98 98
99 [[nodiscard]] constexpr GPUVAddr Address() const noexcept { 99 [[nodiscard]] constexpr GPUVAddr Address() const noexcept {
100 return (static_cast<GPUVAddr>(addr_upper) << 32) | static_cast<GPUVAddr>(addr_lower); 100 return (GPUVAddr{addr_upper} << 32) | GPUVAddr{addr_lower};
101 } 101 }
102 }; 102 };
103 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size"); 103 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size");
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 7c50bdbe0..e5c622155 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -50,11 +50,11 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun
50 u32 methods_pending) { 50 u32 methods_pending) {
51 switch (method) { 51 switch (method) {
52 case KEPLER_COMPUTE_REG_INDEX(data_upload): 52 case KEPLER_COMPUTE_REG_INDEX(data_upload):
53 upload_state.ProcessData(base_start, static_cast<size_t>(amount)); 53 upload_state.ProcessData(base_start, amount);
54 return; 54 return;
55 default: 55 default:
56 for (std::size_t i = 0; i < amount; i++) { 56 for (u32 i = 0; i < amount; i++) {
57 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 57 CallMethod(method, base_start[i], methods_pending - i <= 1);
58 } 58 }
59 break; 59 break;
60 } 60 }
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index aab309ecc..e154e3f06 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -68,7 +68,7 @@ public:
68 struct { 68 struct {
69 u32 address; 69 u32 address;
70 GPUVAddr Address() const { 70 GPUVAddr Address() const {
71 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address) << 8)); 71 return GPUVAddr{address} << 8;
72 } 72 }
73 } launch_desc_loc; 73 } launch_desc_loc;
74 74
@@ -83,8 +83,7 @@ public:
83 u32 address_low; 83 u32 address_low;
84 u32 limit; 84 u32 limit;
85 GPUVAddr Address() const { 85 GPUVAddr Address() const {
86 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 86 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
87 address_low);
88 } 87 }
89 } tsc; 88 } tsc;
90 89
@@ -95,8 +94,7 @@ public:
95 u32 address_low; 94 u32 address_low;
96 u32 limit; 95 u32 limit;
97 GPUVAddr Address() const { 96 GPUVAddr Address() const {
98 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 97 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
99 address_low);
100 } 98 }
101 } tic; 99 } tic;
102 100
@@ -106,8 +104,7 @@ public:
106 u32 address_high; 104 u32 address_high;
107 u32 address_low; 105 u32 address_low;
108 GPUVAddr Address() const { 106 GPUVAddr Address() const {
109 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 107 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
110 address_low);
111 } 108 }
112 } code_loc; 109 } code_loc;
113 110
@@ -162,8 +159,7 @@ public:
162 BitField<15, 17, u32> size; 159 BitField<15, 17, u32> size;
163 }; 160 };
164 GPUVAddr Address() const { 161 GPUVAddr Address() const {
165 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high.Value()) << 32) | 162 return (GPUVAddr{address_high.Value()} << 32) | GPUVAddr{address_low};
166 address_low);
167 } 163 }
168 }; 164 };
169 std::array<ConstBufferConfig, NumConstBuffers> const_buffer_config; 165 std::array<ConstBufferConfig, NumConstBuffers> const_buffer_config;
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index a3fbab1e5..08045d1cf 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -42,11 +42,11 @@ void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount
42 u32 methods_pending) { 42 u32 methods_pending) {
43 switch (method) { 43 switch (method) {
44 case KEPLERMEMORY_REG_INDEX(data): 44 case KEPLERMEMORY_REG_INDEX(data):
45 upload_state.ProcessData(base_start, static_cast<size_t>(amount)); 45 upload_state.ProcessData(base_start, amount);
46 return; 46 return;
47 default: 47 default:
48 for (std::size_t i = 0; i < amount; i++) { 48 for (u32 i = 0; i < amount; i++) {
49 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 49 CallMethod(method, base_start[i], methods_pending - i <= 1);
50 } 50 }
51 break; 51 break;
52 } 52 }
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 212427b8b..55462752c 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -370,11 +370,11 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
370 ProcessCBMultiData(base_start, amount); 370 ProcessCBMultiData(base_start, amount);
371 break; 371 break;
372 case MAXWELL3D_REG_INDEX(inline_data): 372 case MAXWELL3D_REG_INDEX(inline_data):
373 upload_state.ProcessData(base_start, static_cast<size_t>(amount)); 373 upload_state.ProcessData(base_start, amount);
374 return; 374 return;
375 default: 375 default:
376 for (std::size_t i = 0; i < amount; i++) { 376 for (u32 i = 0; i < amount; i++) {
377 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 377 CallMethod(method, base_start[i], methods_pending - i <= 1);
378 } 378 }
379 break; 379 break;
380 } 380 }
@@ -652,12 +652,12 @@ void Maxwell3D::ProcessDeferredDraw() {
652 return; 652 return;
653 } 653 }
654 654
655 u32 method_count = static_cast<u32>(deferred_draw_method.size()); 655 const auto method_count = deferred_draw_method.size();
656 u32 instance_count = 1; 656 u32 instance_count = 1;
657 u32 vertex_buffer_count = 0; 657 u32 vertex_buffer_count = 0;
658 u32 index_buffer_count = 0; 658 u32 index_buffer_count = 0;
659 for (u32 index = 0; index < method_count; ++index) { 659 for (size_t index = 0; index < method_count; ++index) {
660 u32 method = deferred_draw_method[index]; 660 const u32 method = deferred_draw_method[index];
661 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) { 661 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
662 instance_count = ++vertex_buffer_count; 662 instance_count = ++vertex_buffer_count;
663 } else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) { 663 } else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 84c497ebd..deba292a5 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -96,8 +96,7 @@ public:
96 u32 type; 96 u32 type;
97 97
98 GPUVAddr Address() const { 98 GPUVAddr Address() const {
99 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 99 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
100 address_low);
101 } 100 }
102 }; 101 };
103 102
@@ -106,8 +105,7 @@ public:
106 u32 address_low; 105 u32 address_low;
107 106
108 GPUVAddr Address() const { 107 GPUVAddr Address() const {
109 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 108 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
110 address_low);
111 } 109 }
112 }; 110 };
113 111
@@ -124,8 +122,7 @@ public:
124 Mode mode; 122 Mode mode;
125 123
126 GPUVAddr Address() const { 124 GPUVAddr Address() const {
127 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(offset_high) << 32) | 125 return (GPUVAddr{offset_high} << 32) | GPUVAddr{offset_low};
128 offset_low);
129 } 126 }
130 }; 127 };
131 128
@@ -187,7 +184,7 @@ public:
187 default: 184 default:
188 // Thresholds begin at 0x10 (1 << 4) 185 // Thresholds begin at 0x10 (1 << 4)
189 // Threshold is in the range 0x1 to 0x13 186 // Threshold is in the range 0x1 to 0x13
190 return 1 << (4 + threshold.Value() - 1); 187 return 1U << (4 + threshold.Value() - 1);
191 } 188 }
192 } 189 }
193 }; 190 };
@@ -468,8 +465,7 @@ public:
468 INSERT_PADDING_BYTES_NOINIT(0xC); 465 INSERT_PADDING_BYTES_NOINIT(0xC);
469 466
470 GPUVAddr Address() const { 467 GPUVAddr Address() const {
471 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 468 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
472 address_low);
473 } 469 }
474 }; 470 };
475 static_assert(sizeof(Buffer) == 0x20); 471 static_assert(sizeof(Buffer) == 0x20);
@@ -511,12 +507,11 @@ public:
511 u32 default_size_per_warp; 507 u32 default_size_per_warp;
512 508
513 GPUVAddr Address() const { 509 GPUVAddr Address() const {
514 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 510 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
515 address_low);
516 } 511 }
517 512
518 u64 Size() const { 513 u64 Size() const {
519 return (static_cast<u64>(size_high) << 32) | size_low; 514 return (u64{size_high} << 32) | u64{size_low};
520 } 515 }
521 }; 516 };
522 517
@@ -538,13 +533,11 @@ public:
538 u32 storage_limit_address_low; 533 u32 storage_limit_address_low;
539 534
540 GPUVAddr StorageAddress() const { 535 GPUVAddr StorageAddress() const {
541 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(storage_address_high) << 32) | 536 return (GPUVAddr{storage_address_high} << 32) | GPUVAddr{storage_address_low};
542 storage_address_low);
543 } 537 }
544 GPUVAddr StorageLimitAddress() const { 538 GPUVAddr StorageLimitAddress() const {
545 return static_cast<GPUVAddr>( 539 return (GPUVAddr{storage_limit_address_high} << 32) |
546 (static_cast<GPUVAddr>(storage_limit_address_high) << 32) | 540 GPUVAddr{storage_limit_address_low};
547 storage_limit_address_low);
548 } 541 }
549 }; 542 };
550 543
@@ -829,11 +822,11 @@ public:
829 struct CompressionThresholdSamples { 822 struct CompressionThresholdSamples {
830 u32 samples; 823 u32 samples;
831 824
832 u32 Samples() { 825 u32 Samples() const {
833 if (samples == 0) { 826 if (samples == 0) {
834 return 0; 827 return 0;
835 } 828 }
836 return 1 << (samples - 1); 829 return 1U << (samples - 1);
837 } 830 }
838 }; 831 };
839 832
@@ -1138,8 +1131,7 @@ public:
1138 INSERT_PADDING_BYTES_NOINIT(0x18); 1131 INSERT_PADDING_BYTES_NOINIT(0x18);
1139 1132
1140 GPUVAddr Address() const { 1133 GPUVAddr Address() const {
1141 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1134 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1142 address_low);
1143 } 1135 }
1144 }; 1136 };
1145 static_assert(sizeof(RenderTargetConfig) == 0x40); 1137 static_assert(sizeof(RenderTargetConfig) == 0x40);
@@ -1482,8 +1474,7 @@ public:
1482 u32 address_low; 1474 u32 address_low;
1483 1475
1484 GPUVAddr Address() const { 1476 GPUVAddr Address() const {
1485 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1477 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1486 address_low);
1487 } 1478 }
1488 }; 1479 };
1489 1480
@@ -1533,8 +1524,7 @@ public:
1533 u32 address_low; 1524 u32 address_low;
1534 1525
1535 GPUVAddr Address() const { 1526 GPUVAddr Address() const {
1536 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1527 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1537 address_low);
1538 } 1528 }
1539 }; 1529 };
1540 1530
@@ -1561,8 +1551,7 @@ public:
1561 u32 array_pitch; 1551 u32 array_pitch;
1562 1552
1563 GPUVAddr Address() const { 1553 GPUVAddr Address() const {
1564 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1554 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1565 address_low);
1566 } 1555 }
1567 }; 1556 };
1568 1557
@@ -1910,8 +1899,7 @@ public:
1910 Mode mode; 1899 Mode mode;
1911 1900
1912 GPUVAddr Address() const { 1901 GPUVAddr Address() const {
1913 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1902 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1914 address_low);
1915 } 1903 }
1916 }; 1904 };
1917 1905
@@ -1921,8 +1909,7 @@ public:
1921 u32 limit; 1909 u32 limit;
1922 1910
1923 GPUVAddr Address() const { 1911 GPUVAddr Address() const {
1924 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1912 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1925 address_low);
1926 } 1913 }
1927 }; 1914 };
1928 1915
@@ -1932,8 +1919,7 @@ public:
1932 u32 limit; 1919 u32 limit;
1933 1920
1934 GPUVAddr Address() const { 1921 GPUVAddr Address() const {
1935 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1922 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1936 address_low);
1937 } 1923 }
1938 }; 1924 };
1939 1925
@@ -1981,8 +1967,7 @@ public:
1981 u32 address_low; 1967 u32 address_low;
1982 1968
1983 GPUVAddr Address() const { 1969 GPUVAddr Address() const {
1984 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1970 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1985 address_low);
1986 } 1971 }
1987 }; 1972 };
1988 1973
@@ -2027,8 +2012,7 @@ public:
2027 u32 address_low; 2012 u32 address_low;
2028 2013
2029 GPUVAddr Address() const { 2014 GPUVAddr Address() const {
2030 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2015 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2031 address_low);
2032 } 2016 }
2033 }; 2017 };
2034 2018
@@ -2224,19 +2208,16 @@ public:
2224 } 2208 }
2225 2209
2226 GPUVAddr StartAddress() const { 2210 GPUVAddr StartAddress() const {
2227 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(start_addr_high) << 32) | 2211 return (GPUVAddr{start_addr_high} << 32) | GPUVAddr{start_addr_low};
2228 start_addr_low);
2229 } 2212 }
2230 2213
2231 GPUVAddr EndAddress() const { 2214 GPUVAddr EndAddress() const {
2232 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_addr_high) << 32) | 2215 return (GPUVAddr{limit_addr_high} << 32) | GPUVAddr{limit_addr_low};
2233 limit_addr_low);
2234 } 2216 }
2235 2217
2236 /// Adjust the index buffer offset so it points to the first desired index. 2218 /// Adjust the index buffer offset so it points to the first desired index.
2237 GPUVAddr IndexStart() const { 2219 GPUVAddr IndexStart() const {
2238 return StartAddress() + 2220 return StartAddress() + size_t{first} * size_t{FormatSizeInBytes()};
2239 static_cast<size_t>(first) * static_cast<size_t>(FormatSizeInBytes());
2240 } 2221 }
2241 }; 2222 };
2242 2223
@@ -2464,8 +2445,7 @@ public:
2464 } query; 2445 } query;
2465 2446
2466 GPUVAddr Address() const { 2447 GPUVAddr Address() const {
2467 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2448 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2468 address_low);
2469 } 2449 }
2470 }; 2450 };
2471 2451
@@ -2479,8 +2459,7 @@ public:
2479 u32 frequency; 2459 u32 frequency;
2480 2460
2481 GPUVAddr Address() const { 2461 GPUVAddr Address() const {
2482 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2462 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2483 address_low);
2484 } 2463 }
2485 2464
2486 bool IsEnabled() const { 2465 bool IsEnabled() const {
@@ -2494,8 +2473,7 @@ public:
2494 u32 address_low; 2473 u32 address_low;
2495 2474
2496 GPUVAddr Address() const { 2475 GPUVAddr Address() const {
2497 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2476 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2498 address_low);
2499 } 2477 }
2500 }; 2478 };
2501 static_assert(sizeof(VertexStreamLimit) == 0x8); 2479 static_assert(sizeof(VertexStreamLimit) == 0x8);
@@ -2543,8 +2521,7 @@ public:
2543 std::array<u32, NumCBData> buffer; 2521 std::array<u32, NumCBData> buffer;
2544 2522
2545 GPUVAddr Address() const { 2523 GPUVAddr Address() const {
2546 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2524 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2547 address_low);
2548 } 2525 }
2549 }; 2526 };
2550 2527
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 334429514..a189e60ae 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -41,8 +41,8 @@ void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call)
41 41
42void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount, 42void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
43 u32 methods_pending) { 43 u32 methods_pending) {
44 for (size_t i = 0; i < amount; ++i) { 44 for (u32 i = 0; i < amount; ++i) {
45 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 45 CallMethod(method, base_start[i], methods_pending - i <= 1);
46 } 46 }
47} 47}
48 48
@@ -94,14 +94,14 @@ void MaxwellDMA::Launch() {
94 reinterpret_cast<u8*>(tmp_buffer.data()), 94 reinterpret_cast<u8*>(tmp_buffer.data()),
95 regs.line_length_in * sizeof(u32)); 95 regs.line_length_in * sizeof(u32));
96 } else { 96 } else {
97 auto convert_linear_2_blocklinear_addr = [](u64 address) { 97 const auto convert_linear_2_blocklinear_addr = [](u64 address) {
98 return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) | 98 return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) |
99 ((address & 0x180) >> 1) | ((address & 0x20) << 3); 99 ((address & 0x180) >> 1) | ((address & 0x20) << 3);
100 }; 100 };
101 auto src_kind = memory_manager.GetPageKind(regs.offset_in); 101 const auto src_kind = memory_manager.GetPageKind(regs.offset_in);
102 auto dst_kind = memory_manager.GetPageKind(regs.offset_out); 102 const auto dst_kind = memory_manager.GetPageKind(regs.offset_out);
103 const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind)); 103 const bool is_src_pitch = IsPitchKind(src_kind);
104 const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind)); 104 const bool is_dst_pitch = IsPitchKind(dst_kind);
105 if (!is_src_pitch && is_dst_pitch) { 105 if (!is_src_pitch && is_dst_pitch) {
106 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); 106 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
107 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); 107 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp
index c308ba3fc..7718a09b3 100644
--- a/src/video_core/engines/puller.cpp
+++ b/src/video_core/engines/puller.cpp
@@ -31,7 +31,7 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
31 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, 31 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
32 method_call.argument); 32 method_call.argument);
33 const auto engine_id = static_cast<EngineID>(method_call.argument); 33 const auto engine_id = static_cast<EngineID>(method_call.argument);
34 bound_engines[method_call.subchannel] = static_cast<EngineID>(engine_id); 34 bound_engines[method_call.subchannel] = engine_id;
35 switch (engine_id) { 35 switch (engine_id) {
36 case EngineID::FERMI_TWOD_A: 36 case EngineID::FERMI_TWOD_A:
37 dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel); 37 dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel);
@@ -285,12 +285,12 @@ void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start,
285 if (ExecuteMethodOnEngine(method)) { 285 if (ExecuteMethodOnEngine(method)) {
286 CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); 286 CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending);
287 } else { 287 } else {
288 for (std::size_t i = 0; i < amount; i++) { 288 for (u32 i = 0; i < amount; i++) {
289 CallPullerMethod(MethodCall{ 289 CallPullerMethod(MethodCall{
290 method, 290 method,
291 base_start[i], 291 base_start[i],
292 subchannel, 292 subchannel,
293 methods_pending - static_cast<u32>(i), 293 methods_pending - i,
294 }); 294 });
295 } 295 }
296 } 296 }
diff --git a/src/video_core/host1x/syncpoint_manager.cpp b/src/video_core/host1x/syncpoint_manager.cpp
index a44fc83d3..8f23ce527 100644
--- a/src/video_core/host1x/syncpoint_manager.cpp
+++ b/src/video_core/host1x/syncpoint_manager.cpp
@@ -34,7 +34,7 @@ SyncpointManager::ActionHandle SyncpointManager::RegisterAction(
34} 34}
35 35
36void SyncpointManager::DeregisterAction(std::list<RegisteredAction>& action_storage, 36void SyncpointManager::DeregisterAction(std::list<RegisteredAction>& action_storage,
37 ActionHandle& handle) { 37 const ActionHandle& handle) {
38 std::unique_lock lk(guard); 38 std::unique_lock lk(guard);
39 39
40 // We want to ensure the iterator still exists prior to erasing it 40 // We want to ensure the iterator still exists prior to erasing it
@@ -49,11 +49,11 @@ void SyncpointManager::DeregisterAction(std::list<RegisteredAction>& action_stor
49 } 49 }
50} 50}
51 51
52void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle) { 52void SyncpointManager::DeregisterGuestAction(u32 syncpoint_id, const ActionHandle& handle) {
53 DeregisterAction(guest_action_storage[syncpoint_id], handle); 53 DeregisterAction(guest_action_storage[syncpoint_id], handle);
54} 54}
55 55
56void SyncpointManager::DeregisterHostAction(u32 syncpoint_id, ActionHandle& handle) { 56void SyncpointManager::DeregisterHostAction(u32 syncpoint_id, const ActionHandle& handle) {
57 DeregisterAction(host_action_storage[syncpoint_id], handle); 57 DeregisterAction(host_action_storage[syncpoint_id], handle);
58} 58}
59 59
diff --git a/src/video_core/host1x/syncpoint_manager.h b/src/video_core/host1x/syncpoint_manager.h
index 50a264e23..847ed20c8 100644
--- a/src/video_core/host1x/syncpoint_manager.h
+++ b/src/video_core/host1x/syncpoint_manager.h
@@ -36,21 +36,19 @@ public:
36 36
37 template <typename Func> 37 template <typename Func>
38 ActionHandle RegisterGuestAction(u32 syncpoint_id, u32 expected_value, Func&& action) { 38 ActionHandle RegisterGuestAction(u32 syncpoint_id, u32 expected_value, Func&& action) {
39 std::function<void()> func(action);
40 return RegisterAction(syncpoints_guest[syncpoint_id], guest_action_storage[syncpoint_id], 39 return RegisterAction(syncpoints_guest[syncpoint_id], guest_action_storage[syncpoint_id],
41 expected_value, std::move(func)); 40 expected_value, std::move(action));
42 } 41 }
43 42
44 template <typename Func> 43 template <typename Func>
45 ActionHandle RegisterHostAction(u32 syncpoint_id, u32 expected_value, Func&& action) { 44 ActionHandle RegisterHostAction(u32 syncpoint_id, u32 expected_value, Func&& action) {
46 std::function<void()> func(action);
47 return RegisterAction(syncpoints_host[syncpoint_id], host_action_storage[syncpoint_id], 45 return RegisterAction(syncpoints_host[syncpoint_id], host_action_storage[syncpoint_id],
48 expected_value, std::move(func)); 46 expected_value, std::move(action));
49 } 47 }
50 48
51 void DeregisterGuestAction(u32 syncpoint_id, ActionHandle& handle); 49 void DeregisterGuestAction(u32 syncpoint_id, const ActionHandle& handle);
52 50
53 void DeregisterHostAction(u32 syncpoint_id, ActionHandle& handle); 51 void DeregisterHostAction(u32 syncpoint_id, const ActionHandle& handle);
54 52
55 void IncrementGuest(u32 syncpoint_id); 53 void IncrementGuest(u32 syncpoint_id);
56 54
@@ -76,7 +74,7 @@ private:
76 std::list<RegisteredAction>& action_storage, u32 expected_value, 74 std::list<RegisteredAction>& action_storage, u32 expected_value,
77 std::function<void()>&& action); 75 std::function<void()>&& action);
78 76
79 void DeregisterAction(std::list<RegisteredAction>& action_storage, ActionHandle& handle); 77 void DeregisterAction(std::list<RegisteredAction>& action_storage, const ActionHandle& handle);
80 78
81 void Wait(std::atomic<u32>& syncpoint, std::condition_variable& wait_cv, u32 expected_value); 79 void Wait(std::atomic<u32>& syncpoint, std::condition_variable& wait_cv, u32 expected_value);
82 80
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index b618e1a25..1a76d4178 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -214,23 +214,16 @@ PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format)
214} 214}
215 215
216SurfaceType GetFormatType(PixelFormat pixel_format) { 216SurfaceType GetFormatType(PixelFormat pixel_format) {
217 if (static_cast<std::size_t>(pixel_format) < 217 if (pixel_format < PixelFormat::MaxColorFormat) {
218 static_cast<std::size_t>(PixelFormat::MaxColorFormat)) {
219 return SurfaceType::ColorTexture; 218 return SurfaceType::ColorTexture;
220 } 219 }
221 220 if (pixel_format < PixelFormat::MaxDepthFormat) {
222 if (static_cast<std::size_t>(pixel_format) <
223 static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) {
224 return SurfaceType::Depth; 221 return SurfaceType::Depth;
225 } 222 }
226 223 if (pixel_format < PixelFormat::MaxStencilFormat) {
227 if (static_cast<std::size_t>(pixel_format) <
228 static_cast<std::size_t>(PixelFormat::MaxStencilFormat)) {
229 return SurfaceType::Stencil; 224 return SurfaceType::Stencil;
230 } 225 }
231 226 if (pixel_format < PixelFormat::MaxDepthStencilFormat) {
232 if (static_cast<std::size_t>(pixel_format) <
233 static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
234 return SurfaceType::DepthStencil; 227 return SurfaceType::DepthStencil;
235 } 228 }
236 229
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 0aa109dd3..060de0259 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -387,7 +387,11 @@ if (YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6)
387endif() 387endif()
388 388
389if (ENABLE_SDL2) 389if (ENABLE_SDL2)
390 target_link_libraries(yuzu PRIVATE SDL2) 390 if (YUZU_USE_EXTERNAL_SDL2)
391 target_link_libraries(yuzu PRIVATE SDL2-static)
392 else()
393 target_link_libraries(yuzu PRIVATE SDL2)
394 endif()
391 target_compile_definitions(yuzu PRIVATE HAVE_SDL2) 395 target_compile_definitions(yuzu PRIVATE HAVE_SDL2)
392endif() 396endif()
393 397
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 346d14252..c21153560 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -167,6 +167,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
167 167
168constexpr int default_mouse_hide_timeout = 2500; 168constexpr int default_mouse_hide_timeout = 2500;
169constexpr int default_mouse_center_timeout = 10; 169constexpr int default_mouse_center_timeout = 10;
170constexpr int default_input_update_timeout = 1;
170 171
171/** 172/**
172 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 173 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
@@ -405,6 +406,10 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
405 mouse_center_timer.setInterval(default_mouse_center_timeout); 406 mouse_center_timer.setInterval(default_mouse_center_timeout);
406 connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor); 407 connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor);
407 408
409 update_input_timer.setInterval(default_input_update_timeout);
410 connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers);
411 update_input_timer.start();
412
408 MigrateConfigFiles(); 413 MigrateConfigFiles();
409 414
410 if (has_broken_vulkan) { 415 if (has_broken_vulkan) {
@@ -3637,6 +3642,13 @@ void GMainWindow::UpdateUISettings() {
3637 UISettings::values.first_start = false; 3642 UISettings::values.first_start = false;
3638} 3643}
3639 3644
3645void GMainWindow::UpdateInputDrivers() {
3646 if (!input_subsystem) {
3647 return;
3648 }
3649 input_subsystem->PumpEvents();
3650}
3651
3640void GMainWindow::HideMouseCursor() { 3652void GMainWindow::HideMouseCursor() {
3641 if (emu_thread == nullptr && UISettings::values.hide_mouse) { 3653 if (emu_thread == nullptr && UISettings::values.hide_mouse) {
3642 mouse_hide_timer.stop(); 3654 mouse_hide_timer.stop();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6a9992d05..4f9c3b450 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -353,6 +353,7 @@ private:
353 void UpdateGPUAccuracyButton(); 353 void UpdateGPUAccuracyButton();
354 void UpdateStatusButtons(); 354 void UpdateStatusButtons();
355 void UpdateUISettings(); 355 void UpdateUISettings();
356 void UpdateInputDrivers();
356 void HideMouseCursor(); 357 void HideMouseCursor();
357 void ShowMouseCursor(); 358 void ShowMouseCursor();
358 void CenterMouseCursor(); 359 void CenterMouseCursor();
@@ -404,6 +405,7 @@ private:
404 bool auto_muted = false; 405 bool auto_muted = false;
405 QTimer mouse_hide_timer; 406 QTimer mouse_hide_timer;
406 QTimer mouse_center_timer; 407 QTimer mouse_center_timer;
408 QTimer update_input_timer;
407 409
408 QString startup_icon_theme; 410 QString startup_icon_theme;
409 bool os_dark_mode = false; 411 bool os_dark_mode = false;
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index 6a91212e2..ccdcf10fa 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -4,16 +4,19 @@
4#include "video_core/vulkan_common/vulkan_wrapper.h" 4#include "video_core/vulkan_common/vulkan_wrapper.h"
5 5
6#ifdef _WIN32 6#ifdef _WIN32
7#include <cstring> // for memset, strncpy 7#include <cstring>
8#include <processthreadsapi.h> 8#include <processthreadsapi.h>
9#include <windows.h> 9#include <windows.h>
10#elif defined(YUZU_UNIX) 10#elif defined(YUZU_UNIX)
11#include <cstring>
11#include <errno.h> 12#include <errno.h>
13#include <spawn.h>
14#include <sys/types.h>
12#include <sys/wait.h> 15#include <sys/wait.h>
13#include <unistd.h> 16#include <unistd.h>
14#endif 17#endif
15 18
16#include <cstdio> 19#include <fmt/core.h>
17#include "video_core/vulkan_common/vulkan_instance.h" 20#include "video_core/vulkan_common/vulkan_instance.h"
18#include "video_core/vulkan_common/vulkan_library.h" 21#include "video_core/vulkan_common/vulkan_library.h"
19#include "yuzu/startup_checks.h" 22#include "yuzu/startup_checks.h"
@@ -27,7 +30,7 @@ void CheckVulkan() {
27 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); 30 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
28 31
29 } catch (const Vulkan::vk::Exception& exception) { 32 } catch (const Vulkan::vk::Exception& exception) {
30 std::fprintf(stderr, "Failed to initialize Vulkan: %s\n", exception.what()); 33 fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what());
31 } 34 }
32} 35}
33 36
@@ -49,8 +52,15 @@ bool CheckEnvVars(bool* is_child) {
49 *is_child = true; 52 *is_child = true;
50 return false; 53 return false;
51 } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) { 54 } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) {
52 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n", 55 fmt::print(stderr, "SetEnvironmentVariableA failed to set {} with error {}\n",
53 IS_CHILD_ENV_VAR, GetLastError()); 56 IS_CHILD_ENV_VAR, GetLastError());
57 return true;
58 }
59#elif defined(YUZU_UNIX)
60 const char* startup_check_var = getenv(STARTUP_CHECK_ENV_VAR);
61 if (startup_check_var != nullptr &&
62 std::strncmp(startup_check_var, ENV_VAR_ENABLED_TEXT, 8) == 0) {
63 CheckVulkan();
54 return true; 64 return true;
55 } 65 }
56#endif 66#endif
@@ -62,8 +72,8 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulka
62 // Set the startup variable for child processes 72 // Set the startup variable for child processes
63 const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT); 73 const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT);
64 if (!env_var_set) { 74 if (!env_var_set) {
65 std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n", 75 fmt::print(stderr, "SetEnvironmentVariableA failed to set {} with error {}\n",
66 STARTUP_CHECK_ENV_VAR, GetLastError()); 76 STARTUP_CHECK_ENV_VAR, GetLastError());
67 return false; 77 return false;
68 } 78 }
69 79
@@ -81,48 +91,57 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulka
81 DWORD exit_code = STILL_ACTIVE; 91 DWORD exit_code = STILL_ACTIVE;
82 const int err = GetExitCodeProcess(process_info.hProcess, &exit_code); 92 const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
83 if (err == 0) { 93 if (err == 0) {
84 std::fprintf(stderr, "GetExitCodeProcess failed with error %lu\n", GetLastError()); 94 fmt::print(stderr, "GetExitCodeProcess failed with error {}\n", GetLastError());
85 } 95 }
86 96
87 // Vulkan is broken if the child crashed (return value is not zero) 97 // Vulkan is broken if the child crashed (return value is not zero)
88 *has_broken_vulkan = (exit_code != 0); 98 *has_broken_vulkan = (exit_code != 0);
89 99
90 if (CloseHandle(process_info.hProcess) == 0) { 100 if (CloseHandle(process_info.hProcess) == 0) {
91 std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError()); 101 fmt::print(stderr, "CloseHandle failed with error {}\n", GetLastError());
92 } 102 }
93 if (CloseHandle(process_info.hThread) == 0) { 103 if (CloseHandle(process_info.hThread) == 0) {
94 std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError()); 104 fmt::print(stderr, "CloseHandle failed with error {}\n", GetLastError());
95 } 105 }
96 } 106 }
97 107
98 if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) { 108 if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) {
99 std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %lu\n", 109 fmt::print(stderr, "SetEnvironmentVariableA failed to clear {} with error {}\n",
100 STARTUP_CHECK_ENV_VAR, GetLastError()); 110 STARTUP_CHECK_ENV_VAR, GetLastError());
101 } 111 }
102 112
103#elif defined(YUZU_UNIX) 113#elif defined(YUZU_UNIX)
114 const int env_var_set = setenv(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT, 1);
115 if (env_var_set == -1) {
116 const int err = errno;
117 fmt::print(stderr, "setenv failed to set {} with error {}\n", STARTUP_CHECK_ENV_VAR, err);
118 return false;
119 }
120
104 if (perform_vulkan_check) { 121 if (perform_vulkan_check) {
105 const pid_t pid = fork(); 122 const pid_t pid = SpawnChild(arg0);
106 if (pid == 0) { 123 if (pid == -1) {
107 CheckVulkan();
108 return true;
109 } else if (pid == -1) {
110 const int err = errno;
111 std::fprintf(stderr, "fork failed with error %d\n", err);
112 return false; 124 return false;
113 } 125 }
114 126
115 // Get exit code from child process 127 // Get exit code from child process
116 int status; 128 int status;
117 const int r_val = wait(&status); 129 const int r_val = waitpid(pid, &status, 0);
118 if (r_val == -1) { 130 if (r_val == -1) {
119 const int err = errno; 131 const int err = errno;
120 std::fprintf(stderr, "wait failed with error %d\n", err); 132 fmt::print(stderr, "wait failed with error {}\n", err);
121 return false; 133 return false;
122 } 134 }
123 // Vulkan is broken if the child crashed (return value is not zero) 135 // Vulkan is broken if the child crashed (return value is not zero)
124 *has_broken_vulkan = (status != 0); 136 *has_broken_vulkan = (status != 0);
125 } 137 }
138
139 const int env_var_cleared = unsetenv(STARTUP_CHECK_ENV_VAR);
140 if (env_var_cleared == -1) {
141 const int err = errno;
142 fmt::print(stderr, "unsetenv failed to clear {} with error {}\n", STARTUP_CHECK_ENV_VAR,
143 err);
144 }
126#endif 145#endif
127 return false; 146 return false;
128} 147}
@@ -150,10 +169,29 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) {
150 pi // lpProcessInformation 169 pi // lpProcessInformation
151 ); 170 );
152 if (!process_created) { 171 if (!process_created) {
153 std::fprintf(stderr, "CreateProcessA failed with error %lu\n", GetLastError()); 172 fmt::print(stderr, "CreateProcessA failed with error {}\n", GetLastError());
154 return false; 173 return false;
155 } 174 }
156 175
157 return true; 176 return true;
158} 177}
178#elif defined(YUZU_UNIX)
179pid_t SpawnChild(const char* arg0) {
180 const pid_t pid = fork();
181
182 if (pid == -1) {
183 // error
184 const int err = errno;
185 fmt::print(stderr, "fork failed with error {}\n", err);
186 return pid;
187 } else if (pid == 0) {
188 // child
189 execl(arg0, arg0, nullptr);
190 const int err = errno;
191 fmt::print(stderr, "execl failed with error {}\n", err);
192 _exit(0);
193 }
194
195 return pid;
196}
159#endif 197#endif
diff --git a/src/yuzu/startup_checks.h b/src/yuzu/startup_checks.h
index d8e563be6..2f86fb843 100644
--- a/src/yuzu/startup_checks.h
+++ b/src/yuzu/startup_checks.h
@@ -5,6 +5,8 @@
5 5
6#ifdef _WIN32 6#ifdef _WIN32
7#include <windows.h> 7#include <windows.h>
8#elif defined(YUZU_UNIX)
9#include <sys/types.h>
8#endif 10#endif
9 11
10constexpr char IS_CHILD_ENV_VAR[] = "YUZU_IS_CHILD"; 12constexpr char IS_CHILD_ENV_VAR[] = "YUZU_IS_CHILD";
@@ -17,4 +19,6 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulka
17 19
18#ifdef _WIN32 20#ifdef _WIN32
19bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags); 21bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags);
22#elif defined(YUZU_UNIX)
23pid_t SpawnChild(const char* arg0);
20#endif 24#endif