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/common/cache_management.cpp5
-rw-r--r--src/common/cache_management.h2
-rw-r--r--src/common/input.h40
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/frontend/emu_window.h2
-rw-r--r--src/core/hid/emulated_console.cpp4
-rw-r--r--src/core/hid/emulated_controller.cpp38
-rw-r--r--src/core/hid/emulated_devices.cpp14
-rw-r--r--src/core/hle/service/nifm/nifm.cpp41
-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/helpers/stick_from_buttons.cpp25
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp13
-rw-r--r--src/input_common/main.cpp112
-rw-r--r--src/input_common/main.h3
-rw-r--r--src/shader_recompiler/CMakeLists.txt1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp13
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp35
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp81
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.h9
-rw-r--r--src/shader_recompiler/host_translate_info.h3
-rw-r--r--src/shader_recompiler/ir_opt/layer_pass.cpp68
-rw-r--r--src/shader_recompiler/ir_opt/passes.h1
-rw-r--r--src/shader_recompiler/shader_info.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/renderer_opengl/gl_shader_cache.cpp37
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp37
-rw-r--r--src/video_core/surface.cpp15
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp120
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h16
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp27
-rw-r--r--src/video_core/vulkan_common/vulkan_surface.cpp38
-rw-r--r--src/yuzu/CMakeLists.txt6
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp2
-rw-r--r--src/yuzu/main.cpp15
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/startup_checks.cpp84
-rw-r--r--src/yuzu/startup_checks.h4
-rw-r--r--src/yuzu_cmd/config.cpp6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp27
67 files changed, 859 insertions, 443 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/common/cache_management.cpp b/src/common/cache_management.cpp
index 57810b76a..ed353828a 100644
--- a/src/common/cache_management.cpp
+++ b/src/common/cache_management.cpp
@@ -1,11 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <cstdint>
4#include <cstring> 5#include <cstring>
5 6
6#include "alignment.h" 7#include "common/cache_management.h"
7#include "cache_management.h"
8#include "common_types.h"
9 8
10namespace Common { 9namespace Common {
11 10
diff --git a/src/common/cache_management.h b/src/common/cache_management.h
index e467b87e4..038323e95 100644
--- a/src/common/cache_management.h
+++ b/src/common/cache_management.h
@@ -3,7 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "stdlib.h" 6#include <cstddef>
7 7
8namespace Common { 8namespace Common {
9 9
diff --git a/src/common/input.h b/src/common/input.h
index cb30b7254..449e0193f 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -383,6 +383,16 @@ void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDevic
383 } 383 }
384} 384}
385 385
386inline void RegisterInputFactory(const std::string& name,
387 std::shared_ptr<Factory<InputDevice>> factory) {
388 RegisterFactory<InputDevice>(name, std::move(factory));
389}
390
391inline void RegisterOutputFactory(const std::string& name,
392 std::shared_ptr<Factory<OutputDevice>> factory) {
393 RegisterFactory<OutputDevice>(name, std::move(factory));
394}
395
386/** 396/**
387 * Unregisters an input device factory. 397 * Unregisters an input device factory.
388 * @tparam InputDeviceType the type of input devices the factory can create 398 * @tparam InputDeviceType the type of input devices the factory can create
@@ -395,6 +405,14 @@ void UnregisterFactory(const std::string& name) {
395 } 405 }
396} 406}
397 407
408inline void UnregisterInputFactory(const std::string& name) {
409 UnregisterFactory<InputDevice>(name);
410}
411
412inline void UnregisterOutputFactory(const std::string& name) {
413 UnregisterFactory<OutputDevice>(name);
414}
415
398/** 416/**
399 * Create an input device from given paramters. 417 * Create an input device from given paramters.
400 * @tparam InputDeviceType the type of input devices to create 418 * @tparam InputDeviceType the type of input devices to create
@@ -416,13 +434,21 @@ std::unique_ptr<InputDeviceType> CreateDeviceFromString(const std::string& param
416 return pair->second->Create(package); 434 return pair->second->Create(package);
417} 435}
418 436
437inline std::unique_ptr<InputDevice> CreateInputDeviceFromString(const std::string& params) {
438 return CreateDeviceFromString<InputDevice>(params);
439}
440
441inline std::unique_ptr<OutputDevice> CreateOutputDeviceFromString(const std::string& params) {
442 return CreateDeviceFromString<OutputDevice>(params);
443}
444
419/** 445/**
420 * Create an input device from given paramters. 446 * Create an input device from given parameters.
421 * @tparam InputDeviceType the type of input devices to create 447 * @tparam InputDeviceType the type of input devices to create
422 * @param A ParamPackage that contains all parameters for creating the device 448 * @param package A ParamPackage that contains all parameters for creating the device
423 */ 449 */
424template <typename InputDeviceType> 450template <typename InputDeviceType>
425std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package) { 451std::unique_ptr<InputDeviceType> CreateDevice(const ParamPackage& package) {
426 const std::string engine = package.Get("engine", "null"); 452 const std::string engine = package.Get("engine", "null");
427 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list; 453 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
428 const auto pair = factory_list.find(engine); 454 const auto pair = factory_list.find(engine);
@@ -435,4 +461,12 @@ std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package
435 return pair->second->Create(package); 461 return pair->second->Create(package);
436} 462}
437 463
464inline std::unique_ptr<InputDevice> CreateInputDevice(const ParamPackage& package) {
465 return CreateDevice<InputDevice>(package);
466}
467
468inline std::unique_ptr<OutputDevice> CreateOutputDevice(const ParamPackage& package) {
469 return CreateDevice<OutputDevice>(package);
470}
471
438} // namespace Common::Input 472} // namespace Common::Input
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/frontend/emu_window.h b/src/core/frontend/emu_window.h
index ac1906d5e..95363b645 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -17,6 +17,8 @@ enum class WindowSystemType {
17 Windows, 17 Windows,
18 X11, 18 X11,
19 Wayland, 19 Wayland,
20 Cocoa,
21 Android,
20}; 22};
21 23
22/** 24/**
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index fb7e5802a..b6c8cc58d 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -68,7 +68,7 @@ void EmulatedConsole::ReloadInput() {
68 // If you load any device here add the equivalent to the UnloadInput() function 68 // If you load any device here add the equivalent to the UnloadInput() function
69 SetTouchParams(); 69 SetTouchParams();
70 70
71 motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params); 71 motion_devices = Common::Input::CreateInputDevice(motion_params);
72 if (motion_devices) { 72 if (motion_devices) {
73 motion_devices->SetCallback({ 73 motion_devices->SetCallback({
74 .on_change = 74 .on_change =
@@ -79,7 +79,7 @@ void EmulatedConsole::ReloadInput() {
79 // Unique index for identifying touch device source 79 // Unique index for identifying touch device source
80 std::size_t index = 0; 80 std::size_t index = 0;
81 for (auto& touch_device : touch_devices) { 81 for (auto& touch_device : touch_devices) {
82 touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]); 82 touch_device = Common::Input::CreateInputDevice(touch_params[index]);
83 if (!touch_device) { 83 if (!touch_device) {
84 continue; 84 continue;
85 } 85 }
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index ec1364452..c96d9eef3 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
5
4#include "common/thread.h" 6#include "common/thread.h"
5#include "core/hid/emulated_controller.h" 7#include "core/hid/emulated_controller.h"
6#include "core/hid/input_converter.h" 8#include "core/hid/input_converter.h"
@@ -144,29 +146,23 @@ void EmulatedController::LoadDevices() {
144 146
145 LoadTASParams(); 147 LoadTASParams();
146 148
147 std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 149 std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice);
148 button_params.begin() + Settings::NativeButton::BUTTON_NS_END, 150 std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice);
149 button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); 151 std::ranges::transform(motion_params, motion_devices.begin(), Common::Input::CreateInputDevice);
150 std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 152 std::ranges::transform(trigger_params, trigger_devices.begin(),
151 stick_params.begin() + Settings::NativeAnalog::STICK_HID_END, 153 Common::Input::CreateInputDevice);
152 stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); 154 std::ranges::transform(battery_params, battery_devices.begin(),
153 std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, 155 Common::Input::CreateInputDevice);
154 motion_params.begin() + Settings::NativeMotion::MOTION_HID_END, 156 camera_devices = Common::Input::CreateInputDevice(camera_params);
155 motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); 157 nfc_devices = Common::Input::CreateInputDevice(nfc_params);
156 std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(), 158 std::ranges::transform(output_params, output_devices.begin(),
157 Common::Input::CreateDevice<Common::Input::InputDevice>); 159 Common::Input::CreateOutputDevice);
158 std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
159 Common::Input::CreateDevice<Common::Input::InputDevice>);
160 camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
161 nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params);
162 std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
163 Common::Input::CreateDevice<Common::Input::OutputDevice>);
164 160
165 // Initialize TAS devices 161 // Initialize TAS devices
166 std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(), 162 std::ranges::transform(tas_button_params, tas_button_devices.begin(),
167 Common::Input::CreateDevice<Common::Input::InputDevice>); 163 Common::Input::CreateInputDevice);
168 std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(), 164 std::ranges::transform(tas_stick_params, tas_stick_devices.begin(),
169 Common::Input::CreateDevice<Common::Input::InputDevice>); 165 Common::Input::CreateInputDevice);
170} 166}
171 167
172void EmulatedController::LoadTASParams() { 168void EmulatedController::LoadTASParams() {
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index 658dbd318..e421828d2 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -25,12 +25,12 @@ void EmulatedDevices::ReloadInput() {
25 Common::ParamPackage mouse_params; 25 Common::ParamPackage mouse_params;
26 mouse_params.Set("engine", "mouse"); 26 mouse_params.Set("engine", "mouse");
27 mouse_params.Set("button", static_cast<int>(key_index)); 27 mouse_params.Set("button", static_cast<int>(key_index));
28 mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params); 28 mouse_device = Common::Input::CreateInputDevice(mouse_params);
29 key_index++; 29 key_index++;
30 } 30 }
31 31
32 mouse_stick_device = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( 32 mouse_stick_device =
33 "engine:mouse,axis_x:0,axis_y:1"); 33 Common::Input::CreateInputDeviceFromString("engine:mouse,axis_x:0,axis_y:1");
34 34
35 // First two axis are reserved for mouse position 35 // First two axis are reserved for mouse position
36 key_index = 2; 36 key_index = 2;
@@ -38,7 +38,7 @@ void EmulatedDevices::ReloadInput() {
38 Common::ParamPackage mouse_params; 38 Common::ParamPackage mouse_params;
39 mouse_params.Set("engine", "mouse"); 39 mouse_params.Set("engine", "mouse");
40 mouse_params.Set("axis", static_cast<int>(key_index)); 40 mouse_params.Set("axis", static_cast<int>(key_index));
41 mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params); 41 mouse_device = Common::Input::CreateInputDevice(mouse_params);
42 key_index++; 42 key_index++;
43 } 43 }
44 44
@@ -50,7 +50,7 @@ void EmulatedDevices::ReloadInput() {
50 keyboard_params.Set("button", static_cast<int>(key_index)); 50 keyboard_params.Set("button", static_cast<int>(key_index));
51 keyboard_params.Set("port", 1); 51 keyboard_params.Set("port", 1);
52 keyboard_params.Set("pad", 0); 52 keyboard_params.Set("pad", 0);
53 keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params); 53 keyboard_device = Common::Input::CreateInputDevice(keyboard_params);
54 key_index++; 54 key_index++;
55 } 55 }
56 56
@@ -62,11 +62,11 @@ void EmulatedDevices::ReloadInput() {
62 keyboard_params.Set("button", static_cast<int>(key_index)); 62 keyboard_params.Set("button", static_cast<int>(key_index));
63 keyboard_params.Set("port", 1); 63 keyboard_params.Set("port", 1);
64 keyboard_params.Set("pad", 1); 64 keyboard_params.Set("pad", 1);
65 keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params); 65 keyboard_device = Common::Input::CreateInputDevice(keyboard_params);
66 key_index++; 66 key_index++;
67 } 67 }
68 68
69 ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params); 69 ring_analog_device = Common::Input::CreateInputDevice(ring_params);
70 70
71 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { 71 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
72 if (!mouse_button_devices[index]) { 72 if (!mouse_button_devices[index]) {
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index e3ef06481..4fa9f51a6 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -129,6 +129,9 @@ static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
129 "NifmNetworkProfileData has incorrect size."); 129 "NifmNetworkProfileData has incorrect size.");
130#pragma pack(pop) 130#pragma pack(pop)
131 131
132constexpr Result ResultPendingConnection{ErrorModule::NIFM, 111};
133constexpr Result ResultNetworkCommunicationDisabled{ErrorModule::NIFM, 1111};
134
132class IScanRequest final : public ServiceFramework<IScanRequest> { 135class IScanRequest final : public ServiceFramework<IScanRequest> {
133public: 136public:
134 explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} { 137 explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} {
@@ -192,6 +195,10 @@ private:
192 void Submit(Kernel::HLERequestContext& ctx) { 195 void Submit(Kernel::HLERequestContext& ctx) {
193 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 196 LOG_WARNING(Service_NIFM, "(STUBBED) called");
194 197
198 if (state == RequestState::NotSubmitted) {
199 UpdateState(RequestState::Pending);
200 }
201
195 IPC::ResponseBuilder rb{ctx, 2}; 202 IPC::ResponseBuilder rb{ctx, 2};
196 rb.Push(ResultSuccess); 203 rb.Push(ResultSuccess);
197 } 204 }
@@ -201,19 +208,32 @@ private:
201 208
202 IPC::ResponseBuilder rb{ctx, 3}; 209 IPC::ResponseBuilder rb{ctx, 3};
203 rb.Push(ResultSuccess); 210 rb.Push(ResultSuccess);
204 211 rb.PushEnum(state);
205 if (Network::GetHostIPv4Address().has_value()) {
206 rb.PushEnum(RequestState::Connected);
207 } else {
208 rb.PushEnum(RequestState::NotSubmitted);
209 }
210 } 212 }
211 213
212 void GetResult(Kernel::HLERequestContext& ctx) { 214 void GetResult(Kernel::HLERequestContext& ctx) {
213 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 215 LOG_WARNING(Service_NIFM, "(STUBBED) called");
214 216
217 const auto result = [this] {
218 const auto has_connection = Network::GetHostIPv4Address().has_value();
219 switch (state) {
220 case RequestState::NotSubmitted:
221 return has_connection ? ResultSuccess : ResultNetworkCommunicationDisabled;
222 case RequestState::Pending:
223 if (has_connection) {
224 UpdateState(RequestState::Connected);
225 } else {
226 UpdateState(RequestState::Error);
227 }
228 return ResultPendingConnection;
229 case RequestState::Connected:
230 default:
231 return ResultSuccess;
232 }
233 }();
234
215 IPC::ResponseBuilder rb{ctx, 2}; 235 IPC::ResponseBuilder rb{ctx, 2};
216 rb.Push(ResultSuccess); 236 rb.Push(result);
217 } 237 }
218 238
219 void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { 239 void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
@@ -252,8 +272,15 @@ private:
252 rb.Push<u32>(0); 272 rb.Push<u32>(0);
253 } 273 }
254 274
275 void UpdateState(RequestState new_state) {
276 state = new_state;
277 event1->Signal();
278 }
279
255 KernelHelpers::ServiceContext service_context; 280 KernelHelpers::ServiceContext service_context;
256 281
282 RequestState state;
283
257 Kernel::KEvent* event1; 284 Kernel::KEvent* event1;
258 Kernel::KEvent* event2; 285 Kernel::KEvent* event2;
259}; 286};
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/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index 536d413a5..82aa6ac2f 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -294,6 +294,15 @@ public:
294 } 294 }
295 295
296private: 296private:
297 static constexpr Common::Input::AnalogProperties properties{
298 .deadzone = 0.0f,
299 .range = 1.0f,
300 .threshold = 0.5f,
301 .offset = 0.0f,
302 .inverted = false,
303 .toggle = false,
304 };
305
297 Button up; 306 Button up;
298 Button down; 307 Button down;
299 Button left; 308 Button left;
@@ -311,23 +320,17 @@ private:
311 float last_x_axis_value{}; 320 float last_x_axis_value{};
312 float last_y_axis_value{}; 321 float last_y_axis_value{};
313 Common::Input::ButtonStatus modifier_status{}; 322 Common::Input::ButtonStatus modifier_status{};
314 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
315 std::chrono::time_point<std::chrono::steady_clock> last_update; 323 std::chrono::time_point<std::chrono::steady_clock> last_update;
316}; 324};
317 325
318std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create( 326std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create(
319 const Common::ParamPackage& params) { 327 const Common::ParamPackage& params) {
320 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); 328 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
321 auto up = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( 329 auto up = Common::Input::CreateInputDeviceFromString(params.Get("up", null_engine));
322 params.Get("up", null_engine)); 330 auto down = Common::Input::CreateInputDeviceFromString(params.Get("down", null_engine));
323 auto down = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( 331 auto left = Common::Input::CreateInputDeviceFromString(params.Get("left", null_engine));
324 params.Get("down", null_engine)); 332 auto right = Common::Input::CreateInputDeviceFromString(params.Get("right", null_engine));
325 auto left = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( 333 auto modifier = Common::Input::CreateInputDeviceFromString(params.Get("modifier", null_engine));
326 params.Get("left", null_engine));
327 auto right = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
328 params.Get("right", null_engine));
329 auto modifier = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
330 params.Get("modifier", null_engine));
331 auto modifier_scale = params.Get("modifier_scale", 0.5f); 334 auto modifier_scale = params.Get("modifier_scale", 0.5f);
332 auto modifier_angle = params.Get("modifier_angle", 5.5f); 335 auto modifier_angle = params.Get("modifier_angle", 5.5f);
333 return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left), 336 return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left),
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp
index 003a38da5..e064b13d9 100644
--- a/src/input_common/helpers/touch_from_buttons.cpp
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -59,18 +59,25 @@ public:
59 } 59 }
60 60
61private: 61private:
62 static constexpr Common::Input::AnalogProperties properties{
63 .deadzone = 0.0f,
64 .range = 1.0f,
65 .threshold = 0.5f,
66 .offset = 0.0f,
67 .inverted = false,
68 .toggle = false,
69 };
70
62 Button button; 71 Button button;
63 bool last_button_value; 72 bool last_button_value;
64 const float x; 73 const float x;
65 const float y; 74 const float y;
66 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
67}; 75};
68 76
69std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create( 77std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
70 const Common::ParamPackage& params) { 78 const Common::ParamPackage& params) {
71 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); 79 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
72 auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( 80 auto button = Common::Input::CreateInputDeviceFromString(params.Get("button", null_engine));
73 params.Get("button", null_engine));
74 const float x = params.Get("x", 0.0f) / 1280.0f; 81 const float x = params.Get("x", 0.0f) / 1280.0f;
75 const float y = params.Get("y", 0.0f) / 720.0f; 82 const float y = params.Get("y", 0.0f) / 720.0f;
76 return std::make_unique<TouchFromButtonDevice>(std::move(button), x, y); 83 return std::make_unique<TouchFromButtonDevice>(std::move(button), x, y);
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 76df133f3..942a13535 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -33,129 +33,113 @@ struct InputSubsystem::Impl {
33 keyboard->SetMappingCallback(mapping_callback); 33 keyboard->SetMappingCallback(mapping_callback);
34 keyboard_factory = std::make_shared<InputFactory>(keyboard); 34 keyboard_factory = std::make_shared<InputFactory>(keyboard);
35 keyboard_output_factory = std::make_shared<OutputFactory>(keyboard); 35 keyboard_output_factory = std::make_shared<OutputFactory>(keyboard);
36 Common::Input::RegisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName(), 36 Common::Input::RegisterInputFactory(keyboard->GetEngineName(), keyboard_factory);
37 keyboard_factory); 37 Common::Input::RegisterOutputFactory(keyboard->GetEngineName(), keyboard_output_factory);
38 Common::Input::RegisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName(),
39 keyboard_output_factory);
40 38
41 mouse = std::make_shared<Mouse>("mouse"); 39 mouse = std::make_shared<Mouse>("mouse");
42 mouse->SetMappingCallback(mapping_callback); 40 mouse->SetMappingCallback(mapping_callback);
43 mouse_factory = std::make_shared<InputFactory>(mouse); 41 mouse_factory = std::make_shared<InputFactory>(mouse);
44 mouse_output_factory = std::make_shared<OutputFactory>(mouse); 42 mouse_output_factory = std::make_shared<OutputFactory>(mouse);
45 Common::Input::RegisterFactory<Common::Input::InputDevice>(mouse->GetEngineName(), 43 Common::Input::RegisterInputFactory(mouse->GetEngineName(), mouse_factory);
46 mouse_factory); 44 Common::Input::RegisterOutputFactory(mouse->GetEngineName(), mouse_output_factory);
47 Common::Input::RegisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName(),
48 mouse_output_factory);
49 45
50 touch_screen = std::make_shared<TouchScreen>("touch"); 46 touch_screen = std::make_shared<TouchScreen>("touch");
51 touch_screen_factory = std::make_shared<InputFactory>(touch_screen); 47 touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
52 Common::Input::RegisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName(), 48 Common::Input::RegisterInputFactory(touch_screen->GetEngineName(), touch_screen_factory);
53 touch_screen_factory);
54 49
55 gcadapter = std::make_shared<GCAdapter>("gcpad"); 50 gcadapter = std::make_shared<GCAdapter>("gcpad");
56 gcadapter->SetMappingCallback(mapping_callback); 51 gcadapter->SetMappingCallback(mapping_callback);
57 gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter); 52 gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
58 gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter); 53 gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
59 Common::Input::RegisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName(), 54 Common::Input::RegisterInputFactory(gcadapter->GetEngineName(), gcadapter_input_factory);
60 gcadapter_input_factory); 55 Common::Input::RegisterOutputFactory(gcadapter->GetEngineName(), gcadapter_output_factory);
61 Common::Input::RegisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName(),
62 gcadapter_output_factory);
63 56
64 udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp"); 57 udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
65 udp_client->SetMappingCallback(mapping_callback); 58 udp_client->SetMappingCallback(mapping_callback);
66 udp_client_input_factory = std::make_shared<InputFactory>(udp_client); 59 udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
67 udp_client_output_factory = std::make_shared<OutputFactory>(udp_client); 60 udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
68 Common::Input::RegisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName(), 61 Common::Input::RegisterInputFactory(udp_client->GetEngineName(), udp_client_input_factory);
69 udp_client_input_factory); 62 Common::Input::RegisterOutputFactory(udp_client->GetEngineName(),
70 Common::Input::RegisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName(), 63 udp_client_output_factory);
71 udp_client_output_factory);
72 64
73 tas_input = std::make_shared<TasInput::Tas>("tas"); 65 tas_input = std::make_shared<TasInput::Tas>("tas");
74 tas_input->SetMappingCallback(mapping_callback); 66 tas_input->SetMappingCallback(mapping_callback);
75 tas_input_factory = std::make_shared<InputFactory>(tas_input); 67 tas_input_factory = std::make_shared<InputFactory>(tas_input);
76 tas_output_factory = std::make_shared<OutputFactory>(tas_input); 68 tas_output_factory = std::make_shared<OutputFactory>(tas_input);
77 Common::Input::RegisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName(), 69 Common::Input::RegisterInputFactory(tas_input->GetEngineName(), tas_input_factory);
78 tas_input_factory); 70 Common::Input::RegisterOutputFactory(tas_input->GetEngineName(), tas_output_factory);
79 Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
80 tas_output_factory);
81 71
82 camera = std::make_shared<Camera>("camera"); 72 camera = std::make_shared<Camera>("camera");
83 camera->SetMappingCallback(mapping_callback); 73 camera->SetMappingCallback(mapping_callback);
84 camera_input_factory = std::make_shared<InputFactory>(camera); 74 camera_input_factory = std::make_shared<InputFactory>(camera);
85 camera_output_factory = std::make_shared<OutputFactory>(camera); 75 camera_output_factory = std::make_shared<OutputFactory>(camera);
86 Common::Input::RegisterFactory<Common::Input::InputDevice>(camera->GetEngineName(), 76 Common::Input::RegisterInputFactory(camera->GetEngineName(), camera_input_factory);
87 camera_input_factory); 77 Common::Input::RegisterOutputFactory(camera->GetEngineName(), camera_output_factory);
88 Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
89 camera_output_factory);
90 78
91 virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo"); 79 virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo");
92 virtual_amiibo->SetMappingCallback(mapping_callback); 80 virtual_amiibo->SetMappingCallback(mapping_callback);
93 virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo); 81 virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo);
94 virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo); 82 virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo);
95 Common::Input::RegisterFactory<Common::Input::InputDevice>(virtual_amiibo->GetEngineName(), 83 Common::Input::RegisterInputFactory(virtual_amiibo->GetEngineName(),
96 virtual_amiibo_input_factory); 84 virtual_amiibo_input_factory);
97 Common::Input::RegisterFactory<Common::Input::OutputDevice>(virtual_amiibo->GetEngineName(), 85 Common::Input::RegisterOutputFactory(virtual_amiibo->GetEngineName(),
98 virtual_amiibo_output_factory); 86 virtual_amiibo_output_factory);
99 87
100#ifdef HAVE_SDL2 88#ifdef HAVE_SDL2
101 sdl = std::make_shared<SDLDriver>("sdl"); 89 sdl = std::make_shared<SDLDriver>("sdl");
102 sdl->SetMappingCallback(mapping_callback); 90 sdl->SetMappingCallback(mapping_callback);
103 sdl_input_factory = std::make_shared<InputFactory>(sdl); 91 sdl_input_factory = std::make_shared<InputFactory>(sdl);
104 sdl_output_factory = std::make_shared<OutputFactory>(sdl); 92 sdl_output_factory = std::make_shared<OutputFactory>(sdl);
105 Common::Input::RegisterFactory<Common::Input::InputDevice>(sdl->GetEngineName(), 93 Common::Input::RegisterInputFactory(sdl->GetEngineName(), sdl_input_factory);
106 sdl_input_factory); 94 Common::Input::RegisterOutputFactory(sdl->GetEngineName(), sdl_output_factory);
107 Common::Input::RegisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName(),
108 sdl_output_factory);
109#endif 95#endif
110 96
111 Common::Input::RegisterFactory<Common::Input::InputDevice>( 97 Common::Input::RegisterInputFactory("touch_from_button",
112 "touch_from_button", std::make_shared<TouchFromButton>()); 98 std::make_shared<TouchFromButton>());
113 Common::Input::RegisterFactory<Common::Input::InputDevice>( 99 Common::Input::RegisterInputFactory("analog_from_button",
114 "analog_from_button", std::make_shared<StickFromButton>()); 100 std::make_shared<StickFromButton>());
115 } 101 }
116 102
117 void Shutdown() { 103 void Shutdown() {
118 Common::Input::UnregisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName()); 104 Common::Input::UnregisterInputFactory(keyboard->GetEngineName());
119 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName()); 105 Common::Input::UnregisterOutputFactory(keyboard->GetEngineName());
120 keyboard.reset(); 106 keyboard.reset();
121 107
122 Common::Input::UnregisterFactory<Common::Input::InputDevice>(mouse->GetEngineName()); 108 Common::Input::UnregisterInputFactory(mouse->GetEngineName());
123 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName()); 109 Common::Input::UnregisterOutputFactory(mouse->GetEngineName());
124 mouse.reset(); 110 mouse.reset();
125 111
126 Common::Input::UnregisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName()); 112 Common::Input::UnregisterInputFactory(touch_screen->GetEngineName());
127 touch_screen.reset(); 113 touch_screen.reset();
128 114
129 Common::Input::UnregisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName()); 115 Common::Input::UnregisterInputFactory(gcadapter->GetEngineName());
130 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName()); 116 Common::Input::UnregisterOutputFactory(gcadapter->GetEngineName());
131 gcadapter.reset(); 117 gcadapter.reset();
132 118
133 Common::Input::UnregisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName()); 119 Common::Input::UnregisterInputFactory(udp_client->GetEngineName());
134 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName()); 120 Common::Input::UnregisterOutputFactory(udp_client->GetEngineName());
135 udp_client.reset(); 121 udp_client.reset();
136 122
137 Common::Input::UnregisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName()); 123 Common::Input::UnregisterInputFactory(tas_input->GetEngineName());
138 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName()); 124 Common::Input::UnregisterOutputFactory(tas_input->GetEngineName());
139 tas_input.reset(); 125 tas_input.reset();
140 126
141 Common::Input::UnregisterFactory<Common::Input::InputDevice>(camera->GetEngineName()); 127 Common::Input::UnregisterInputFactory(camera->GetEngineName());
142 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(camera->GetEngineName()); 128 Common::Input::UnregisterOutputFactory(camera->GetEngineName());
143 camera.reset(); 129 camera.reset();
144 130
145 Common::Input::UnregisterFactory<Common::Input::InputDevice>( 131 Common::Input::UnregisterInputFactory(virtual_amiibo->GetEngineName());
146 virtual_amiibo->GetEngineName()); 132 Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName());
147 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(
148 virtual_amiibo->GetEngineName());
149 virtual_amiibo.reset(); 133 virtual_amiibo.reset();
150 134
151#ifdef HAVE_SDL2 135#ifdef HAVE_SDL2
152 Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName()); 136 Common::Input::UnregisterInputFactory(sdl->GetEngineName());
153 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName()); 137 Common::Input::UnregisterOutputFactory(sdl->GetEngineName());
154 sdl.reset(); 138 sdl.reset();
155#endif 139#endif
156 140
157 Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button"); 141 Common::Input::UnregisterInputFactory("touch_from_button");
158 Common::Input::UnregisterFactory<Common::Input::InputDevice>("analog_from_button"); 142 Common::Input::UnregisterInputFactory("analog_from_button");
159 } 143 }
160 144
161 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 145 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
@@ -334,6 +318,12 @@ struct InputSubsystem::Impl {
334#endif 318#endif
335 } 319 }
336 320
321 void PumpEvents() const {
322#ifdef HAVE_SDL2
323 sdl->PumpEvents();
324#endif
325 }
326
337 void RegisterInput(const MappingData& data) { 327 void RegisterInput(const MappingData& data) {
338 mapping_factory->RegisterInput(data); 328 mapping_factory->RegisterInput(data);
339 } 329 }
@@ -482,6 +472,10 @@ void InputSubsystem::StopMapping() const {
482 impl->mapping_factory->StopMapping(); 472 impl->mapping_factory->StopMapping();
483} 473}
484 474
475void InputSubsystem::PumpEvents() const {
476 impl->PumpEvents();
477}
478
485std::string GenerateKeyboardParam(int key_code) { 479std::string GenerateKeyboardParam(int key_code) {
486 Common::ParamPackage param; 480 Common::ParamPackage param;
487 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/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 545d69c7e..8cd584154 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -221,6 +221,7 @@ add_library(shader_recompiler STATIC
221 ir_opt/dual_vertex_pass.cpp 221 ir_opt/dual_vertex_pass.cpp
222 ir_opt/global_memory_to_storage_buffer_pass.cpp 222 ir_opt/global_memory_to_storage_buffer_pass.cpp
223 ir_opt/identity_removal_pass.cpp 223 ir_opt/identity_removal_pass.cpp
224 ir_opt/layer_pass.cpp
224 ir_opt/lower_fp16_to_fp32.cpp 225 ir_opt/lower_fp16_to_fp32.cpp
225 ir_opt/lower_int64_to_int32.cpp 226 ir_opt/lower_int64_to_int32.cpp
226 ir_opt/passes.h 227 ir_opt/passes.h
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 265ac9c85..0f86a8004 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -402,8 +402,10 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
402 ctx.AddCapability(spv::Capability::SparseResidency); 402 ctx.AddCapability(spv::Capability::SparseResidency);
403 } 403 }
404 if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) { 404 if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) {
405 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); 405 if (profile.supported_spirv < 0x00010600) {
406 ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); 406 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
407 }
408 ctx.AddCapability(spv::Capability::DemoteToHelperInvocation);
407 } 409 }
408 if (info.stores[IR::Attribute::ViewportIndex]) { 410 if (info.stores[IR::Attribute::ViewportIndex]) {
409 ctx.AddCapability(spv::Capability::MultiViewport); 411 ctx.AddCapability(spv::Capability::MultiViewport);
@@ -426,12 +428,11 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
426 if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || 428 if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
427 info.uses_subgroup_shuffles) && 429 info.uses_subgroup_shuffles) &&
428 profile.support_vote) { 430 profile.support_vote) {
429 ctx.AddExtension("SPV_KHR_shader_ballot"); 431 ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
430 ctx.AddCapability(spv::Capability::SubgroupBallotKHR); 432 ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
431 if (!profile.warp_size_potentially_larger_than_guest) { 433 if (!profile.warp_size_potentially_larger_than_guest) {
432 // vote ops are only used when not taking the long path 434 // vote ops are only used when not taking the long path
433 ctx.AddExtension("SPV_KHR_subgroup_vote"); 435 ctx.AddCapability(spv::Capability::GroupNonUniformVote);
434 ctx.AddCapability(spv::Capability::SubgroupVoteKHR);
435 } 436 }
436 } 437 }
437 if (info.uses_int64_bit_atomics && profile.support_int64_atomics) { 438 if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
index 7ad0b08ac..fb2c792c1 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -12,7 +12,7 @@ void EmitJoin(EmitContext&) {
12 12
13void EmitDemoteToHelperInvocation(EmitContext& ctx) { 13void EmitDemoteToHelperInvocation(EmitContext& ctx) {
14 if (ctx.profile.support_demote_to_helper_invocation) { 14 if (ctx.profile.support_demote_to_helper_invocation) {
15 ctx.OpDemoteToHelperInvocationEXT(); 15 ctx.OpDemoteToHelperInvocation();
16 } else { 16 } else {
17 const Id kill_label{ctx.OpLabel()}; 17 const Id kill_label{ctx.OpLabel()};
18 const Id impossible_label{ctx.OpLabel()}; 18 const Id impossible_label{ctx.OpLabel()};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index 7cbbbfaa6..2c90f2368 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -6,6 +6,10 @@
6 6
7namespace Shader::Backend::SPIRV { 7namespace Shader::Backend::SPIRV {
8namespace { 8namespace {
9Id SubgroupScope(EmitContext& ctx) {
10 return ctx.Const(static_cast<u32>(spv::Scope::Subgroup));
11}
12
9Id GetThreadId(EmitContext& ctx) { 13Id GetThreadId(EmitContext& ctx) {
10 return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); 14 return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
11} 15}
@@ -49,8 +53,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
49} 53}
50 54
51Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { 55Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
52 return ctx.OpSelect(ctx.U32[1], in_range, 56 return ctx.OpSelect(
53 ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); 57 ctx.U32[1], in_range,
58 ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
54} 59}
55 60
56Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { 61Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) {
@@ -71,40 +76,46 @@ Id EmitLaneId(EmitContext& ctx) {
71 76
72Id EmitVoteAll(EmitContext& ctx, Id pred) { 77Id EmitVoteAll(EmitContext& ctx, Id pred) {
73 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 78 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
74 return ctx.OpSubgroupAllKHR(ctx.U1, pred); 79 return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
75 } 80 }
76 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 81 const Id mask_ballot{
82 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
77 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 83 const Id active_mask{WarpExtract(ctx, mask_ballot)};
78 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 84 const Id ballot{
85 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
79 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; 86 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
80 return ctx.OpIEqual(ctx.U1, lhs, active_mask); 87 return ctx.OpIEqual(ctx.U1, lhs, active_mask);
81} 88}
82 89
83Id EmitVoteAny(EmitContext& ctx, Id pred) { 90Id EmitVoteAny(EmitContext& ctx, Id pred) {
84 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 91 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
85 return ctx.OpSubgroupAnyKHR(ctx.U1, pred); 92 return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
86 } 93 }
87 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 94 const Id mask_ballot{
95 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
88 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 96 const Id active_mask{WarpExtract(ctx, mask_ballot)};
89 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 97 const Id ballot{
98 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
90 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; 99 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
91 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value); 100 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value);
92} 101}
93 102
94Id EmitVoteEqual(EmitContext& ctx, Id pred) { 103Id EmitVoteEqual(EmitContext& ctx, Id pred) {
95 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 104 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
96 return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred); 105 return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
97 } 106 }
98 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 107 const Id mask_ballot{
108 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
99 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 109 const Id active_mask{WarpExtract(ctx, mask_ballot)};
100 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 110 const Id ballot{
111 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
101 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)}; 112 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)};
102 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value), 113 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value),
103 ctx.OpIEqual(ctx.U1, lhs, active_mask)); 114 ctx.OpIEqual(ctx.U1, lhs, active_mask));
104} 115}
105 116
106Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { 117Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
107 const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)}; 118 const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
108 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 119 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
109 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); 120 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
110 } 121 }
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 376aae0ea..3adbd2b16 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -9,6 +9,7 @@
9#include "common/settings.h" 9#include "common/settings.h"
10#include "shader_recompiler/exception.h" 10#include "shader_recompiler/exception.h"
11#include "shader_recompiler/frontend/ir/basic_block.h" 11#include "shader_recompiler/frontend/ir/basic_block.h"
12#include "shader_recompiler/frontend/ir/ir_emitter.h"
12#include "shader_recompiler/frontend/ir/post_order.h" 13#include "shader_recompiler/frontend/ir/post_order.h"
13#include "shader_recompiler/frontend/maxwell/structured_control_flow.h" 14#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
14#include "shader_recompiler/frontend/maxwell/translate/translate.h" 15#include "shader_recompiler/frontend/maxwell/translate/translate.h"
@@ -233,6 +234,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
233 Optimization::VerificationPass(program); 234 Optimization::VerificationPass(program);
234 } 235 }
235 Optimization::CollectShaderInfoPass(env, program); 236 Optimization::CollectShaderInfoPass(env, program);
237 Optimization::LayerPass(program, host_info);
238
236 CollectInterpolationInfo(env, program); 239 CollectInterpolationInfo(env, program);
237 AddNVNStorageBuffers(program); 240 AddNVNStorageBuffers(program);
238 return program; 241 return program;
@@ -331,4 +334,82 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run
331 } 334 }
332} 335}
333 336
337IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
338 ObjectPool<IR::Block>& block_pool,
339 const HostTranslateInfo& host_info,
340 IR::Program& source_program,
341 Shader::OutputTopology output_topology) {
342 IR::Program program;
343 program.stage = Stage::Geometry;
344 program.output_topology = output_topology;
345 switch (output_topology) {
346 case OutputTopology::PointList:
347 program.output_vertices = 1;
348 break;
349 case OutputTopology::LineStrip:
350 program.output_vertices = 2;
351 break;
352 default:
353 program.output_vertices = 3;
354 break;
355 }
356
357 program.is_geometry_passthrough = false;
358 program.info.loads.mask = source_program.info.stores.mask;
359 program.info.stores.mask = source_program.info.stores.mask;
360 program.info.stores.Set(IR::Attribute::Layer, true);
361 program.info.stores.Set(source_program.info.emulated_layer, false);
362
363 IR::Block* current_block = block_pool.Create(inst_pool);
364 auto& node{program.syntax_list.emplace_back()};
365 node.type = IR::AbstractSyntaxNode::Type::Block;
366 node.data.block = current_block;
367
368 IR::IREmitter ir{*current_block};
369 for (u32 i = 0; i < program.output_vertices; i++) {
370 // Assign generics from input
371 for (u32 j = 0; j < 32; j++) {
372 if (!program.info.stores.Generic(j)) {
373 continue;
374 }
375
376 const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
377 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
378 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
379 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
380 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
381 }
382
383 // Assign position from input
384 const IR::Attribute attr = IR::Attribute::PositionX;
385 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
386 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
387 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
388 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
389
390 // Assign layer
391 ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(source_program.info.emulated_layer),
392 ir.Imm32(0));
393
394 // Emit vertex
395 ir.EmitVertex(ir.Imm32(0));
396 }
397 ir.EndPrimitive(ir.Imm32(0));
398
399 IR::Block* return_block{block_pool.Create(inst_pool)};
400 IR::IREmitter{*return_block}.Epilogue();
401 current_block->AddBranch(return_block);
402
403 auto& merge{program.syntax_list.emplace_back()};
404 merge.type = IR::AbstractSyntaxNode::Type::Block;
405 merge.data.block = return_block;
406 program.syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return;
407
408 program.blocks = GenerateBlocks(program.syntax_list);
409 program.post_order_blocks = PostOrder(program.syntax_list.front());
410 Optimization::SsaRewritePass(program);
411
412 return program;
413}
414
334} // namespace Shader::Maxwell 415} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h
index 02ede8c9c..497afe7cb 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.h
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.h
@@ -25,4 +25,13 @@ namespace Shader::Maxwell {
25 25
26void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info); 26void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info);
27 27
28// Maxwell v1 and older Nvidia cards don't support setting gl_Layer from non-geometry stages.
29// This creates a workaround by setting the layer as a generic output and creating a
30// passthrough geometry shader that reads the generic and sets the layer.
31[[nodiscard]] IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
32 ObjectPool<IR::Block>& block_pool,
33 const HostTranslateInfo& host_info,
34 IR::Program& source_program,
35 Shader::OutputTopology output_topology);
36
28} // namespace Shader::Maxwell 37} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index cc1500690..d5d279554 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -13,7 +13,8 @@ struct HostTranslateInfo {
13 bool support_float16{}; ///< True when the device supports 16-bit floats 13 bool support_float16{}; ///< True when the device supports 16-bit floats
14 bool support_int64{}; ///< True when the device supports 64-bit integers 14 bool support_int64{}; ///< True when the device supports 64-bit integers
15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered 15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers 16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
17 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
17}; 18};
18 19
19} // namespace Shader 20} // namespace Shader
diff --git a/src/shader_recompiler/ir_opt/layer_pass.cpp b/src/shader_recompiler/ir_opt/layer_pass.cpp
new file mode 100644
index 000000000..4574f7cf2
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/layer_pass.cpp
@@ -0,0 +1,68 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <bit>
6#include <optional>
7
8#include <boost/container/small_vector.hpp>
9
10#include "shader_recompiler/environment.h"
11#include "shader_recompiler/frontend/ir/basic_block.h"
12#include "shader_recompiler/frontend/ir/breadth_first_search.h"
13#include "shader_recompiler/frontend/ir/ir_emitter.h"
14#include "shader_recompiler/host_translate_info.h"
15#include "shader_recompiler/ir_opt/passes.h"
16#include "shader_recompiler/shader_info.h"
17
18namespace Shader::Optimization {
19
20static IR::Attribute EmulatedLayerAttribute(VaryingState& stores) {
21 for (u32 i = 0; i < 32; i++) {
22 if (!stores.Generic(i)) {
23 return IR::Attribute::Generic0X + (i * 4);
24 }
25 }
26 return IR::Attribute::Layer;
27}
28
29static bool PermittedProgramStage(Stage stage) {
30 switch (stage) {
31 case Stage::VertexA:
32 case Stage::VertexB:
33 case Stage::TessellationControl:
34 case Stage::TessellationEval:
35 return true;
36 default:
37 return false;
38 }
39}
40
41void LayerPass(IR::Program& program, const HostTranslateInfo& host_info) {
42 if (host_info.support_viewport_index_layer || !PermittedProgramStage(program.stage)) {
43 return;
44 }
45
46 const auto end{program.post_order_blocks.end()};
47 const auto layer_attribute = EmulatedLayerAttribute(program.info.stores);
48 bool requires_layer_emulation = false;
49
50 for (auto block = program.post_order_blocks.begin(); block != end; ++block) {
51 for (IR::Inst& inst : (*block)->Instructions()) {
52 if (inst.GetOpcode() == IR::Opcode::SetAttribute &&
53 inst.Arg(0).Attribute() == IR::Attribute::Layer) {
54 requires_layer_emulation = true;
55 inst.SetArg(0, IR::Value{layer_attribute});
56 }
57 }
58 }
59
60 if (requires_layer_emulation) {
61 program.info.requires_layer_emulation = true;
62 program.info.emulated_layer = layer_attribute;
63 program.info.stores.Set(IR::Attribute::Layer, false);
64 program.info.stores.Set(layer_attribute, true);
65 }
66}
67
68} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 586a0668f..11bfe801a 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -23,6 +23,7 @@ void RescalingPass(IR::Program& program);
23void SsaRewritePass(IR::Program& program); 23void SsaRewritePass(IR::Program& program);
24void PositionPass(Environment& env, IR::Program& program); 24void PositionPass(Environment& env, IR::Program& program);
25void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); 25void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
26void LayerPass(IR::Program& program, const HostTranslateInfo& host_info);
26void VerificationPass(const IR::Program& program); 27void VerificationPass(const IR::Program& program);
27 28
28// Dual Vertex 29// Dual Vertex
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index ee6252bb5..d9c6e92db 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -204,6 +204,9 @@ struct Info {
204 u32 nvn_buffer_base{}; 204 u32 nvn_buffer_base{};
205 std::bitset<16> nvn_buffer_used{}; 205 std::bitset<16> nvn_buffer_used{};
206 206
207 bool requires_layer_emulation{};
208 IR::Attribute emulated_layer{};
209
207 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS> 210 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
208 constant_buffer_descriptors; 211 constant_buffer_descriptors;
209 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors; 212 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;
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 3e823f563..34bbc72cf 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -377,11 +377,11 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
377 ProcessCBMultiData(base_start, amount); 377 ProcessCBMultiData(base_start, amount);
378 break; 378 break;
379 case MAXWELL3D_REG_INDEX(inline_data): 379 case MAXWELL3D_REG_INDEX(inline_data):
380 upload_state.ProcessData(base_start, static_cast<size_t>(amount)); 380 upload_state.ProcessData(base_start, amount);
381 return; 381 return;
382 default: 382 default:
383 for (std::size_t i = 0; i < amount; i++) { 383 for (u32 i = 0; i < amount; i++) {
384 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 384 CallMethod(method, base_start[i], methods_pending - i <= 1);
385 } 385 }
386 break; 386 break;
387 } 387 }
@@ -648,12 +648,12 @@ void Maxwell3D::ProcessDeferredDraw() {
648 return; 648 return;
649 } 649 }
650 650
651 u32 method_count = static_cast<u32>(deferred_draw_method.size()); 651 const auto method_count = deferred_draw_method.size();
652 u32 instance_count = 1; 652 u32 instance_count = 1;
653 u32 vertex_buffer_count = 0; 653 u32 vertex_buffer_count = 0;
654 u32 index_buffer_count = 0; 654 u32 index_buffer_count = 0;
655 for (u32 index = 0; index < method_count; ++index) { 655 for (size_t index = 0; index < method_count; ++index) {
656 u32 method = deferred_draw_method[index]; 656 const u32 method = deferred_draw_method[index];
657 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) { 657 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
658 instance_count = ++vertex_buffer_count; 658 instance_count = ++vertex_buffer_count;
659 } else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) { 659 } 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 fa9910a27..a541cd95f 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/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 3fe04a115..a38060100 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -39,6 +39,7 @@ using Shader::Backend::GLASM::EmitGLASM;
39using Shader::Backend::GLSL::EmitGLSL; 39using Shader::Backend::GLSL::EmitGLSL;
40using Shader::Backend::SPIRV::EmitSPIRV; 40using Shader::Backend::SPIRV::EmitSPIRV;
41using Shader::Maxwell::ConvertLegacyToGeneric; 41using Shader::Maxwell::ConvertLegacyToGeneric;
42using Shader::Maxwell::GenerateGeometryPassthrough;
42using Shader::Maxwell::MergeDualVertexPrograms; 43using Shader::Maxwell::MergeDualVertexPrograms;
43using Shader::Maxwell::TranslateProgram; 44using Shader::Maxwell::TranslateProgram;
44using VideoCommon::ComputeEnvironment; 45using VideoCommon::ComputeEnvironment;
@@ -56,6 +57,17 @@ auto MakeSpan(Container& container) {
56 return std::span(container.data(), container.size()); 57 return std::span(container.data(), container.size());
57} 58}
58 59
60Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) {
61 switch (topology) {
62 case Maxwell::PrimitiveTopology::Points:
63 return Shader::OutputTopology::PointList;
64 case Maxwell::PrimitiveTopology::LineStrip:
65 return Shader::OutputTopology::LineStrip;
66 default:
67 return Shader::OutputTopology::TriangleStrip;
68 }
69}
70
59Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, 71Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
60 const Shader::IR::Program& program, 72 const Shader::IR::Program& program,
61 const Shader::IR::Program* previous_program, 73 const Shader::IR::Program* previous_program,
@@ -220,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
220 .support_int64 = device.HasShaderInt64(), 232 .support_int64 = device.HasShaderInt64(),
221 .needs_demote_reorder = device.IsAmd(), 233 .needs_demote_reorder = device.IsAmd(),
222 .support_snorm_render_buffer = false, 234 .support_snorm_render_buffer = false,
235 .support_viewport_index_layer = device.HasVertexViewportLayer(),
223 } { 236 } {
224 if (use_asynchronous_shaders) { 237 if (use_asynchronous_shaders) {
225 workers = CreateWorkers(); 238 workers = CreateWorkers();
@@ -314,9 +327,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
314 const auto& regs{maxwell3d->regs}; 327 const auto& regs{maxwell3d->regs};
315 graphics_key.raw = 0; 328 graphics_key.raw = 0;
316 graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0); 329 graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0);
317 graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0 330 graphics_key.gs_input_topology.Assign(regs.draw.topology.Value());
318 ? regs.draw.topology.Value()
319 : Maxwell::PrimitiveTopology{});
320 graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value()); 331 graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value());
321 graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value()); 332 graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value());
322 graphics_key.tessellation_clockwise.Assign( 333 graphics_key.tessellation_clockwise.Assign(
@@ -415,7 +426,19 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
415 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; 426 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
416 const bool uses_vertex_a{key.unique_hashes[0] != 0}; 427 const bool uses_vertex_a{key.unique_hashes[0] != 0};
417 const bool uses_vertex_b{key.unique_hashes[1] != 0}; 428 const bool uses_vertex_b{key.unique_hashes[1] != 0};
429
430 // Layer passthrough generation for devices without GL_ARB_shader_viewport_layer_array
431 Shader::IR::Program* layer_source_program{};
432
418 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 433 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
434 const bool is_emulated_stage = layer_source_program != nullptr &&
435 index == static_cast<u32>(Maxwell::ShaderType::Geometry);
436 if (key.unique_hashes[index] == 0 && is_emulated_stage) {
437 auto topology = MaxwellToOutputTopology(key.gs_input_topology);
438 programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
439 *layer_source_program, topology);
440 continue;
441 }
419 if (key.unique_hashes[index] == 0) { 442 if (key.unique_hashes[index] == 0) {
420 continue; 443 continue;
421 } 444 }
@@ -443,6 +466,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
443 Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); 466 Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors);
444 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); 467 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
445 } 468 }
469
470 if (programs[index].info.requires_layer_emulation) {
471 layer_source_program = &programs[index];
472 }
446 } 473 }
447 const u32 glasm_storage_buffer_limit{device.GetMaxGLASMStorageBufferBlocks()}; 474 const u32 glasm_storage_buffer_limit{device.GetMaxGLASMStorageBufferBlocks()};
448 const bool glasm_use_storage_buffers{total_storage_buffers <= glasm_storage_buffer_limit}; 475 const bool glasm_use_storage_buffers{total_storage_buffers <= glasm_storage_buffer_limit};
@@ -456,7 +483,9 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
456 const bool use_glasm{device.UseAssemblyShaders()}; 483 const bool use_glasm{device.UseAssemblyShaders()};
457 const size_t first_index = uses_vertex_a && uses_vertex_b ? 1 : 0; 484 const size_t first_index = uses_vertex_a && uses_vertex_b ? 1 : 0;
458 for (size_t index = first_index; index < Maxwell::MaxShaderProgram; ++index) { 485 for (size_t index = first_index; index < Maxwell::MaxShaderProgram; ++index) {
459 if (key.unique_hashes[index] == 0) { 486 const bool is_emulated_stage = layer_source_program != nullptr &&
487 index == static_cast<u32>(Maxwell::ShaderType::Geometry);
488 if (key.unique_hashes[index] == 0 && !is_emulated_stage) {
460 continue; 489 continue;
461 } 490 }
462 UNIMPLEMENTED_IF(index == 0); 491 UNIMPLEMENTED_IF(index == 0);
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index d4b0a542a..29da442fa 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -46,6 +46,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache);
46namespace { 46namespace {
47using Shader::Backend::SPIRV::EmitSPIRV; 47using Shader::Backend::SPIRV::EmitSPIRV;
48using Shader::Maxwell::ConvertLegacyToGeneric; 48using Shader::Maxwell::ConvertLegacyToGeneric;
49using Shader::Maxwell::GenerateGeometryPassthrough;
49using Shader::Maxwell::MergeDualVertexPrograms; 50using Shader::Maxwell::MergeDualVertexPrograms;
50using Shader::Maxwell::TranslateProgram; 51using Shader::Maxwell::TranslateProgram;
51using VideoCommon::ComputeEnvironment; 52using VideoCommon::ComputeEnvironment;
@@ -53,13 +54,24 @@ using VideoCommon::FileEnvironment;
53using VideoCommon::GenericEnvironment; 54using VideoCommon::GenericEnvironment;
54using VideoCommon::GraphicsEnvironment; 55using VideoCommon::GraphicsEnvironment;
55 56
56constexpr u32 CACHE_VERSION = 7; 57constexpr u32 CACHE_VERSION = 8;
57 58
58template <typename Container> 59template <typename Container>
59auto MakeSpan(Container& container) { 60auto MakeSpan(Container& container) {
60 return std::span(container.data(), container.size()); 61 return std::span(container.data(), container.size());
61} 62}
62 63
64Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) {
65 switch (topology) {
66 case Maxwell::PrimitiveTopology::Points:
67 return Shader::OutputTopology::PointList;
68 case Maxwell::PrimitiveTopology::LineStrip:
69 return Shader::OutputTopology::LineStrip;
70 default:
71 return Shader::OutputTopology::TriangleStrip;
72 }
73}
74
63Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { 75Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) {
64 switch (comparison) { 76 switch (comparison) {
65 case Maxwell::ComparisonOp::Never_D3D: 77 case Maxwell::ComparisonOp::Never_D3D:
@@ -277,7 +289,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
277 const auto& float_control{device.FloatControlProperties()}; 289 const auto& float_control{device.FloatControlProperties()};
278 const VkDriverIdKHR driver_id{device.GetDriverID()}; 290 const VkDriverIdKHR driver_id{device.GetDriverID()};
279 profile = Shader::Profile{ 291 profile = Shader::Profile{
280 .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, 292 .supported_spirv = device.SupportedSpirvVersion(),
281 .unified_descriptor_binding = true, 293 .unified_descriptor_binding = true,
282 .support_descriptor_aliasing = true, 294 .support_descriptor_aliasing = true,
283 .support_int8 = device.IsInt8Supported(), 295 .support_int8 = device.IsInt8Supported(),
@@ -327,6 +339,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
327 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || 339 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR ||
328 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 340 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR,
329 .support_snorm_render_buffer = true, 341 .support_snorm_render_buffer = true,
342 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
330 }; 343 };
331} 344}
332 345
@@ -509,7 +522,19 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
509 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; 522 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
510 const bool uses_vertex_a{key.unique_hashes[0] != 0}; 523 const bool uses_vertex_a{key.unique_hashes[0] != 0};
511 const bool uses_vertex_b{key.unique_hashes[1] != 0}; 524 const bool uses_vertex_b{key.unique_hashes[1] != 0};
525
526 // Layer passthrough generation for devices without VK_EXT_shader_viewport_index_layer
527 Shader::IR::Program* layer_source_program{};
528
512 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 529 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
530 const bool is_emulated_stage = layer_source_program != nullptr &&
531 index == static_cast<u32>(Maxwell::ShaderType::Geometry);
532 if (key.unique_hashes[index] == 0 && is_emulated_stage) {
533 auto topology = MaxwellToOutputTopology(key.state.topology);
534 programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
535 *layer_source_program, topology);
536 continue;
537 }
513 if (key.unique_hashes[index] == 0) { 538 if (key.unique_hashes[index] == 0) {
514 continue; 539 continue;
515 } 540 }
@@ -530,6 +555,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
530 auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; 555 auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
531 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); 556 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
532 } 557 }
558
559 if (programs[index].info.requires_layer_emulation) {
560 layer_source_program = &programs[index];
561 }
533 } 562 }
534 std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{}; 563 std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{};
535 std::array<vk::ShaderModule, Maxwell::MaxShaderStage> modules; 564 std::array<vk::ShaderModule, Maxwell::MaxShaderStage> modules;
@@ -538,7 +567,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
538 Shader::Backend::Bindings binding; 567 Shader::Backend::Bindings binding;
539 for (size_t index = uses_vertex_a && uses_vertex_b ? 1 : 0; index < Maxwell::MaxShaderProgram; 568 for (size_t index = uses_vertex_a && uses_vertex_b ? 1 : 0; index < Maxwell::MaxShaderProgram;
540 ++index) { 569 ++index) {
541 if (key.unique_hashes[index] == 0) { 570 const bool is_emulated_stage = layer_source_program != nullptr &&
571 index == static_cast<u32>(Maxwell::ShaderType::Geometry);
572 if (key.unique_hashes[index] == 0 && !is_emulated_stage) {
542 continue; 573 continue;
543 } 574 }
544 UNIMPLEMENTED_IF(index == 0); 575 UNIMPLEMENTED_IF(index == 0);
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/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index ddecfca13..a16a8275b 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -74,23 +74,14 @@ enum class NvidiaArchitecture {
74}; 74};
75 75
76constexpr std::array REQUIRED_EXTENSIONS{ 76constexpr std::array REQUIRED_EXTENSIONS{
77 VK_KHR_MAINTENANCE1_EXTENSION_NAME,
78 VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME,
79 VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME,
80 VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
81 VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
82 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
83 VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
84 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
85 VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
86 VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
87 VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME,
88 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, 77 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
89 VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
90 VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
91 VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, 78 VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
79
80 // Core in 1.2, but required due to use of extension methods,
81 // and well-supported by drivers
82 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
83 VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
92 VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, 84 VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
93 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME,
94#ifdef _WIN32 85#ifdef _WIN32
95 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, 86 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
96#endif 87#endif
@@ -99,6 +90,17 @@ constexpr std::array REQUIRED_EXTENSIONS{
99#endif 90#endif
100}; 91};
101 92
93constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_2{
94 VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
95 VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
96 VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
97 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
98};
99
100constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_3{
101 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME,
102};
103
102template <typename T> 104template <typename T>
103void SetNext(void**& next, T& data) { 105void SetNext(void**& next, T& data) {
104 *next = &data; 106 *next = &data;
@@ -327,7 +329,8 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
327Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, 329Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
328 const vk::InstanceDispatch& dld_) 330 const vk::InstanceDispatch& dld_)
329 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, 331 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()},
330 supported_extensions{GetSupportedExtensions(physical)}, 332 instance_version{properties.apiVersion}, supported_extensions{GetSupportedExtensions(
333 physical)},
331 format_properties(GetFormatProperties(physical)) { 334 format_properties(GetFormatProperties(physical)) {
332 CheckSuitability(surface != nullptr); 335 CheckSuitability(surface != nullptr);
333 SetupFamilies(surface); 336 SetupFamilies(surface);
@@ -451,8 +454,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
451 }; 454 };
452 SetNext(next, variable_pointers); 455 SetNext(next, variable_pointers);
453 456
454 VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{ 457 VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{
455 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT, 458 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES,
456 .pNext = nullptr, 459 .pNext = nullptr,
457 .shaderDemoteToHelperInvocation = true, 460 .shaderDemoteToHelperInvocation = true,
458 }; 461 };
@@ -896,28 +899,51 @@ std::string Device::GetDriverName() const {
896 } 899 }
897} 900}
898 901
902static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) {
903 std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()};
904
905 if (available_version < VK_API_VERSION_1_2) {
906 extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_2.begin(),
907 REQUIRED_EXTENSIONS_BEFORE_1_2.end());
908 }
909
910 if (available_version < VK_API_VERSION_1_3) {
911 extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_3.begin(),
912 REQUIRED_EXTENSIONS_BEFORE_1_3.end());
913 }
914
915 return extensions;
916}
917
899void Device::CheckSuitability(bool requires_swapchain) const { 918void Device::CheckSuitability(bool requires_swapchain) const {
900 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; 919 std::vector<const char*> required_extensions =
901 bool has_swapchain = false; 920 ExtensionsRequiredForInstanceVersion(instance_version);
902 for (const VkExtensionProperties& property : physical.EnumerateDeviceExtensionProperties()) { 921 std::vector<const char*> available_extensions;
903 const std::string_view name{property.extensionName}; 922
904 for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { 923 if (requires_swapchain) {
905 if (available_extensions[i]) { 924 required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
906 continue;
907 }
908 available_extensions[i] = name == REQUIRED_EXTENSIONS[i];
909 }
910 has_swapchain = has_swapchain || name == VK_KHR_SWAPCHAIN_EXTENSION_NAME;
911 } 925 }
912 for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { 926
913 if (available_extensions[i]) { 927 auto extension_properties = physical.EnumerateDeviceExtensionProperties();
914 continue; 928
929 for (const VkExtensionProperties& property : extension_properties) {
930 available_extensions.push_back(property.extensionName);
931 }
932
933 bool has_all_required_extensions = true;
934 for (const char* requirement_name : required_extensions) {
935 const bool found =
936 std::ranges::any_of(available_extensions, [&](const char* extension_name) {
937 return std::strcmp(requirement_name, extension_name) == 0;
938 });
939
940 if (!found) {
941 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", requirement_name);
942 has_all_required_extensions = false;
915 } 943 }
916 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]);
917 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
918 } 944 }
919 if (requires_swapchain && !has_swapchain) { 945
920 LOG_ERROR(Render_Vulkan, "Missing required extension: VK_KHR_swapchain"); 946 if (!has_all_required_extensions) {
921 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); 947 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
922 } 948 }
923 949
@@ -940,9 +966,8 @@ void Device::CheckSuitability(bool requires_swapchain) const {
940 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); 966 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
941 } 967 }
942 } 968 }
943 VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{}; 969 VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{};
944 demote.sType = 970 demote.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES;
945 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT;
946 demote.pNext = nullptr; 971 demote.pNext = nullptr;
947 972
948 VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{}; 973 VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{};
@@ -960,7 +985,7 @@ void Device::CheckSuitability(bool requires_swapchain) const {
960 physical.GetFeatures2KHR(features2); 985 physical.GetFeatures2KHR(features2);
961 986
962 const VkPhysicalDeviceFeatures& features{features2.features}; 987 const VkPhysicalDeviceFeatures& features{features2.features};
963 const std::array feature_report{ 988 std::vector feature_report{
964 std::make_pair(features.robustBufferAccess, "robustBufferAccess"), 989 std::make_pair(features.robustBufferAccess, "robustBufferAccess"),
965 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), 990 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
966 std::make_pair(features.imageCubeArray, "imageCubeArray"), 991 std::make_pair(features.imageCubeArray, "imageCubeArray"),
@@ -983,27 +1008,30 @@ void Device::CheckSuitability(bool requires_swapchain) const {
983 "shaderStorageImageWriteWithoutFormat"), 1008 "shaderStorageImageWriteWithoutFormat"),
984 std::make_pair(features.shaderClipDistance, "shaderClipDistance"), 1009 std::make_pair(features.shaderClipDistance, "shaderClipDistance"),
985 std::make_pair(features.shaderCullDistance, "shaderCullDistance"), 1010 std::make_pair(features.shaderCullDistance, "shaderCullDistance"),
986 std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"),
987 std::make_pair(variable_pointers.variablePointers, "variablePointers"), 1011 std::make_pair(variable_pointers.variablePointers, "variablePointers"),
988 std::make_pair(variable_pointers.variablePointersStorageBuffer, 1012 std::make_pair(variable_pointers.variablePointersStorageBuffer,
989 "variablePointersStorageBuffer"), 1013 "variablePointersStorageBuffer"),
990 std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"), 1014 std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"),
991 std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"), 1015 std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"),
992 std::make_pair(robustness2.nullDescriptor, "nullDescriptor"), 1016 std::make_pair(robustness2.nullDescriptor, "nullDescriptor"),
1017 std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"),
993 }; 1018 };
1019
1020 bool has_all_required_features = true;
994 for (const auto& [is_supported, name] : feature_report) { 1021 for (const auto& [is_supported, name] : feature_report) {
995 if (is_supported) { 1022 if (!is_supported) {
996 continue; 1023 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name);
1024 has_all_required_features = false;
997 } 1025 }
998 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); 1026 }
1027
1028 if (!has_all_required_features) {
999 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); 1029 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
1000 } 1030 }
1001} 1031}
1002 1032
1003std::vector<const char*> Device::LoadExtensions(bool requires_surface) { 1033std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1004 std::vector<const char*> extensions; 1034 std::vector<const char*> extensions = ExtensionsRequiredForInstanceVersion(instance_version);
1005 extensions.reserve(8 + REQUIRED_EXTENSIONS.size());
1006 extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end());
1007 if (requires_surface) { 1035 if (requires_surface) {
1008 extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); 1036 extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
1009 } 1037 }
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index d7cc6c593..c85fbba77 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -211,11 +211,6 @@ public:
211 return khr_uniform_buffer_standard_layout; 211 return khr_uniform_buffer_standard_layout;
212 } 212 }
213 213
214 /// Returns true if the device supports VK_KHR_spirv_1_4.
215 bool IsKhrSpirv1_4Supported() const {
216 return khr_spirv_1_4;
217 }
218
219 /// Returns true if the device supports VK_KHR_push_descriptor. 214 /// Returns true if the device supports VK_KHR_push_descriptor.
220 bool IsKhrPushDescriptorSupported() const { 215 bool IsKhrPushDescriptorSupported() const {
221 return khr_push_descriptor; 216 return khr_push_descriptor;
@@ -316,6 +311,17 @@ public:
316 return ext_shader_atomic_int64; 311 return ext_shader_atomic_int64;
317 } 312 }
318 313
314 /// Returns the minimum supported version of SPIR-V.
315 u32 SupportedSpirvVersion() const {
316 if (instance_version >= VK_API_VERSION_1_3) {
317 return 0x00010600U;
318 }
319 if (khr_spirv_1_4) {
320 return 0x00010400U;
321 }
322 return 0x00010000U;
323 }
324
319 /// Returns true when a known debugging tool is attached. 325 /// Returns true when a known debugging tool is attached.
320 bool HasDebuggingToolAttached() const { 326 bool HasDebuggingToolAttached() const {
321 return has_renderdoc || has_nsight_graphics; 327 return has_renderdoc || has_nsight_graphics;
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index a082e3059..35e073e16 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -14,13 +14,15 @@
14#include "video_core/vulkan_common/vulkan_wrapper.h" 14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
16// Include these late to avoid polluting previous headers 16// Include these late to avoid polluting previous headers
17#ifdef _WIN32 17#if defined(_WIN32)
18#include <windows.h> 18#include <windows.h>
19// ensure include order 19// ensure include order
20#include <vulkan/vulkan_win32.h> 20#include <vulkan/vulkan_win32.h>
21#endif 21#elif defined(__APPLE__)
22 22#include <vulkan/vulkan_macos.h>
23#if !defined(_WIN32) && !defined(__APPLE__) 23#elif defined(__ANDROID__)
24#include <vulkan/vulkan_android.h>
25#else
24#include <X11/Xlib.h> 26#include <X11/Xlib.h>
25#include <vulkan/vulkan_wayland.h> 27#include <vulkan/vulkan_wayland.h>
26#include <vulkan/vulkan_xlib.h> 28#include <vulkan/vulkan_xlib.h>
@@ -39,8 +41,15 @@ namespace {
39 case Core::Frontend::WindowSystemType::Windows: 41 case Core::Frontend::WindowSystemType::Windows:
40 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); 42 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
41 break; 43 break;
42#endif 44#elif defined(__APPLE__)
43#if !defined(_WIN32) && !defined(__APPLE__) 45 case Core::Frontend::WindowSystemType::Cocoa:
46 extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
47 break;
48#elif defined(__ANDROID__)
49 case Core::Frontend::WindowSystemType::Android:
50 extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
51 break;
52#else
44 case Core::Frontend::WindowSystemType::X11: 53 case Core::Frontend::WindowSystemType::X11:
45 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); 54 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
46 break; 55 break;
@@ -59,6 +68,10 @@ namespace {
59 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 68 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
60 } 69 }
61 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); 70 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
71
72#ifdef __APPLE__
73 extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
74#endif
62 return extensions; 75 return extensions;
63} 76}
64 77
@@ -140,7 +153,7 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
140 } 153 }
141 vk::Instance instance = 154 vk::Instance instance =
142 std::async([&] { 155 std::async([&] {
143 return vk::Instance::Create(required_version, layers, extensions, dld); 156 return vk::Instance::Create(available_version, layers, extensions, dld);
144 }).get(); 157 }).get();
145 if (!vk::Load(*instance, dld)) { 158 if (!vk::Load(*instance, dld)) {
146 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers"); 159 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers");
diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp
index 69f9c494b..fa9bafa20 100644
--- a/src/video_core/vulkan_common/vulkan_surface.cpp
+++ b/src/video_core/vulkan_common/vulkan_surface.cpp
@@ -11,9 +11,11 @@
11#include <windows.h> 11#include <windows.h>
12// ensure include order 12// ensure include order
13#include <vulkan/vulkan_win32.h> 13#include <vulkan/vulkan_win32.h>
14#endif 14#elif defined(__APPLE__)
15 15#include <vulkan/vulkan_macos.h>
16#if !defined(_WIN32) && !defined(__APPLE__) 16#elif defined(__ANDROID__)
17#include <vulkan/vulkan_android.h>
18#else
17#include <X11/Xlib.h> 19#include <X11/Xlib.h>
18#include <vulkan/vulkan_wayland.h> 20#include <vulkan/vulkan_wayland.h>
19#include <vulkan/vulkan_xlib.h> 21#include <vulkan/vulkan_xlib.h>
@@ -40,8 +42,33 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
40 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 42 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
41 } 43 }
42 } 44 }
43#endif 45#elif defined(__APPLE__)
44#if !defined(_WIN32) && !defined(__APPLE__) 46 if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) {
47 const VkMacOSSurfaceCreateInfoMVK mvk_ci{VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK,
48 nullptr, 0, window_info.render_surface};
49 const auto vkCreateMacOSSurfaceMVK = reinterpret_cast<PFN_vkCreateMacOSSurfaceMVK>(
50 dld.vkGetInstanceProcAddr(*instance, "vkCreateMacOSSurfaceMVK"));
51 if (!vkCreateMacOSSurfaceMVK ||
52 vkCreateMacOSSurfaceMVK(*instance, &mvk_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
53 LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface");
54 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
55 }
56 }
57#elif defined(__ANDROID__)
58 if (window_info.type == Core::Frontend::WindowSystemType::Android) {
59 const VkAndroidSurfaceCreateInfoKHR android_ci{
60 VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, nullptr, 0,
61 reinterpret_cast<ANativeWindow*>(window_info.render_surface)};
62 const auto vkCreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(
63 dld.vkGetInstanceProcAddr(*instance, "vkCreateAndroidSurfaceKHR"));
64 if (!vkCreateAndroidSurfaceKHR ||
65 vkCreateAndroidSurfaceKHR(*instance, &android_ci, nullptr, &unsafe_surface) !=
66 VK_SUCCESS) {
67 LOG_ERROR(Render_Vulkan, "Failed to initialize Android surface");
68 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
69 }
70 }
71#else
45 if (window_info.type == Core::Frontend::WindowSystemType::X11) { 72 if (window_info.type == Core::Frontend::WindowSystemType::X11) {
46 const VkXlibSurfaceCreateInfoKHR xlib_ci{ 73 const VkXlibSurfaceCreateInfoKHR xlib_ci{
47 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, 74 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0,
@@ -70,6 +97,7 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
70 } 97 }
71 } 98 }
72#endif 99#endif
100
73 if (!unsafe_surface) { 101 if (!unsafe_surface) {
74 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); 102 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
75 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 103 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
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/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index c934069dd..ccf1c50f4 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -267,6 +267,10 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() {
267 return Core::Frontend::WindowSystemType::X11; 267 return Core::Frontend::WindowSystemType::X11;
268 else if (platform_name == QStringLiteral("wayland")) 268 else if (platform_name == QStringLiteral("wayland"))
269 return Core::Frontend::WindowSystemType::Wayland; 269 return Core::Frontend::WindowSystemType::Wayland;
270 else if (platform_name == QStringLiteral("cocoa"))
271 return Core::Frontend::WindowSystemType::Cocoa;
272 else if (platform_name == QStringLiteral("android"))
273 return Core::Frontend::WindowSystemType::Android;
270 274
271 LOG_CRITICAL(Frontend, "Unknown Qt platform!"); 275 LOG_CRITICAL(Frontend, "Unknown Qt platform!");
272 return Core::Frontend::WindowSystemType::Windows; 276 return Core::Frontend::WindowSystemType::Windows;
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index f1385e972..20bc651f1 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -360,7 +360,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
360 360
361 vk::InstanceDispatch dld; 361 vk::InstanceDispatch dld;
362 const Common::DynamicLibrary library = OpenLibrary(); 362 const Common::DynamicLibrary library = OpenLibrary();
363 const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_0); 363 const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1);
364 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices(); 364 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
365 365
366 vulkan_devices.clear(); 366 vulkan_devices.clear();
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 2aae746f0..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();
@@ -4037,7 +4049,6 @@ void GMainWindow::UpdateUITheme() {
4037 const QString default_theme = 4049 const QString default_theme =
4038 QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second); 4050 QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second);
4039 QString current_theme = UISettings::values.theme; 4051 QString current_theme = UISettings::values.theme;
4040 QStringList theme_paths(default_theme_paths);
4041 4052
4042 if (current_theme.isEmpty()) { 4053 if (current_theme.isEmpty()) {
4043 current_theme = default_theme; 4054 current_theme = default_theme;
@@ -4050,7 +4061,7 @@ void GMainWindow::UpdateUITheme() {
4050 if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) { 4061 if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) {
4051 QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme 4062 QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme
4052 : startup_icon_theme); 4063 : startup_icon_theme);
4053 QIcon::setThemeSearchPaths(theme_paths); 4064 QIcon::setThemeSearchPaths(QStringList(default_theme_paths));
4054 if (CheckDarkMode()) { 4065 if (CheckDarkMode()) {
4055 current_theme = QStringLiteral("default_dark"); 4066 current_theme = QStringLiteral("default_dark");
4056 } 4067 }
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..563818362 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"
@@ -24,10 +27,10 @@ void CheckVulkan() {
24 Vulkan::vk::InstanceDispatch dld; 27 Vulkan::vk::InstanceDispatch dld;
25 const Common::DynamicLibrary library = Vulkan::OpenLibrary(); 28 const Common::DynamicLibrary library = Vulkan::OpenLibrary();
26 const Vulkan::vk::Instance instance = 29 const Vulkan::vk::Instance instance =
27 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); 30 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1);
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
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index d6bea9aa8..59f9c8e09 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -90,7 +90,11 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>
90 90
91template <> 91template <>
92void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { 92void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
93 setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); 93 std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault());
94 if (setting_value.empty()) {
95 setting_value = setting.GetDefault();
96 }
97 setting = std::move(setting_value);
94} 98}
95 99
96template <> 100template <>
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index 25948328c..0d580fe4f 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -51,11 +51,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
51 window_info.type = Core::Frontend::WindowSystemType::Windows; 51 window_info.type = Core::Frontend::WindowSystemType::Windows;
52 window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window); 52 window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
53 break; 53 break;
54#else
55 case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
56 LOG_CRITICAL(Frontend, "Window manager subsystem Windows not compiled");
57 std::exit(EXIT_FAILURE);
58 break;
59#endif 54#endif
60#ifdef SDL_VIDEO_DRIVER_X11 55#ifdef SDL_VIDEO_DRIVER_X11
61 case SDL_SYSWM_TYPE::SDL_SYSWM_X11: 56 case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
@@ -63,11 +58,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
63 window_info.display_connection = wm.info.x11.display; 58 window_info.display_connection = wm.info.x11.display;
64 window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window); 59 window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
65 break; 60 break;
66#else
67 case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
68 LOG_CRITICAL(Frontend, "Window manager subsystem X11 not compiled");
69 std::exit(EXIT_FAILURE);
70 break;
71#endif 61#endif
72#ifdef SDL_VIDEO_DRIVER_WAYLAND 62#ifdef SDL_VIDEO_DRIVER_WAYLAND
73 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: 63 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
@@ -75,14 +65,21 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
75 window_info.display_connection = wm.info.wl.display; 65 window_info.display_connection = wm.info.wl.display;
76 window_info.render_surface = wm.info.wl.surface; 66 window_info.render_surface = wm.info.wl.surface;
77 break; 67 break;
78#else 68#endif
79 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: 69#ifdef SDL_VIDEO_DRIVER_COCOA
80 LOG_CRITICAL(Frontend, "Window manager subsystem Wayland not compiled"); 70 case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA:
81 std::exit(EXIT_FAILURE); 71 window_info.type = Core::Frontend::WindowSystemType::Cocoa;
72 window_info.render_surface = SDL_Metal_CreateView(render_window);
73 break;
74#endif
75#ifdef SDL_VIDEO_DRIVER_ANDROID
76 case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID:
77 window_info.type = Core::Frontend::WindowSystemType::Android;
78 window_info.render_surface = reinterpret_cast<void*>(wm.info.android.window);
82 break; 79 break;
83#endif 80#endif
84 default: 81 default:
85 LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); 82 LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem);
86 std::exit(EXIT_FAILURE); 83 std::exit(EXIT_FAILURE);
87 break; 84 break;
88 } 85 }