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/renderer/command/command_buffer.cpp14
-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/common/settings.cpp2
-rw-r--r--src/common/settings.h3
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/crypto/key_manager.cpp8
-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.cpp15
-rw-r--r--src/core/hle/kernel/hle_ipc.h62
-rw-r--r--src/core/hle/service/audio/audin_u.cpp10
-rw-r--r--src/core/hle/service/audio/audout_u.cpp10
-rw-r--r--src/core/hle/service/audio/audren_u.cpp6
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/bcat/bcat_module.cpp4
-rw-r--r--src/core/hle/service/es/es.cpp27
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp4
-rw-r--r--src/core/hle/service/ldn/ldn.cpp4
-rw-r--r--src/core/hle/service/nfc/nfc_user.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp6
-rw-r--r--src/core/hle/service/nifm/nifm.cpp41
-rw-r--r--src/core/hle/service/ns/iplatform_service_manager.cpp11
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.cpp46
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.h2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp44
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.h2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp2
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.cpp6
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.h16
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp3
-rw-r--r--src/core/hle/service/nvflinger/producer_listener.h1
-rw-r--r--src/core/hle/service/set/set.cpp2
-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.cpp110
-rw-r--r--src/input_common/main.h3
-rw-r--r--src/shader_recompiler/CMakeLists.txt1
-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/CMakeLists.txt4
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h4
-rw-r--r--src/video_core/control/channel_state.cpp2
-rw-r--r--src/video_core/engines/engine_upload.cpp11
-rw-r--r--src/video_core/engines/engine_upload.h2
-rw-r--r--src/video_core/engines/fermi_2d.cpp26
-rw-r--r--src/video_core/engines/fermi_2d.h11
-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.cpp132
-rw-r--r--src/video_core/engines/maxwell_3d.h87
-rw-r--r--src/video_core/engines/maxwell_dma.cpp81
-rw-r--r--src/video_core/engines/maxwell_dma.h3
-rw-r--r--src/video_core/engines/puller.cpp10
-rw-r--r--src/video_core/engines/sw_blitter/blitter.cpp238
-rw-r--r--src/video_core/engines/sw_blitter/blitter.h27
-rw-r--r--src/video_core/engines/sw_blitter/converter.cpp1234
-rw-r--r--src/video_core/engines/sw_blitter/converter.h36
-rw-r--r--src/video_core/engines/sw_blitter/generate_converters.py136
-rw-r--r--src/video_core/gpu.h18
-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_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp37
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h1
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp33
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp3
-rw-r--r--src/video_core/surface.cpp27
-rw-r--r--src/video_core/surface.h4
-rw-r--r--src/video_core/texture_cache/formatter.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h29
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h8
-rw-r--r--src/yuzu/CMakeLists.txt32
-rw-r--r--src/yuzu/bootmanager.cpp8
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu/configuration/configure_camera.cpp7
-rw-r--r--src/yuzu/configuration/configure_camera.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp38
-rw-r--r--src/yuzu/configuration/configure_graphics.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui146
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp4
-rw-r--r--src/yuzu/main.cpp18
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/main.ui9
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp2
-rw-r--r--src/yuzu/multiplayer/validation.h16
-rw-r--r--src/yuzu/startup_checks.cpp82
-rw-r--r--src/yuzu/startup_checks.h4
-rw-r--r--src/yuzu_cmd/config.cpp7
-rw-r--r--src/yuzu_cmd/default_ini.h29
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
107 files changed, 2960 insertions, 543 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/renderer/command/command_buffer.cpp b/src/audio_core/renderer/command/command_buffer.cpp
index 2ef879ee1..8c6fe97e7 100644
--- a/src/audio_core/renderer/command/command_buffer.cpp
+++ b/src/audio_core/renderer/command/command_buffer.cpp
@@ -460,21 +460,23 @@ void CommandBuffer::GenerateDeviceSinkCommand(const s32 node_id, const s16 buffe
460 460
461 cmd.session_id = session_id; 461 cmd.session_id = session_id;
462 462
463 cmd.input_count = parameter.input_count;
464 s16 max_input{0};
465 for (u32 i = 0; i < parameter.input_count; i++) {
466 cmd.inputs[i] = buffer_offset + parameter.inputs[i];
467 max_input = std::max(max_input, cmd.inputs[i]);
468 }
469
463 if (state.upsampler_info != nullptr) { 470 if (state.upsampler_info != nullptr) {
464 const auto size_{state.upsampler_info->sample_count * parameter.input_count}; 471 const auto size_{state.upsampler_info->sample_count * parameter.input_count};
465 const auto size_bytes{size_ * sizeof(s32)}; 472 const auto size_bytes{size_ * sizeof(s32)};
466 const auto addr{memory_pool->Translate(state.upsampler_info->samples_pos, size_bytes)}; 473 const auto addr{memory_pool->Translate(state.upsampler_info->samples_pos, size_bytes)};
467 cmd.sample_buffer = {reinterpret_cast<s32*>(addr), 474 cmd.sample_buffer = {reinterpret_cast<s32*>(addr),
468 parameter.input_count * state.upsampler_info->sample_count}; 475 (max_input + 1) * state.upsampler_info->sample_count};
469 } else { 476 } else {
470 cmd.sample_buffer = samples_buffer; 477 cmd.sample_buffer = samples_buffer;
471 } 478 }
472 479
473 cmd.input_count = parameter.input_count;
474 for (u32 i = 0; i < parameter.input_count; i++) {
475 cmd.inputs[i] = buffer_offset + parameter.inputs[i];
476 }
477
478 GenerateEnd<DeviceSinkCommand>(cmd); 480 GenerateEnd<DeviceSinkCommand>(cmd);
479} 481}
480 482
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/common/settings.cpp b/src/common/settings.cpp
index 8173462cb..d8ffe34c3 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -48,6 +48,7 @@ void LogSettings() {
48 log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); 48 log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
49 log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); 49 log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
50 log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue()); 50 log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue());
51 log_setting("Renderer_FSRSlider", values.fsr_sharpening_slider.GetValue());
51 log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue()); 52 log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue());
52 log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue()); 53 log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
53 log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue()); 54 log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
@@ -181,6 +182,7 @@ void RestoreGlobalState(bool is_powered_on) {
181 values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true); 182 values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
182 183
183 // Renderer 184 // Renderer
185 values.fsr_sharpening_slider.SetGlobal(true);
184 values.renderer_backend.SetGlobal(true); 186 values.renderer_backend.SetGlobal(true);
185 values.vulkan_device.SetGlobal(true); 187 values.vulkan_device.SetGlobal(true);
186 values.aspect_ratio.SetGlobal(true); 188 values.aspect_ratio.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 0eb98939c..00e4421f7 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -421,6 +421,7 @@ struct Values {
421 ResolutionScalingInfo resolution_info{}; 421 ResolutionScalingInfo resolution_info{};
422 SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; 422 SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
423 SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; 423 SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
424 SwitchableSetting<int, true> fsr_sharpening_slider{25, 0, 200, "fsr_sharpening_slider"};
424 SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; 425 SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
425 // *nix platforms may have issues with the borderless windowed fullscreen mode. 426 // *nix platforms may have issues with the borderless windowed fullscreen mode.
426 // Default to exclusive fullscreen on these platforms for now. 427 // Default to exclusive fullscreen on these platforms for now.
@@ -442,7 +443,7 @@ struct Values {
442 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; 443 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
443 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; 444 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
444 SwitchableSetting<bool> use_vsync{true, "use_vsync"}; 445 SwitchableSetting<bool> use_vsync{true, "use_vsync"};
445 SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, 446 SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
446 ShaderBackend::SPIRV, "shader_backend"}; 447 ShaderBackend::SPIRV, "shader_backend"};
447 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 448 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
448 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 449 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
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/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 443323390..65a9fe802 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -578,18 +578,18 @@ KeyManager::KeyManager() {
578 578
579 if (Settings::values.use_dev_keys) { 579 if (Settings::values.use_dev_keys) {
580 dev_mode = true; 580 dev_mode = true;
581 LoadFromFile(yuzu_keys_dir / "dev.keys", false);
582 LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false); 581 LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false);
582 LoadFromFile(yuzu_keys_dir / "dev.keys", false);
583 } else { 583 } else {
584 dev_mode = false; 584 dev_mode = false;
585 LoadFromFile(yuzu_keys_dir / "prod.keys", false);
586 LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false); 585 LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false);
586 LoadFromFile(yuzu_keys_dir / "prod.keys", false);
587 } 587 }
588 588
589 LoadFromFile(yuzu_keys_dir / "title.keys", true);
590 LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true); 589 LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true);
591 LoadFromFile(yuzu_keys_dir / "console.keys", false); 590 LoadFromFile(yuzu_keys_dir / "title.keys", true);
592 LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false); 591 LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false);
592 LoadFromFile(yuzu_keys_dir / "console.keys", false);
593} 593}
594 594
595static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { 595static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
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 8d367b546..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]) {
@@ -145,6 +145,7 @@ void EmulatedDevices::UnloadInput() {
145 for (auto& button : keyboard_modifier_devices) { 145 for (auto& button : keyboard_modifier_devices) {
146 button.reset(); 146 button.reset();
147 } 147 }
148 ring_analog_device.reset();
148} 149}
149 150
150void EmulatedDevices::EnableConfiguration() { 151void EmulatedDevices::EnableConfiguration() {
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index d87be72d6..e252b5f4b 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -199,7 +199,7 @@ public:
199 ~HLERequestContext(); 199 ~HLERequestContext();
200 200
201 /// Returns a pointer to the IPC command buffer for this request. 201 /// Returns a pointer to the IPC command buffer for this request.
202 u32* CommandBuffer() { 202 [[nodiscard]] u32* CommandBuffer() {
203 return cmd_buf.data(); 203 return cmd_buf.data();
204 } 204 }
205 205
@@ -207,7 +207,7 @@ public:
207 * Returns the session through which this request was made. This can be used as a map key to 207 * Returns the session through which this request was made. This can be used as a map key to
208 * access per-client data on services. 208 * access per-client data on services.
209 */ 209 */
210 Kernel::KServerSession* Session() { 210 [[nodiscard]] Kernel::KServerSession* Session() {
211 return server_session; 211 return server_session;
212 } 212 }
213 213
@@ -217,61 +217,61 @@ public:
217 /// Writes data from this context back to the requesting process/thread. 217 /// Writes data from this context back to the requesting process/thread.
218 Result WriteToOutgoingCommandBuffer(KThread& requesting_thread); 218 Result WriteToOutgoingCommandBuffer(KThread& requesting_thread);
219 219
220 u32_le GetHipcCommand() const { 220 [[nodiscard]] u32_le GetHipcCommand() const {
221 return command; 221 return command;
222 } 222 }
223 223
224 u32_le GetTipcCommand() const { 224 [[nodiscard]] u32_le GetTipcCommand() const {
225 return static_cast<u32_le>(command_header->type.Value()) - 225 return static_cast<u32_le>(command_header->type.Value()) -
226 static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion); 226 static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion);
227 } 227 }
228 228
229 u32_le GetCommand() const { 229 [[nodiscard]] u32_le GetCommand() const {
230 return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand(); 230 return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand();
231 } 231 }
232 232
233 bool IsTipc() const { 233 [[nodiscard]] bool IsTipc() const {
234 return command_header->IsTipc(); 234 return command_header->IsTipc();
235 } 235 }
236 236
237 IPC::CommandType GetCommandType() const { 237 [[nodiscard]] IPC::CommandType GetCommandType() const {
238 return command_header->type; 238 return command_header->type;
239 } 239 }
240 240
241 u64 GetPID() const { 241 [[nodiscard]] u64 GetPID() const {
242 return pid; 242 return pid;
243 } 243 }
244 244
245 u32 GetDataPayloadOffset() const { 245 [[nodiscard]] u32 GetDataPayloadOffset() const {
246 return data_payload_offset; 246 return data_payload_offset;
247 } 247 }
248 248
249 const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const { 249 [[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
250 return buffer_x_desciptors; 250 return buffer_x_desciptors;
251 } 251 }
252 252
253 const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const { 253 [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
254 return buffer_a_desciptors; 254 return buffer_a_desciptors;
255 } 255 }
256 256
257 const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const { 257 [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
258 return buffer_b_desciptors; 258 return buffer_b_desciptors;
259 } 259 }
260 260
261 const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const { 261 [[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
262 return buffer_c_desciptors; 262 return buffer_c_desciptors;
263 } 263 }
264 264
265 const IPC::DomainMessageHeader& GetDomainMessageHeader() const { 265 [[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
266 return domain_message_header.value(); 266 return domain_message_header.value();
267 } 267 }
268 268
269 bool HasDomainMessageHeader() const { 269 [[nodiscard]] bool HasDomainMessageHeader() const {
270 return domain_message_header.has_value(); 270 return domain_message_header.has_value();
271 } 271 }
272 272
273 /// Helper function to read a buffer using the appropriate buffer descriptor 273 /// Helper function to read a buffer using the appropriate buffer descriptor
274 std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const; 274 [[nodiscard]] std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
275 275
276 /// Helper function to write a buffer using the appropriate buffer descriptor 276 /// Helper function to write a buffer using the appropriate buffer descriptor
277 std::size_t WriteBuffer(const void* buffer, std::size_t size, 277 std::size_t WriteBuffer(const void* buffer, std::size_t size,
@@ -308,22 +308,34 @@ public:
308 } 308 }
309 309
310 /// Helper function to get the size of the input buffer 310 /// Helper function to get the size of the input buffer
311 std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const; 311 [[nodiscard]] std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
312 312
313 /// Helper function to get the size of the output buffer 313 /// Helper function to get the size of the output buffer
314 std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const; 314 [[nodiscard]] std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
315
316 /// Helper function to derive the number of elements able to be contained in the read buffer
317 template <typename T>
318 [[nodiscard]] std::size_t GetReadBufferNumElements(std::size_t buffer_index = 0) const {
319 return GetReadBufferSize(buffer_index) / sizeof(T);
320 }
321
322 /// Helper function to derive the number of elements able to be contained in the write buffer
323 template <typename T>
324 [[nodiscard]] std::size_t GetWriteBufferNumElements(std::size_t buffer_index = 0) const {
325 return GetWriteBufferSize(buffer_index) / sizeof(T);
326 }
315 327
316 /// Helper function to test whether the input buffer at buffer_index can be read 328 /// Helper function to test whether the input buffer at buffer_index can be read
317 bool CanReadBuffer(std::size_t buffer_index = 0) const; 329 [[nodiscard]] bool CanReadBuffer(std::size_t buffer_index = 0) const;
318 330
319 /// Helper function to test whether the output buffer at buffer_index can be written 331 /// Helper function to test whether the output buffer at buffer_index can be written
320 bool CanWriteBuffer(std::size_t buffer_index = 0) const; 332 [[nodiscard]] bool CanWriteBuffer(std::size_t buffer_index = 0) const;
321 333
322 Handle GetCopyHandle(std::size_t index) const { 334 [[nodiscard]] Handle GetCopyHandle(std::size_t index) const {
323 return incoming_copy_handles.at(index); 335 return incoming_copy_handles.at(index);
324 } 336 }
325 337
326 Handle GetMoveHandle(std::size_t index) const { 338 [[nodiscard]] Handle GetMoveHandle(std::size_t index) const {
327 return incoming_move_handles.at(index); 339 return incoming_move_handles.at(index);
328 } 340 }
329 341
@@ -348,13 +360,13 @@ public:
348 manager = manager_; 360 manager = manager_;
349 } 361 }
350 362
351 std::string Description() const; 363 [[nodiscard]] std::string Description() const;
352 364
353 KThread& GetThread() { 365 [[nodiscard]] KThread& GetThread() {
354 return *thread; 366 return *thread;
355 } 367 }
356 368
357 std::shared_ptr<SessionRequestManager> GetManager() const { 369 [[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
358 return manager.lock(); 370 return manager.lock();
359 } 371 }
360 372
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 608925dfc..053e8f9dd 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -122,10 +122,10 @@ private:
122 } 122 }
123 123
124 void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) { 124 void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) {
125 auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); 125 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
126 std::vector<u64> released_buffers(write_buffer_size, 0); 126 std::vector<u64> released_buffers(write_buffer_size);
127 127
128 auto count = impl->GetReleasedBuffers(released_buffers); 128 const auto count = impl->GetReleasedBuffers(released_buffers);
129 129
130 [[maybe_unused]] std::string tags{}; 130 [[maybe_unused]] std::string tags{};
131 for (u32 i = 0; i < count; i++) { 131 for (u32 i = 0; i < count; i++) {
@@ -228,7 +228,7 @@ void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
228 LOG_DEBUG(Service_Audio, "called"); 228 LOG_DEBUG(Service_Audio, "called");
229 229
230 const auto write_count = 230 const auto write_count =
231 static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); 231 static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
232 std::vector<AudioDevice::AudioDeviceName> device_names{}; 232 std::vector<AudioDevice::AudioDeviceName> device_names{};
233 233
234 u32 out_count{0}; 234 u32 out_count{0};
@@ -248,7 +248,7 @@ void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
248 LOG_DEBUG(Service_Audio, "called"); 248 LOG_DEBUG(Service_Audio, "called");
249 249
250 const auto write_count = 250 const auto write_count =
251 static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); 251 static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
252 std::vector<AudioDevice::AudioDeviceName> device_names{}; 252 std::vector<AudioDevice::AudioDeviceName> device_names{};
253 253
254 u32 out_count{0}; 254 u32 out_count{0};
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 122290c6a..29751f075 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -129,16 +129,16 @@ private:
129 } 129 }
130 130
131 void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) { 131 void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) {
132 auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); 132 const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
133 std::vector<u64> released_buffers(write_buffer_size, 0); 133 std::vector<u64> released_buffers(write_buffer_size);
134 134
135 auto count = impl->GetReleasedBuffers(released_buffers); 135 const auto count = impl->GetReleasedBuffers(released_buffers);
136 136
137 [[maybe_unused]] std::string tags{}; 137 [[maybe_unused]] std::string tags{};
138 for (u32 i = 0; i < count; i++) { 138 for (u32 i = 0; i < count; i++) {
139 tags += fmt::format("{:08X}, ", released_buffers[i]); 139 tags += fmt::format("{:08X}, ", released_buffers[i]);
140 } 140 }
141 [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; 141 [[maybe_unused]] const auto sessionid{impl->GetSystem().GetSessionId()};
142 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, 142 LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
143 tags); 143 tags);
144 144
@@ -244,7 +244,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
244 std::scoped_lock l{impl->mutex}; 244 std::scoped_lock l{impl->mutex};
245 245
246 const auto write_count = 246 const auto write_count =
247 static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); 247 static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
248 std::vector<AudioDevice::AudioDeviceName> device_names{}; 248 std::vector<AudioDevice::AudioDeviceName> device_names{};
249 if (write_count > 0) { 249 if (write_count > 0) {
250 device_names.emplace_back("DeviceOut"); 250 device_names.emplace_back("DeviceOut");
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 13423dca6..034ee273f 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -274,7 +274,7 @@ public:
274 274
275private: 275private:
276 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { 276 void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
277 const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); 277 const size_t in_count = ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>();
278 278
279 std::vector<AudioDevice::AudioDeviceName> out_names{}; 279 std::vector<AudioDevice::AudioDeviceName> out_names{};
280 280
@@ -335,7 +335,7 @@ private:
335 } 335 }
336 336
337 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { 337 void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
338 const auto write_size = ctx.GetWriteBufferSize() / sizeof(char); 338 const auto write_size = ctx.GetWriteBufferSize();
339 std::string out_name{"AudioTvOutput"}; 339 std::string out_name{"AudioTvOutput"};
340 340
341 LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name); 341 LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
@@ -387,7 +387,7 @@ private:
387 } 387 }
388 388
389 void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) { 389 void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) {
390 const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); 390 const size_t in_count = ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>();
391 391
392 std::vector<AudioDevice::AudioDeviceName> out_names{}; 392 std::vector<AudioDevice::AudioDeviceName> out_names{};
393 393
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 8bafc3a98..825fb8bcc 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -68,7 +68,7 @@ private:
68 ExtraBehavior extra_behavior) { 68 ExtraBehavior extra_behavior) {
69 u32 consumed = 0; 69 u32 consumed = 0;
70 u32 sample_count = 0; 70 u32 sample_count = 0;
71 std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); 71 std::vector<opus_int16> samples(ctx.GetWriteBufferNumElements<opus_int16>());
72 72
73 if (extra_behavior == ExtraBehavior::ResetContext) { 73 if (extra_behavior == ExtraBehavior::ResetContext) {
74 ResetDecoderContext(); 74 ResetDecoderContext();
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp
index bc08ac487..cbe690a5d 100644
--- a/src/core/hle/service/bcat/bcat_module.cpp
+++ b/src/core/hle/service/bcat/bcat_module.cpp
@@ -443,7 +443,7 @@ private:
443 } 443 }
444 444
445 void Read(Kernel::HLERequestContext& ctx) { 445 void Read(Kernel::HLERequestContext& ctx) {
446 auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry); 446 auto write_size = ctx.GetWriteBufferNumElements<DeliveryCacheDirectoryEntry>();
447 447
448 LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size); 448 LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
449 449
@@ -533,7 +533,7 @@ private:
533 } 533 }
534 534
535 void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) { 535 void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) {
536 auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName); 536 auto size = ctx.GetWriteBufferNumElements<DirectoryName>();
537 537
538 LOG_DEBUG(Service_BCAT, "called, size={:016X}", size); 538 LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
539 539
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index ff9b0427c..d183e5829 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -192,12 +192,10 @@ private:
192 } 192 }
193 193
194 void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) { 194 void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) {
195 u32 out_entries; 195 size_t out_entries = 0;
196 if (keys.GetCommonTickets().empty()) 196 if (!keys.GetCommonTickets().empty()) {
197 out_entries = 0; 197 out_entries = ctx.GetWriteBufferNumElements<u128>();
198 else 198 }
199 out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
200
201 LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); 199 LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
202 200
203 keys.PopulateTickets(); 201 keys.PopulateTickets();
@@ -206,20 +204,19 @@ private:
206 std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), 204 std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
207 [](const auto& pair) { return pair.first; }); 205 [](const auto& pair) { return pair.first; });
208 206
209 out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries)); 207 out_entries = std::min(ids.size(), out_entries);
210 ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); 208 ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
211 209
212 IPC::ResponseBuilder rb{ctx, 3}; 210 IPC::ResponseBuilder rb{ctx, 3};
213 rb.Push(ResultSuccess); 211 rb.Push(ResultSuccess);
214 rb.Push<u32>(out_entries); 212 rb.Push<u32>(static_cast<u32>(out_entries));
215 } 213 }
216 214
217 void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) { 215 void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) {
218 u32 out_entries; 216 size_t out_entries = 0;
219 if (keys.GetPersonalizedTickets().empty()) 217 if (!keys.GetPersonalizedTickets().empty()) {
220 out_entries = 0; 218 out_entries = ctx.GetWriteBufferNumElements<u128>();
221 else 219 }
222 out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
223 220
224 LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); 221 LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
225 222
@@ -229,12 +226,12 @@ private:
229 std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), 226 std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
230 [](const auto& pair) { return pair.first; }); 227 [](const auto& pair) { return pair.first; });
231 228
232 out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries)); 229 out_entries = std::min(ids.size(), out_entries);
233 ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); 230 ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
234 231
235 IPC::ResponseBuilder rb{ctx, 3}; 232 IPC::ResponseBuilder rb{ctx, 3};
236 rb.Push(ResultSuccess); 233 rb.Push(ResultSuccess);
237 rb.Push<u32>(out_entries); 234 rb.Push<u32>(static_cast<u32>(out_entries));
238 } 235 }
239 236
240 void GetCommonTicketSize(Kernel::HLERequestContext& ctx) { 237 void GetCommonTicketSize(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index c08274ef9..fbb16a7da 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -277,7 +277,7 @@ private:
277 LOG_DEBUG(Service_FS, "called."); 277 LOG_DEBUG(Service_FS, "called.");
278 278
279 // Calculate how many entries we can fit in the output buffer 279 // Calculate how many entries we can fit in the output buffer
280 const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); 280 const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::Entry>();
281 281
282 // Cap at total number of entries. 282 // Cap at total number of entries.
283 const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); 283 const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
@@ -543,7 +543,7 @@ public:
543 LOG_DEBUG(Service_FS, "called"); 543 LOG_DEBUG(Service_FS, "called");
544 544
545 // Calculate how many entries we can fit in the output buffer 545 // Calculate how many entries we can fit in the output buffer
546 const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo); 546 const u64 count_entries = ctx.GetWriteBufferNumElements<SaveDataInfo>();
547 547
548 // Cap at total number of entries. 548 // Cap at total number of entries.
549 const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index); 549 const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 6df563136..c49c61cff 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -292,7 +292,7 @@ public:
292 292
293 void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) { 293 void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) {
294 const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0); 294 const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
295 const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); 295 const std::size_t node_buffer_count = ctx.GetWriteBufferNumElements<NodeLatestUpdate>(1);
296 296
297 if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { 297 if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
298 LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size, 298 LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size,
@@ -333,7 +333,7 @@ public:
333 const auto channel{rp.PopEnum<WifiChannel>()}; 333 const auto channel{rp.PopEnum<WifiChannel>()};
334 const auto scan_filter{rp.PopRaw<ScanFilter>()}; 334 const auto scan_filter{rp.PopRaw<ScanFilter>()};
335 335
336 const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo); 336 const std::size_t network_info_size = ctx.GetWriteBufferNumElements<NetworkInfo>();
337 337
338 if (network_info_size == 0) { 338 if (network_info_size == 0) {
339 LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size); 339 LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp
index 0753333bf..ced2d560b 100644
--- a/src/core/hle/service/nfc/nfc_user.cpp
+++ b/src/core/hle/service/nfc/nfc_user.cpp
@@ -118,7 +118,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
118 } 118 }
119 119
120 std::vector<u64> nfp_devices; 120 std::vector<u64> nfp_devices;
121 const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64); 121 const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
122 122
123 for (auto& device : devices) { 123 for (auto& device : devices) {
124 if (nfp_devices.size() >= max_allowed_devices) { 124 if (nfp_devices.size() >= max_allowed_devices) {
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 2fe3c0ea0..49816b4c7 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -104,9 +104,9 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
104 } 104 }
105 105
106 std::vector<u64> nfp_devices; 106 std::vector<u64> nfp_devices;
107 const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64); 107 const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
108 108
109 for (auto& device : devices) { 109 for (const auto& device : devices) {
110 if (nfp_devices.size() >= max_allowed_devices) { 110 if (nfp_devices.size() >= max_allowed_devices) {
111 continue; 111 continue;
112 } 112 }
@@ -115,7 +115,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
115 } 115 }
116 } 116 }
117 117
118 if (nfp_devices.size() == 0) { 118 if (nfp_devices.empty()) {
119 IPC::ResponseBuilder rb{ctx, 2}; 119 IPC::ResponseBuilder rb{ctx, 2};
120 rb.Push(DeviceNotFound); 120 rb.Push(DeviceNotFound);
121 return; 121 return;
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/ns/iplatform_service_manager.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp
index fd047ff26..1fab2f0dd 100644
--- a/src/core/hle/service/ns/iplatform_service_manager.cpp
+++ b/src/core/hle/service/ns/iplatform_service_manager.cpp
@@ -279,13 +279,10 @@ void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestC
279 font_sizes.push_back(region.size); 279 font_sizes.push_back(region.size);
280 } 280 }
281 281
282 // Resize buffers if game requests smaller size output. 282 // Resize buffers if game requests smaller size output
283 font_codes.resize( 283 font_codes.resize(std::min(font_codes.size(), ctx.GetWriteBufferNumElements<u32>(0)));
284 std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32))); 284 font_offsets.resize(std::min(font_offsets.size(), ctx.GetWriteBufferNumElements<u32>(1)));
285 font_offsets.resize( 285 font_sizes.resize(std::min(font_sizes.size(), ctx.GetWriteBufferNumElements<u32>(2)));
286 std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
287 font_sizes.resize(
288 std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
289 286
290 ctx.WriteBuffer(font_codes, 0); 287 ctx.WriteBuffer(font_codes, 0);
291 ctx.WriteBuffer(font_offsets, 1); 288 ctx.WriteBuffer(font_offsets, 1);
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/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index f761c2da4..4f1a8d6b7 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -83,7 +83,7 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
83} 83}
84 84
85void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) { 85void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
86 const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode); 86 const std::size_t requested_amount = ctx.GetWriteBufferNumElements<LanguageCode>();
87 const std::size_t max_amount = std::min(requested_amount, max_entries); 87 const std::size_t max_amount = std::min(requested_amount, max_entries);
88 const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount); 88 const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount);
89 const std::size_t copy_size = copy_amount * sizeof(LanguageCode); 89 const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
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 b2064ef95..942a13535 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -33,119 +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
127 Common::Input::UnregisterInputFactory(camera->GetEngineName());
128 Common::Input::UnregisterOutputFactory(camera->GetEngineName());
129 camera.reset();
130
131 Common::Input::UnregisterInputFactory(virtual_amiibo->GetEngineName());
132 Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName());
133 virtual_amiibo.reset();
134
141#ifdef HAVE_SDL2 135#ifdef HAVE_SDL2
142 Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName()); 136 Common::Input::UnregisterInputFactory(sdl->GetEngineName());
143 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName()); 137 Common::Input::UnregisterOutputFactory(sdl->GetEngineName());
144 sdl.reset(); 138 sdl.reset();
145#endif 139#endif
146 140
147 Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button"); 141 Common::Input::UnregisterInputFactory("touch_from_button");
148 Common::Input::UnregisterFactory<Common::Input::InputDevice>("analog_from_button"); 142 Common::Input::UnregisterInputFactory("analog_from_button");
149 } 143 }
150 144
151 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 145 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
@@ -324,6 +318,12 @@ struct InputSubsystem::Impl {
324#endif 318#endif
325 } 319 }
326 320
321 void PumpEvents() const {
322#ifdef HAVE_SDL2
323 sdl->PumpEvents();
324#endif
325 }
326
327 void RegisterInput(const MappingData& data) { 327 void RegisterInput(const MappingData& data) {
328 mapping_factory->RegisterInput(data); 328 mapping_factory->RegisterInput(data);
329 } 329 }
@@ -472,6 +472,10 @@ void InputSubsystem::StopMapping() const {
472 impl->mapping_factory->StopMapping(); 472 impl->mapping_factory->StopMapping();
473} 473}
474 474
475void InputSubsystem::PumpEvents() const {
476 impl->PumpEvents();
477}
478
475std::string GenerateKeyboardParam(int key_code) { 479std::string GenerateKeyboardParam(int key_code) {
476 Common::ParamPackage param; 480 Common::ParamPackage param;
477 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/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/CMakeLists.txt b/src/video_core/CMakeLists.txt
index d7f7d336c..b03a30992 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -28,6 +28,10 @@ add_library(video_core STATIC
28 dirty_flags.h 28 dirty_flags.h
29 dma_pusher.cpp 29 dma_pusher.cpp
30 dma_pusher.h 30 dma_pusher.h
31 engines/sw_blitter/blitter.cpp
32 engines/sw_blitter/blitter.h
33 engines/sw_blitter/converter.cpp
34 engines/sw_blitter/converter.h
31 engines/const_buffer_info.h 35 engines/const_buffer_info.h
32 engines/engine_interface.h 36 engines/engine_interface.h
33 engines/engine_upload.cpp 37 engines/engine_upload.cpp
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 599551013..5d3a8293b 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -1742,12 +1742,12 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size,
1742 SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size)); 1742 SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
1743 1743
1744 if constexpr (USE_MEMORY_MAPS) { 1744 if constexpr (USE_MEMORY_MAPS) {
1745 auto upload_staging = runtime.UploadStagingBuffer(copy_size);
1745 std::array copies{BufferCopy{ 1746 std::array copies{BufferCopy{
1746 .src_offset = 0, 1747 .src_offset = upload_staging.offset,
1747 .dst_offset = buffer.Offset(dest_address), 1748 .dst_offset = buffer.Offset(dest_address),
1748 .size = copy_size, 1749 .size = copy_size,
1749 }}; 1750 }};
1750 auto upload_staging = runtime.UploadStagingBuffer(copy_size);
1751 u8* const src_pointer = upload_staging.mapped_span.data(); 1751 u8* const src_pointer = upload_staging.mapped_span.data();
1752 std::memcpy(src_pointer, inlined_buffer.data(), copy_size); 1752 std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
1753 runtime.CopyBuffer(buffer, upload_staging.buffer, copies); 1753 runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
diff --git a/src/video_core/control/channel_state.cpp b/src/video_core/control/channel_state.cpp
index cdecc3a91..832025d75 100644
--- a/src/video_core/control/channel_state.cpp
+++ b/src/video_core/control/channel_state.cpp
@@ -20,7 +20,7 @@ void ChannelState::Init(Core::System& system, GPU& gpu) {
20 ASSERT(memory_manager); 20 ASSERT(memory_manager);
21 dma_pusher = std::make_unique<Tegra::DmaPusher>(system, gpu, *memory_manager, *this); 21 dma_pusher = std::make_unique<Tegra::DmaPusher>(system, gpu, *memory_manager, *this);
22 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, *memory_manager); 22 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, *memory_manager);
23 fermi_2d = std::make_unique<Engines::Fermi2D>(); 23 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
24 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, *memory_manager); 24 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, *memory_manager);
25 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager); 25 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager);
26 kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager); 26 kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp
index a34819234..e4f8331ab 100644
--- a/src/video_core/engines/engine_upload.cpp
+++ b/src/video_core/engines/engine_upload.cpp
@@ -49,13 +49,12 @@ 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 memory_manager.WriteBlockUnsafe( 54 std::span<const u8> buffer(read_buffer.data() + line * regs.line_length_in,
55 dest_line, read_buffer.data() + static_cast<size_t>(line) * regs.line_length_in, 55 regs.line_length_in);
56 regs.line_length_in); 56 rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer);
57 } 57 }
58 memory_manager.InvalidateRegion(address, regs.dest.pitch * regs.line_count);
59 } 58 }
60 } else { 59 } else {
61 u32 width = regs.dest.width; 60 u32 width = regs.dest.width;
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.cpp b/src/video_core/engines/fermi_2d.cpp
index 453e0fb01..c6478ae85 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -3,17 +3,25 @@
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/microprofile.h"
6#include "video_core/engines/fermi_2d.h" 7#include "video_core/engines/fermi_2d.h"
7#include "video_core/memory_manager.h" 8#include "video_core/engines/sw_blitter/blitter.h"
8#include "video_core/rasterizer_interface.h" 9#include "video_core/rasterizer_interface.h"
9#include "video_core/surface.h" 10#include "video_core/surface.h"
11#include "video_core/textures/decoders.h"
12
13MICROPROFILE_DECLARE(GPU_BlitEngine);
14MICROPROFILE_DEFINE(GPU_BlitEngine, "GPU", "Blit Engine", MP_RGB(224, 224, 128));
10 15
11using VideoCore::Surface::BytesPerBlock; 16using VideoCore::Surface::BytesPerBlock;
12using VideoCore::Surface::PixelFormatFromRenderTargetFormat; 17using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
13 18
14namespace Tegra::Engines { 19namespace Tegra::Engines {
15 20
16Fermi2D::Fermi2D() { 21using namespace Texture;
22
23Fermi2D::Fermi2D(MemoryManager& memory_manager_) {
24 sw_blitter = std::make_unique<Blitter::SoftwareBlitEngine>(memory_manager_);
17 // Nvidia's OpenGL driver seems to assume these values 25 // Nvidia's OpenGL driver seems to assume these values
18 regs.src.depth = 1; 26 regs.src.depth = 1;
19 regs.dst.depth = 1; 27 regs.dst.depth = 1;
@@ -42,6 +50,7 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32
42} 50}
43 51
44void Fermi2D::Blit() { 52void Fermi2D::Blit() {
53 MICROPROFILE_SCOPE(GPU_BlitEngine);
45 LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}", 54 LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",
46 regs.src.Address(), regs.dst.Address()); 55 regs.src.Address(), regs.dst.Address());
47 56
@@ -52,9 +61,16 @@ void Fermi2D::Blit() {
52 UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled"); 61 UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled");
53 62
54 const auto& args = regs.pixels_from_memory; 63 const auto& args = regs.pixels_from_memory;
64 constexpr s64 null_derivate = 1ULL << 32;
65 Surface src = regs.src;
66 const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
67 const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 &&
68 src.format != regs.dst.format;
55 Config config{ 69 Config config{
56 .operation = regs.operation, 70 .operation = regs.operation,
57 .filter = args.sample_mode.filter, 71 .filter = args.sample_mode.filter,
72 .must_accelerate =
73 args.du_dx != null_derivate || args.dv_dy != null_derivate || delegate_to_gpu,
58 .dst_x0 = args.dst_x0, 74 .dst_x0 = args.dst_x0,
59 .dst_y0 = args.dst_y0, 75 .dst_y0 = args.dst_y0,
60 .dst_x1 = args.dst_x0 + args.dst_width, 76 .dst_x1 = args.dst_x0 + args.dst_width,
@@ -64,8 +80,7 @@ void Fermi2D::Blit() {
64 .src_x1 = static_cast<s32>((args.du_dx * args.dst_width + args.src_x0) >> 32), 80 .src_x1 = static_cast<s32>((args.du_dx * args.dst_width + args.src_x0) >> 32),
65 .src_y1 = static_cast<s32>((args.dv_dy * args.dst_height + args.src_y0) >> 32), 81 .src_y1 = static_cast<s32>((args.dv_dy * args.dst_height + args.src_y0) >> 32),
66 }; 82 };
67 Surface src = regs.src; 83
68 const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
69 const auto need_align_to_pitch = 84 const auto need_align_to_pitch =
70 src.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch && 85 src.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch &&
71 static_cast<s32>(src.width) == config.src_x1 && 86 static_cast<s32>(src.width) == config.src_x1 &&
@@ -78,8 +93,9 @@ void Fermi2D::Blit() {
78 config.src_x1 -= config.src_x0; 93 config.src_x1 -= config.src_x0;
79 config.src_x0 = 0; 94 config.src_x0 = 0;
80 } 95 }
96
81 if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) { 97 if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) {
82 UNIMPLEMENTED(); 98 sw_blitter->Blit(src, regs.dst, config);
83 } 99 }
84} 100}
85 101
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 1229aa35b..100b21bac 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <cstddef> 7#include <cstddef>
8#include <memory>
8#include "common/bit_field.h" 9#include "common/bit_field.h"
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
@@ -21,6 +22,10 @@ class RasterizerInterface;
21 22
22namespace Tegra::Engines { 23namespace Tegra::Engines {
23 24
25namespace Blitter {
26class SoftwareBlitEngine;
27}
28
24/** 29/**
25 * This Engine is known as G80_2D. Documentation can be found in: 30 * This Engine is known as G80_2D. Documentation can be found in:
26 * https://github.com/envytools/envytools/blob/master/rnndb/graph/g80_2d.xml 31 * https://github.com/envytools/envytools/blob/master/rnndb/graph/g80_2d.xml
@@ -32,7 +37,7 @@ namespace Tegra::Engines {
32 37
33class Fermi2D final : public EngineInterface { 38class Fermi2D final : public EngineInterface {
34public: 39public:
35 explicit Fermi2D(); 40 explicit Fermi2D(MemoryManager& memory_manager_);
36 ~Fermi2D() override; 41 ~Fermi2D() override;
37 42
38 /// Binds a rasterizer to this engine. 43 /// Binds a rasterizer to this engine.
@@ -92,7 +97,7 @@ public:
92 u32 addr_lower; 97 u32 addr_lower;
93 98
94 [[nodiscard]] constexpr GPUVAddr Address() const noexcept { 99 [[nodiscard]] constexpr GPUVAddr Address() const noexcept {
95 return (static_cast<GPUVAddr>(addr_upper) << 32) | static_cast<GPUVAddr>(addr_lower); 100 return (GPUVAddr{addr_upper} << 32) | GPUVAddr{addr_lower};
96 } 101 }
97 }; 102 };
98 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size"); 103 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size");
@@ -286,6 +291,7 @@ public:
286 struct Config { 291 struct Config {
287 Operation operation; 292 Operation operation;
288 Filter filter; 293 Filter filter;
294 bool must_accelerate;
289 s32 dst_x0; 295 s32 dst_x0;
290 s32 dst_y0; 296 s32 dst_y0;
291 s32 dst_x1; 297 s32 dst_x1;
@@ -298,6 +304,7 @@ public:
298 304
299private: 305private:
300 VideoCore::RasterizerInterface* rasterizer = nullptr; 306 VideoCore::RasterizerInterface* rasterizer = nullptr;
307 std::unique_ptr<Blitter::SoftwareBlitEngine> sw_blitter;
301 308
302 /// Performs the copy from the source surface to the destination surface as configured in the 309 /// Performs the copy from the source surface to the destination surface as configured in the
303 /// registers. 310 /// registers.
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 5bb1427c1..55462752c 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -126,6 +126,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
126 draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true; 126 draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
127 draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true; 127 draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
128 draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true; 128 draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
129 draw_command[MAXWELL3D_REG_INDEX(draw.instance_id)] = true;
129} 130}
130 131
131void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { 132void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
@@ -249,9 +250,6 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
249 return; 250 return;
250 case MAXWELL3D_REG_INDEX(fragment_barrier): 251 case MAXWELL3D_REG_INDEX(fragment_barrier):
251 return rasterizer->FragmentBarrier(); 252 return rasterizer->FragmentBarrier();
252 case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache):
253 rasterizer->InvalidateGPUCache();
254 return rasterizer->WaitForIdle();
255 case MAXWELL3D_REG_INDEX(tiled_cache_barrier): 253 case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
256 return rasterizer->TiledCacheBarrier(); 254 return rasterizer->TiledCacheBarrier();
257 } 255 }
@@ -288,31 +286,58 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
288 ASSERT_MSG(method < Regs::NUM_REGS, 286 ASSERT_MSG(method < Regs::NUM_REGS,
289 "Invalid Maxwell3D register, increase the size of the Regs structure"); 287 "Invalid Maxwell3D register, increase the size of the Regs structure");
290 288
289 const u32 argument = ProcessShadowRam(method, method_argument);
290 ProcessDirtyRegisters(method, argument);
291
291 if (draw_command[method]) { 292 if (draw_command[method]) {
292 regs.reg_array[method] = method_argument; 293 regs.reg_array[method] = method_argument;
293 deferred_draw_method.push_back(method); 294 deferred_draw_method.push_back(method);
294 auto u32_to_u8 = [&](const u32 argument) { 295 auto update_inline_index = [&](const u32 index) {
295 inline_index_draw_indexes.push_back(static_cast<u8>(argument & 0x000000ff)); 296 inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
296 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x0000ff00) >> 8)); 297 inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
297 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x00ff0000) >> 16)); 298 inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x00ff0000) >> 16));
298 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0xff000000) >> 24)); 299 inline_index_draw_indexes.push_back(static_cast<u8>((index & 0xff000000) >> 24));
300 draw_mode = DrawMode::InlineIndex;
299 }; 301 };
300 if (MAXWELL3D_REG_INDEX(draw_inline_index) == method) { 302 switch (method) {
301 u32_to_u8(method_argument); 303 case MAXWELL3D_REG_INDEX(draw.end):
302 } else if (MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method) { 304 switch (draw_mode) {
303 u32_to_u8(regs.inline_index_2x16.even); 305 case DrawMode::General:
304 u32_to_u8(regs.inline_index_2x16.odd); 306 ProcessDraw(1);
305 } else if (MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) { 307 break;
306 u32_to_u8(regs.inline_index_4x8.index0); 308 case DrawMode::InlineIndex:
307 u32_to_u8(regs.inline_index_4x8.index1); 309 regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
308 u32_to_u8(regs.inline_index_4x8.index2); 310 regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
309 u32_to_u8(regs.inline_index_4x8.index3); 311 ProcessDraw(1);
312 inline_index_draw_indexes.clear();
313 break;
314 case DrawMode::Instance:
315 break;
316 }
317 break;
318 case MAXWELL3D_REG_INDEX(draw_inline_index):
319 update_inline_index(method_argument);
320 break;
321 case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
322 update_inline_index(regs.inline_index_2x16.even);
323 update_inline_index(regs.inline_index_2x16.odd);
324 break;
325 case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
326 update_inline_index(regs.inline_index_4x8.index0);
327 update_inline_index(regs.inline_index_4x8.index1);
328 update_inline_index(regs.inline_index_4x8.index2);
329 update_inline_index(regs.inline_index_4x8.index3);
330 break;
331 case MAXWELL3D_REG_INDEX(draw.instance_id):
332 draw_mode =
333 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
334 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
335 ? DrawMode::Instance
336 : DrawMode::General;
337 break;
310 } 338 }
311 } else { 339 } else {
312 ProcessDeferredDraw(); 340 ProcessDeferredDraw();
313
314 const u32 argument = ProcessShadowRam(method, method_argument);
315 ProcessDirtyRegisters(method, argument);
316 ProcessMethodCall(method, argument, method_argument, is_last_call); 341 ProcessMethodCall(method, argument, method_argument, is_last_call);
317 } 342 }
318} 343}
@@ -345,11 +370,11 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
345 ProcessCBMultiData(base_start, amount); 370 ProcessCBMultiData(base_start, amount);
346 break; 371 break;
347 case MAXWELL3D_REG_INDEX(inline_data): 372 case MAXWELL3D_REG_INDEX(inline_data):
348 upload_state.ProcessData(base_start, static_cast<size_t>(amount)); 373 upload_state.ProcessData(base_start, amount);
349 return; 374 return;
350 default: 375 default:
351 for (std::size_t i = 0; i < amount; i++) { 376 for (u32 i = 0; i < amount; i++) {
352 CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1); 377 CallMethod(method, base_start[i], methods_pending - i <= 1);
353 } 378 }
354 break; 379 break;
355 } 380 }
@@ -511,10 +536,7 @@ void Maxwell3D::ProcessCounterReset() {
511 536
512void Maxwell3D::ProcessSyncPoint() { 537void Maxwell3D::ProcessSyncPoint() {
513 const u32 sync_point = regs.sync_info.sync_point.Value(); 538 const u32 sync_point = regs.sync_info.sync_point.Value();
514 const u32 cache_flush = regs.sync_info.clean_l2.Value(); 539 [[maybe_unused]] const u32 cache_flush = regs.sync_info.clean_l2.Value();
515 if (cache_flush != 0) {
516 rasterizer->InvalidateGPUCache();
517 }
518 rasterizer->SignalSyncPoint(sync_point); 540 rasterizer->SignalSyncPoint(sync_point);
519} 541}
520 542
@@ -626,57 +648,27 @@ void Maxwell3D::ProcessDraw(u32 instance_count) {
626} 648}
627 649
628void Maxwell3D::ProcessDeferredDraw() { 650void Maxwell3D::ProcessDeferredDraw() {
629 if (deferred_draw_method.empty()) { 651 if (draw_mode != DrawMode::Instance || deferred_draw_method.empty()) {
630 return; 652 return;
631 } 653 }
632 654
633 enum class DrawMode { 655 const auto method_count = deferred_draw_method.size();
634 Undefined, 656 u32 instance_count = 1;
635 General, 657 u32 vertex_buffer_count = 0;
636 Instance, 658 u32 index_buffer_count = 0;
637 }; 659 for (size_t index = 0; index < method_count; ++index) {
638 DrawMode draw_mode{DrawMode::Undefined}; 660 const u32 method = deferred_draw_method[index];
639 u32 method_count = static_cast<u32>(deferred_draw_method.size()); 661 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
640 u32 method = deferred_draw_method[method_count - 1]; 662 instance_count = ++vertex_buffer_count;
641 if (MAXWELL3D_REG_INDEX(draw.end) != method) { 663 } else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
642 return; 664 instance_count = ++index_buffer_count;
643 }
644 draw_mode = (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
645 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
646 ? DrawMode::Instance
647 : DrawMode::General;
648 u32 instance_count = 0;
649 if (draw_mode == DrawMode::Instance) {
650 u32 vertex_buffer_count = 0;
651 u32 index_buffer_count = 0;
652 for (u32 index = 0; index < method_count; ++index) {
653 method = deferred_draw_method[index];
654 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
655 instance_count = ++vertex_buffer_count;
656 } else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
657 instance_count = ++index_buffer_count;
658 }
659 }
660 ASSERT_MSG(!(vertex_buffer_count && index_buffer_count),
661 "Instance both indexed and direct?");
662 } else {
663 instance_count = 1;
664 for (u32 index = 0; index < method_count; ++index) {
665 method = deferred_draw_method[index];
666 if (MAXWELL3D_REG_INDEX(draw_inline_index) == method ||
667 MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method ||
668 MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
669 regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
670 regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
671 break;
672 }
673 } 665 }
674 } 666 }
667 ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?");
675 668
676 ProcessDraw(instance_count); 669 ProcessDraw(instance_count);
677 670
678 deferred_draw_method.clear(); 671 deferred_draw_method.clear();
679 inline_index_draw_indexes.clear();
680} 672}
681 673
682} // namespace Tegra::Engines 674} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c3099f9a6..deba292a5 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -96,8 +96,7 @@ public:
96 u32 type; 96 u32 type;
97 97
98 GPUVAddr Address() const { 98 GPUVAddr Address() const {
99 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 99 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
100 address_low);
101 } 100 }
102 }; 101 };
103 102
@@ -106,8 +105,7 @@ public:
106 u32 address_low; 105 u32 address_low;
107 106
108 GPUVAddr Address() const { 107 GPUVAddr Address() const {
109 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 108 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
110 address_low);
111 } 109 }
112 }; 110 };
113 111
@@ -124,8 +122,7 @@ public:
124 Mode mode; 122 Mode mode;
125 123
126 GPUVAddr Address() const { 124 GPUVAddr Address() const {
127 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(offset_high) << 32) | 125 return (GPUVAddr{offset_high} << 32) | GPUVAddr{offset_low};
128 offset_low);
129 } 126 }
130 }; 127 };
131 128
@@ -187,7 +184,7 @@ public:
187 default: 184 default:
188 // Thresholds begin at 0x10 (1 << 4) 185 // Thresholds begin at 0x10 (1 << 4)
189 // Threshold is in the range 0x1 to 0x13 186 // Threshold is in the range 0x1 to 0x13
190 return 1 << (4 + threshold.Value() - 1); 187 return 1U << (4 + threshold.Value() - 1);
191 } 188 }
192 } 189 }
193 }; 190 };
@@ -468,8 +465,7 @@ public:
468 INSERT_PADDING_BYTES_NOINIT(0xC); 465 INSERT_PADDING_BYTES_NOINIT(0xC);
469 466
470 GPUVAddr Address() const { 467 GPUVAddr Address() const {
471 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 468 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
472 address_low);
473 } 469 }
474 }; 470 };
475 static_assert(sizeof(Buffer) == 0x20); 471 static_assert(sizeof(Buffer) == 0x20);
@@ -511,12 +507,11 @@ public:
511 u32 default_size_per_warp; 507 u32 default_size_per_warp;
512 508
513 GPUVAddr Address() const { 509 GPUVAddr Address() const {
514 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 510 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
515 address_low);
516 } 511 }
517 512
518 u64 Size() const { 513 u64 Size() const {
519 return (static_cast<u64>(size_high) << 32) | size_low; 514 return (u64{size_high} << 32) | u64{size_low};
520 } 515 }
521 }; 516 };
522 517
@@ -538,13 +533,11 @@ public:
538 u32 storage_limit_address_low; 533 u32 storage_limit_address_low;
539 534
540 GPUVAddr StorageAddress() const { 535 GPUVAddr StorageAddress() const {
541 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(storage_address_high) << 32) | 536 return (GPUVAddr{storage_address_high} << 32) | GPUVAddr{storage_address_low};
542 storage_address_low);
543 } 537 }
544 GPUVAddr StorageLimitAddress() const { 538 GPUVAddr StorageLimitAddress() const {
545 return static_cast<GPUVAddr>( 539 return (GPUVAddr{storage_limit_address_high} << 32) |
546 (static_cast<GPUVAddr>(storage_limit_address_high) << 32) | 540 GPUVAddr{storage_limit_address_low};
547 storage_limit_address_low);
548 } 541 }
549 }; 542 };
550 543
@@ -829,11 +822,11 @@ public:
829 struct CompressionThresholdSamples { 822 struct CompressionThresholdSamples {
830 u32 samples; 823 u32 samples;
831 824
832 u32 Samples() { 825 u32 Samples() const {
833 if (samples == 0) { 826 if (samples == 0) {
834 return 0; 827 return 0;
835 } 828 }
836 return 1 << (samples - 1); 829 return 1U << (samples - 1);
837 } 830 }
838 }; 831 };
839 832
@@ -1138,8 +1131,7 @@ public:
1138 INSERT_PADDING_BYTES_NOINIT(0x18); 1131 INSERT_PADDING_BYTES_NOINIT(0x18);
1139 1132
1140 GPUVAddr Address() const { 1133 GPUVAddr Address() const {
1141 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1134 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1142 address_low);
1143 } 1135 }
1144 }; 1136 };
1145 static_assert(sizeof(RenderTargetConfig) == 0x40); 1137 static_assert(sizeof(RenderTargetConfig) == 0x40);
@@ -1482,8 +1474,7 @@ public:
1482 u32 address_low; 1474 u32 address_low;
1483 1475
1484 GPUVAddr Address() const { 1476 GPUVAddr Address() const {
1485 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1477 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1486 address_low);
1487 } 1478 }
1488 }; 1479 };
1489 1480
@@ -1533,8 +1524,7 @@ public:
1533 u32 address_low; 1524 u32 address_low;
1534 1525
1535 GPUVAddr Address() const { 1526 GPUVAddr Address() const {
1536 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1527 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1537 address_low);
1538 } 1528 }
1539 }; 1529 };
1540 1530
@@ -1561,8 +1551,7 @@ public:
1561 u32 array_pitch; 1551 u32 array_pitch;
1562 1552
1563 GPUVAddr Address() const { 1553 GPUVAddr Address() const {
1564 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1554 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1565 address_low);
1566 } 1555 }
1567 }; 1556 };
1568 1557
@@ -1910,8 +1899,7 @@ public:
1910 Mode mode; 1899 Mode mode;
1911 1900
1912 GPUVAddr Address() const { 1901 GPUVAddr Address() const {
1913 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1902 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1914 address_low);
1915 } 1903 }
1916 }; 1904 };
1917 1905
@@ -1921,8 +1909,7 @@ public:
1921 u32 limit; 1909 u32 limit;
1922 1910
1923 GPUVAddr Address() const { 1911 GPUVAddr Address() const {
1924 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1912 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1925 address_low);
1926 } 1913 }
1927 }; 1914 };
1928 1915
@@ -1932,8 +1919,7 @@ public:
1932 u32 limit; 1919 u32 limit;
1933 1920
1934 GPUVAddr Address() const { 1921 GPUVAddr Address() const {
1935 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1922 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1936 address_low);
1937 } 1923 }
1938 }; 1924 };
1939 1925
@@ -1981,8 +1967,7 @@ public:
1981 u32 address_low; 1967 u32 address_low;
1982 1968
1983 GPUVAddr Address() const { 1969 GPUVAddr Address() const {
1984 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 1970 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
1985 address_low);
1986 } 1971 }
1987 }; 1972 };
1988 1973
@@ -2027,8 +2012,7 @@ public:
2027 u32 address_low; 2012 u32 address_low;
2028 2013
2029 GPUVAddr Address() const { 2014 GPUVAddr Address() const {
2030 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2015 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2031 address_low);
2032 } 2016 }
2033 }; 2017 };
2034 2018
@@ -2224,19 +2208,16 @@ public:
2224 } 2208 }
2225 2209
2226 GPUVAddr StartAddress() const { 2210 GPUVAddr StartAddress() const {
2227 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(start_addr_high) << 32) | 2211 return (GPUVAddr{start_addr_high} << 32) | GPUVAddr{start_addr_low};
2228 start_addr_low);
2229 } 2212 }
2230 2213
2231 GPUVAddr EndAddress() const { 2214 GPUVAddr EndAddress() const {
2232 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_addr_high) << 32) | 2215 return (GPUVAddr{limit_addr_high} << 32) | GPUVAddr{limit_addr_low};
2233 limit_addr_low);
2234 } 2216 }
2235 2217
2236 /// Adjust the index buffer offset so it points to the first desired index. 2218 /// Adjust the index buffer offset so it points to the first desired index.
2237 GPUVAddr IndexStart() const { 2219 GPUVAddr IndexStart() const {
2238 return StartAddress() + 2220 return StartAddress() + size_t{first} * size_t{FormatSizeInBytes()};
2239 static_cast<size_t>(first) * static_cast<size_t>(FormatSizeInBytes());
2240 } 2221 }
2241 }; 2222 };
2242 2223
@@ -2464,8 +2445,7 @@ public:
2464 } query; 2445 } query;
2465 2446
2466 GPUVAddr Address() const { 2447 GPUVAddr Address() const {
2467 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2448 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2468 address_low);
2469 } 2449 }
2470 }; 2450 };
2471 2451
@@ -2479,8 +2459,7 @@ public:
2479 u32 frequency; 2459 u32 frequency;
2480 2460
2481 GPUVAddr Address() const { 2461 GPUVAddr Address() const {
2482 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2462 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2483 address_low);
2484 } 2463 }
2485 2464
2486 bool IsEnabled() const { 2465 bool IsEnabled() const {
@@ -2494,8 +2473,7 @@ public:
2494 u32 address_low; 2473 u32 address_low;
2495 2474
2496 GPUVAddr Address() const { 2475 GPUVAddr Address() const {
2497 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2476 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2498 address_low);
2499 } 2477 }
2500 }; 2478 };
2501 static_assert(sizeof(VertexStreamLimit) == 0x8); 2479 static_assert(sizeof(VertexStreamLimit) == 0x8);
@@ -2543,8 +2521,7 @@ public:
2543 std::array<u32, NumCBData> buffer; 2521 std::array<u32, NumCBData> buffer;
2544 2522
2545 GPUVAddr Address() const { 2523 GPUVAddr Address() const {
2546 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 2524 return (GPUVAddr{address_high} << 32) | GPUVAddr{address_low};
2547 address_low);
2548 } 2525 }
2549 }; 2526 };
2550 2527
@@ -3148,10 +3125,12 @@ private:
3148 /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro) 3125 /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
3149 void ProcessTopologyOverride(); 3126 void ProcessTopologyOverride();
3150 3127
3151 void ProcessDraw(u32 instance_count = 1); 3128 /// Handles deferred draw(e.g., instance draw).
3152
3153 void ProcessDeferredDraw(); 3129 void ProcessDeferredDraw();
3154 3130
3131 /// Handles a draw.
3132 void ProcessDraw(u32 instance_count = 1);
3133
3155 /// Returns a query's value or an empty object if the value will be deferred through a cache. 3134 /// Returns a query's value or an empty object if the value will be deferred through a cache.
3156 std::optional<u64> GetQueryResult(); 3135 std::optional<u64> GetQueryResult();
3157 3136
@@ -3178,6 +3157,8 @@ private:
3178 3157
3179 std::array<bool, Regs::NUM_REGS> draw_command{}; 3158 std::array<bool, Regs::NUM_REGS> draw_command{};
3180 std::vector<u32> deferred_draw_method; 3159 std::vector<u32> deferred_draw_method;
3160 enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
3161 DrawMode draw_mode{DrawMode::General};
3181}; 3162};
3182 3163
3183#define ASSERT_REG_POSITION(field_name, position) \ 3164#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 1bf6ca2dd..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
@@ -62,7 +62,8 @@ void MaxwellDMA::Launch() {
62 62
63 if (!is_src_pitch && !is_dst_pitch) { 63 if (!is_src_pitch && !is_dst_pitch) {
64 // If both the source and the destination are in block layout, assert. 64 // If both the source and the destination are in block layout, assert.
65 UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented"); 65 CopyBlockLinearToBlockLinear();
66 ReleaseSemaphore();
66 return; 67 return;
67 } 68 }
68 69
@@ -93,14 +94,14 @@ void MaxwellDMA::Launch() {
93 reinterpret_cast<u8*>(tmp_buffer.data()), 94 reinterpret_cast<u8*>(tmp_buffer.data()),
94 regs.line_length_in * sizeof(u32)); 95 regs.line_length_in * sizeof(u32));
95 } else { 96 } else {
96 auto convert_linear_2_blocklinear_addr = [](u64 address) { 97 const auto convert_linear_2_blocklinear_addr = [](u64 address) {
97 return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) | 98 return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) |
98 ((address & 0x180) >> 1) | ((address & 0x20) << 3); 99 ((address & 0x180) >> 1) | ((address & 0x20) << 3);
99 }; 100 };
100 auto src_kind = memory_manager.GetPageKind(regs.offset_in); 101 const auto src_kind = memory_manager.GetPageKind(regs.offset_in);
101 auto dst_kind = memory_manager.GetPageKind(regs.offset_out); 102 const auto dst_kind = memory_manager.GetPageKind(regs.offset_out);
102 const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind)); 103 const bool is_src_pitch = IsPitchKind(src_kind);
103 const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind)); 104 const bool is_dst_pitch = IsPitchKind(dst_kind);
104 if (!is_src_pitch && is_dst_pitch) { 105 if (!is_src_pitch && is_dst_pitch) {
105 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); 106 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
106 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); 107 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
@@ -291,6 +292,70 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
291 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 292 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
292} 293}
293 294
295void MaxwellDMA::CopyBlockLinearToBlockLinear() {
296 UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0);
297
298 const bool is_remapping = regs.launch_dma.remap_enable != 0;
299
300 // Deswizzle the input and copy it over.
301 const Parameters& src = regs.src_params;
302 const Parameters& dst = regs.dst_params;
303
304 const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1;
305 const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1;
306
307 const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size;
308
309 u32 src_width = src.width;
310 u32 dst_width = dst.width;
311 u32 x_elements = regs.line_length_in;
312 u32 src_x_offset = src.origin.x;
313 u32 dst_x_offset = dst.origin.x;
314 u32 bpp_shift = 0U;
315 if (!is_remapping) {
316 bpp_shift = Common::FoldRight(
317 4U, [](u32 x, u32 y) { return std::min(x, static_cast<u32>(std::countr_zero(y))); },
318 src_width, dst_width, x_elements, src_x_offset, dst_x_offset,
319 static_cast<u32>(regs.offset_in), static_cast<u32>(regs.offset_out));
320 src_width >>= bpp_shift;
321 dst_width >>= bpp_shift;
322 x_elements >>= bpp_shift;
323 src_x_offset >>= bpp_shift;
324 dst_x_offset >>= bpp_shift;
325 }
326
327 const u32 bytes_per_pixel = base_bpp << bpp_shift;
328 const size_t src_size = CalculateSize(true, bytes_per_pixel, src_width, src.height, src.depth,
329 src.block_size.height, src.block_size.depth);
330 const size_t dst_size = CalculateSize(true, bytes_per_pixel, dst_width, dst.height, dst.depth,
331 dst.block_size.height, dst.block_size.depth);
332
333 const u32 pitch = x_elements * bytes_per_pixel;
334 const size_t mid_buffer_size = pitch * regs.line_count;
335
336 if (read_buffer.size() < src_size) {
337 read_buffer.resize(src_size);
338 }
339 if (write_buffer.size() < dst_size) {
340 write_buffer.resize(dst_size);
341 }
342
343 intermediate_buffer.resize(mid_buffer_size);
344
345 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
346 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
347
348 UnswizzleSubrect(intermediate_buffer, read_buffer, bytes_per_pixel, src_width, src.height,
349 src.depth, src_x_offset, src.origin.y, x_elements, regs.line_count,
350 src.block_size.height, src.block_size.depth, pitch);
351
352 SwizzleSubrect(write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height,
353 dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count,
354 dst.block_size.height, dst.block_size.depth, pitch);
355
356 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
357}
358
294void MaxwellDMA::ReleaseSemaphore() { 359void MaxwellDMA::ReleaseSemaphore() {
295 const auto type = regs.launch_dma.semaphore_type; 360 const auto type = regs.launch_dma.semaphore_type;
296 const GPUVAddr address = regs.semaphore.address; 361 const GPUVAddr address = regs.semaphore.address;
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 953e34adc..d40d3d302 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -223,6 +223,8 @@ private:
223 223
224 void CopyPitchToBlockLinear(); 224 void CopyPitchToBlockLinear();
225 225
226 void CopyBlockLinearToBlockLinear();
227
226 void FastCopyBlockLinearToPitch(); 228 void FastCopyBlockLinearToPitch();
227 229
228 void ReleaseSemaphore(); 230 void ReleaseSemaphore();
@@ -234,6 +236,7 @@ private:
234 236
235 std::vector<u8> read_buffer; 237 std::vector<u8> read_buffer;
236 std::vector<u8> write_buffer; 238 std::vector<u8> write_buffer;
239 std::vector<u8> intermediate_buffer;
237 240
238 static constexpr std::size_t NUM_REGS = 0x800; 241 static constexpr std::size_t NUM_REGS = 0x800;
239 struct Regs { 242 struct Regs {
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp
index 4d2278811..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);
@@ -118,7 +118,7 @@ void Puller::ProcessSemaphoreRelease() {
118 std::function<void()> operation([this, sequence_address, payload] { 118 std::function<void()> operation([this, sequence_address, payload] {
119 memory_manager.Write<u32>(sequence_address, payload); 119 memory_manager.Write<u32>(sequence_address, payload);
120 }); 120 });
121 rasterizer->SyncOperation(std::move(operation)); 121 rasterizer->SignalFence(std::move(operation));
122} 122}
123 123
124void Puller::ProcessSemaphoreAcquire() { 124void Puller::ProcessSemaphoreAcquire() {
@@ -151,8 +151,8 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
151 case BufferMethods::SemaphoreAddressLow: 151 case BufferMethods::SemaphoreAddressLow:
152 case BufferMethods::SemaphoreSequencePayload: 152 case BufferMethods::SemaphoreSequencePayload:
153 case BufferMethods::SyncpointPayload: 153 case BufferMethods::SyncpointPayload:
154 break;
155 case BufferMethods::WrcacheFlush: 154 case BufferMethods::WrcacheFlush:
155 break;
156 case BufferMethods::RefCnt: 156 case BufferMethods::RefCnt:
157 rasterizer->SignalReference(); 157 rasterizer->SignalReference();
158 break; 158 break;
@@ -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/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp
new file mode 100644
index 000000000..2f1ea4626
--- /dev/null
+++ b/src/video_core/engines/sw_blitter/blitter.cpp
@@ -0,0 +1,238 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <algorithm>
5#include <cmath>
6#include <vector>
7
8#include "video_core/engines/sw_blitter/blitter.h"
9#include "video_core/engines/sw_blitter/converter.h"
10#include "video_core/memory_manager.h"
11#include "video_core/surface.h"
12#include "video_core/textures/decoders.h"
13
14namespace Tegra {
15class MemoryManager;
16}
17
18using VideoCore::Surface::BytesPerBlock;
19using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
20
21namespace Tegra::Engines::Blitter {
22
23using namespace Texture;
24
25namespace {
26
27constexpr size_t ir_components = 4;
28
29void NearestNeighbor(std::span<const u8> input, std::span<u8> output, u32 src_width, u32 src_height,
30 u32 dst_width, u32 dst_height, size_t bpp) {
31 const size_t dx_du = std::llround((static_cast<f64>(src_width) / dst_width) * (1ULL << 32));
32 const size_t dy_dv = std::llround((static_cast<f64>(src_height) / dst_height) * (1ULL << 32));
33 size_t src_y = 0;
34 for (u32 y = 0; y < dst_height; y++) {
35 size_t src_x = 0;
36 for (u32 x = 0; x < dst_width; x++) {
37 const size_t read_from = ((src_y * src_width + src_x) >> 32) * bpp;
38 const size_t write_to = (y * dst_width + x) * bpp;
39
40 std::memcpy(&output[write_to], &input[read_from], bpp);
41 src_x += dx_du;
42 }
43 src_y += dy_dv;
44 }
45}
46
47void NearestNeighborFast(std::span<const f32> input, std::span<f32> output, u32 src_width,
48 u32 src_height, u32 dst_width, u32 dst_height) {
49 const size_t dx_du = std::llround((static_cast<f64>(src_width) / dst_width) * (1ULL << 32));
50 const size_t dy_dv = std::llround((static_cast<f64>(src_height) / dst_height) * (1ULL << 32));
51 size_t src_y = 0;
52 for (u32 y = 0; y < dst_height; y++) {
53 size_t src_x = 0;
54 for (u32 x = 0; x < dst_width; x++) {
55 const size_t read_from = ((src_y * src_width + src_x) >> 32) * ir_components;
56 const size_t write_to = (y * dst_width + x) * ir_components;
57
58 std::memcpy(&output[write_to], &input[read_from], sizeof(f32) * ir_components);
59 src_x += dx_du;
60 }
61 src_y += dy_dv;
62 }
63}
64
65void Bilinear(std::span<const f32> input, std::span<f32> output, size_t src_width,
66 size_t src_height, size_t dst_width, size_t dst_height) {
67 const auto bilinear_sample = [](std::span<const f32> x0_y0, std::span<const f32> x1_y0,
68 std::span<const f32> x0_y1, std::span<const f32> x1_y1,
69 f32 weight_x, f32 weight_y) {
70 std::array<f32, ir_components> result{};
71 for (size_t i = 0; i < ir_components; i++) {
72 const f32 a = std::lerp(x0_y0[i], x1_y0[i], weight_x);
73 const f32 b = std::lerp(x0_y1[i], x1_y1[i], weight_x);
74 result[i] = std::lerp(a, b, weight_y);
75 }
76 return result;
77 };
78 const f32 dx_du =
79 dst_width > 1 ? static_cast<f32>(src_width - 1) / static_cast<f32>(dst_width - 1) : 0.f;
80 const f32 dy_dv =
81 dst_height > 1 ? static_cast<f32>(src_height - 1) / static_cast<f32>(dst_height - 1) : 0.f;
82 for (u32 y = 0; y < dst_height; y++) {
83 for (u32 x = 0; x < dst_width; x++) {
84 const f32 x_low = std::floor(static_cast<f32>(x) * dx_du);
85 const f32 y_low = std::floor(static_cast<f32>(y) * dy_dv);
86 const f32 x_high = std::ceil(static_cast<f32>(x) * dx_du);
87 const f32 y_high = std::ceil(static_cast<f32>(y) * dy_dv);
88 const f32 weight_x = (static_cast<f32>(x) * dx_du) - x_low;
89 const f32 weight_y = (static_cast<f32>(y) * dy_dv) - y_low;
90
91 const auto read_src = [&](f32 in_x, f32 in_y) {
92 const size_t read_from =
93 ((static_cast<size_t>(in_x) * src_width + static_cast<size_t>(in_y)) >> 32) *
94 ir_components;
95 return std::span<const f32>(&input[read_from], ir_components);
96 };
97
98 auto x0_y0 = read_src(x_low, y_low);
99 auto x1_y0 = read_src(x_high, y_low);
100 auto x0_y1 = read_src(x_low, y_high);
101 auto x1_y1 = read_src(x_high, y_high);
102
103 const auto result = bilinear_sample(x0_y0, x1_y0, x0_y1, x1_y1, weight_x, weight_y);
104
105 const size_t write_to = (y * dst_width + x) * ir_components;
106
107 std::memcpy(&output[write_to], &result, sizeof(f32) * ir_components);
108 }
109 }
110}
111
112} // namespace
113
114struct SoftwareBlitEngine::BlitEngineImpl {
115 std::vector<u8> tmp_buffer;
116 std::vector<u8> src_buffer;
117 std::vector<u8> dst_buffer;
118 std::vector<f32> intermediate_src;
119 std::vector<f32> intermediate_dst;
120 ConverterFactory converter_factory;
121};
122
123SoftwareBlitEngine::SoftwareBlitEngine(MemoryManager& memory_manager_)
124 : memory_manager{memory_manager_} {
125 impl = std::make_unique<BlitEngineImpl>();
126}
127
128SoftwareBlitEngine::~SoftwareBlitEngine() = default;
129
130bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
131 Fermi2D::Config& config) {
132 const auto get_surface_size = [](Fermi2D::Surface& surface, u32 bytes_per_pixel) {
133 if (surface.linear == Fermi2D::MemoryLayout::BlockLinear) {
134 return CalculateSize(true, bytes_per_pixel, surface.width, surface.height,
135 surface.depth, surface.block_height, surface.block_depth);
136 }
137 return static_cast<size_t>(surface.pitch * surface.height);
138 };
139 const auto process_pitch_linear = [](bool unpack, std::span<const u8> input,
140 std::span<u8> output, u32 extent_x, u32 extent_y,
141 u32 pitch, u32 x0, u32 y0, size_t bpp) {
142 const size_t base_offset = x0 * bpp;
143 const size_t copy_size = extent_x * bpp;
144 for (u32 y = y0; y < extent_y; y++) {
145 const size_t first_offset = y * pitch + base_offset;
146 const size_t second_offset = y * extent_x * bpp;
147 u8* write_to = unpack ? &output[first_offset] : &output[second_offset];
148 const u8* read_from = unpack ? &input[second_offset] : &input[first_offset];
149 std::memcpy(write_to, read_from, copy_size);
150 }
151 };
152
153 const u32 src_extent_x = config.src_x1 - config.src_x0;
154 const u32 src_extent_y = config.src_y1 - config.src_y0;
155
156 const u32 dst_extent_x = config.dst_x1 - config.dst_x0;
157 const u32 dst_extent_y = config.dst_y1 - config.dst_y0;
158 const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
159 const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format));
160 const size_t src_size = get_surface_size(src, src_bytes_per_pixel);
161 impl->tmp_buffer.resize(src_size);
162 memory_manager.ReadBlock(src.Address(), impl->tmp_buffer.data(), src_size);
163
164 const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel;
165
166 const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel;
167
168 impl->src_buffer.resize(src_copy_size);
169
170 const bool no_passthrough =
171 src.format != dst.format || src_extent_x != dst_extent_x || src_extent_y != dst_extent_y;
172
173 const auto convertion_phase_same_format = [&]() {
174 NearestNeighbor(impl->src_buffer, impl->dst_buffer, src_extent_x, src_extent_y,
175 dst_extent_x, dst_extent_y, dst_bytes_per_pixel);
176 };
177
178 const auto convertion_phase_ir = [&]() {
179 auto* input_converter = impl->converter_factory.GetFormatConverter(src.format);
180 impl->intermediate_src.resize((src_copy_size / src_bytes_per_pixel) * ir_components);
181 impl->intermediate_dst.resize((dst_copy_size / dst_bytes_per_pixel) * ir_components);
182 input_converter->ConvertTo(impl->src_buffer, impl->intermediate_src);
183
184 if (config.filter != Fermi2D::Filter::Bilinear) {
185 NearestNeighborFast(impl->intermediate_src, impl->intermediate_dst, src_extent_x,
186 src_extent_y, dst_extent_x, dst_extent_y);
187 } else {
188 Bilinear(impl->intermediate_src, impl->intermediate_dst, src_extent_x, src_extent_y,
189 dst_extent_x, dst_extent_y);
190 }
191
192 auto* output_converter = impl->converter_factory.GetFormatConverter(dst.format);
193 output_converter->ConvertFrom(impl->intermediate_dst, impl->dst_buffer);
194 };
195
196 // Do actuall Blit
197
198 impl->dst_buffer.resize(dst_copy_size);
199 if (src.linear == Fermi2D::MemoryLayout::BlockLinear) {
200 UnswizzleSubrect(impl->src_buffer, impl->tmp_buffer, src_bytes_per_pixel, src.width,
201 src.height, src.depth, config.src_x0, config.src_y0, src_extent_x,
202 src_extent_y, src.block_height, src.block_depth,
203 src_extent_x * src_bytes_per_pixel);
204 } else {
205 process_pitch_linear(false, impl->tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
206 src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel);
207 }
208
209 // Conversion Phase
210 if (no_passthrough) {
211 if (src.format != dst.format || config.filter == Fermi2D::Filter::Bilinear) {
212 convertion_phase_ir();
213 } else {
214 convertion_phase_same_format();
215 }
216 } else {
217 impl->dst_buffer.swap(impl->src_buffer);
218 }
219
220 const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel);
221 impl->tmp_buffer.resize(dst_size);
222 memory_manager.ReadBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
223
224 if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) {
225 SwizzleSubrect(impl->tmp_buffer, impl->dst_buffer, dst_bytes_per_pixel, dst.width,
226 dst.height, dst.depth, config.dst_x0, config.dst_y0, dst_extent_x,
227 dst_extent_y, dst.block_height, dst.block_depth,
228 dst_extent_x * dst_bytes_per_pixel);
229 } else {
230 process_pitch_linear(true, impl->dst_buffer, impl->tmp_buffer, dst_extent_x, dst_extent_y,
231 dst.pitch, config.dst_x0, config.dst_y0,
232 static_cast<size_t>(dst_bytes_per_pixel));
233 }
234 memory_manager.WriteBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
235 return true;
236}
237
238} // namespace Tegra::Engines::Blitter
diff --git a/src/video_core/engines/sw_blitter/blitter.h b/src/video_core/engines/sw_blitter/blitter.h
new file mode 100644
index 000000000..85b55c836
--- /dev/null
+++ b/src/video_core/engines/sw_blitter/blitter.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "video_core/engines/fermi_2d.h"
7
8namespace Tegra {
9class MemoryManager;
10}
11
12namespace Tegra::Engines::Blitter {
13
14class SoftwareBlitEngine {
15public:
16 explicit SoftwareBlitEngine(MemoryManager& memory_manager_);
17 ~SoftwareBlitEngine();
18
19 bool Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, Fermi2D::Config& copy_config);
20
21private:
22 MemoryManager& memory_manager;
23 struct BlitEngineImpl;
24 std::unique_ptr<BlitEngineImpl> impl;
25};
26
27} // namespace Tegra::Engines::Blitter
diff --git a/src/video_core/engines/sw_blitter/converter.cpp b/src/video_core/engines/sw_blitter/converter.cpp
new file mode 100644
index 000000000..cd46dfd4f
--- /dev/null
+++ b/src/video_core/engines/sw_blitter/converter.cpp
@@ -0,0 +1,1234 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <array>
5#include <bit>
6#include <cmath>
7#include <span>
8#include <unordered_map>
9
10#include "common/assert.h"
11#include "video_core/engines/sw_blitter/converter.h"
12#include "video_core/surface.h"
13#include "video_core/textures/decoders.h"
14
15#ifdef _MSC_VER
16#define FORCE_INLINE __forceinline
17#else
18#define FORCE_INLINE inline __attribute__((always_inline))
19#endif
20
21namespace Tegra::Engines::Blitter {
22
23enum class Swizzle : size_t {
24 R = 0,
25 G = 1,
26 B = 2,
27 A = 3,
28 None,
29};
30
31enum class ComponentType : u32 {
32 SNORM = 1,
33 UNORM = 2,
34 SINT = 3,
35 UINT = 4,
36 SNORM_FORCE_FP16 = 5,
37 UNORM_FORCE_FP16 = 6,
38 FLOAT = 7,
39 SRGB = 8,
40};
41
42namespace {
43
44/*
45 * Note: Use generate_converters.py to generate the structs and searches for new render target
46 * formats and copy paste them to this file in order to update. just call "python
47 * generate_converters.py" and get the code from the output. modify the file to add new formats.
48 */
49
50constexpr std::array<f32, 256> SRGB_TO_RGB_LUT = {
51 0.000000e+00f, 3.035270e-04f, 6.070540e-04f, 9.105810e-04f, 1.214108e-03f, 1.517635e-03f,
52 1.821162e-03f, 2.124689e-03f, 2.428216e-03f, 2.731743e-03f, 3.035270e-03f, 3.346536e-03f,
53 3.676507e-03f, 4.024717e-03f, 4.391442e-03f, 4.776953e-03f, 5.181517e-03f, 5.605392e-03f,
54 6.048833e-03f, 6.512091e-03f, 6.995410e-03f, 7.499032e-03f, 8.023193e-03f, 8.568126e-03f,
55 9.134059e-03f, 9.721218e-03f, 1.032982e-02f, 1.096009e-02f, 1.161224e-02f, 1.228649e-02f,
56 1.298303e-02f, 1.370208e-02f, 1.444384e-02f, 1.520851e-02f, 1.599629e-02f, 1.680738e-02f,
57 1.764195e-02f, 1.850022e-02f, 1.938236e-02f, 2.028856e-02f, 2.121901e-02f, 2.217389e-02f,
58 2.315337e-02f, 2.415763e-02f, 2.518686e-02f, 2.624122e-02f, 2.732089e-02f, 2.842604e-02f,
59 2.955684e-02f, 3.071344e-02f, 3.189603e-02f, 3.310477e-02f, 3.433981e-02f, 3.560131e-02f,
60 3.688945e-02f, 3.820437e-02f, 3.954624e-02f, 4.091520e-02f, 4.231141e-02f, 4.373503e-02f,
61 4.518620e-02f, 4.666509e-02f, 4.817183e-02f, 4.970657e-02f, 5.126946e-02f, 5.286065e-02f,
62 5.448028e-02f, 5.612849e-02f, 5.780543e-02f, 5.951124e-02f, 6.124605e-02f, 6.301001e-02f,
63 6.480327e-02f, 6.662594e-02f, 6.847817e-02f, 7.036009e-02f, 7.227185e-02f, 7.421357e-02f,
64 7.618538e-02f, 7.818742e-02f, 8.021982e-02f, 8.228271e-02f, 8.437621e-02f, 8.650046e-02f,
65 8.865558e-02f, 9.084171e-02f, 9.305897e-02f, 9.530747e-02f, 9.758735e-02f, 9.989873e-02f,
66 1.022417e-01f, 1.046165e-01f, 1.070231e-01f, 1.094617e-01f, 1.119324e-01f, 1.144354e-01f,
67 1.169707e-01f, 1.195384e-01f, 1.221388e-01f, 1.247718e-01f, 1.274377e-01f, 1.301365e-01f,
68 1.328683e-01f, 1.356333e-01f, 1.384316e-01f, 1.412633e-01f, 1.441285e-01f, 1.470273e-01f,
69 1.499598e-01f, 1.529261e-01f, 1.559265e-01f, 1.589608e-01f, 1.620294e-01f, 1.651322e-01f,
70 1.682694e-01f, 1.714411e-01f, 1.746474e-01f, 1.778884e-01f, 1.811642e-01f, 1.844750e-01f,
71 1.878208e-01f, 1.912017e-01f, 1.946178e-01f, 1.980693e-01f, 2.015563e-01f, 2.050787e-01f,
72 2.086369e-01f, 2.122308e-01f, 2.158605e-01f, 2.195262e-01f, 2.232280e-01f, 2.269659e-01f,
73 2.307401e-01f, 2.345506e-01f, 2.383976e-01f, 2.422811e-01f, 2.462013e-01f, 2.501583e-01f,
74 2.541521e-01f, 2.581829e-01f, 2.622507e-01f, 2.663556e-01f, 2.704978e-01f, 2.746773e-01f,
75 2.788943e-01f, 2.831487e-01f, 2.874408e-01f, 2.917706e-01f, 2.961383e-01f, 3.005438e-01f,
76 3.049873e-01f, 3.094689e-01f, 3.139887e-01f, 3.185468e-01f, 3.231432e-01f, 3.277781e-01f,
77 3.324515e-01f, 3.371636e-01f, 3.419144e-01f, 3.467041e-01f, 3.515326e-01f, 3.564001e-01f,
78 3.613068e-01f, 3.662526e-01f, 3.712377e-01f, 3.762621e-01f, 3.813260e-01f, 3.864294e-01f,
79 3.915725e-01f, 3.967552e-01f, 4.019778e-01f, 4.072402e-01f, 4.125426e-01f, 4.178851e-01f,
80 4.232677e-01f, 4.286905e-01f, 4.341536e-01f, 4.396572e-01f, 4.452012e-01f, 4.507858e-01f,
81 4.564110e-01f, 4.620770e-01f, 4.677838e-01f, 4.735315e-01f, 4.793202e-01f, 4.851499e-01f,
82 4.910209e-01f, 4.969330e-01f, 5.028865e-01f, 5.088813e-01f, 5.149177e-01f, 5.209956e-01f,
83 5.271151e-01f, 5.332764e-01f, 5.394795e-01f, 5.457245e-01f, 5.520114e-01f, 5.583404e-01f,
84 5.647115e-01f, 5.711249e-01f, 5.775805e-01f, 5.840784e-01f, 5.906188e-01f, 5.972018e-01f,
85 6.038274e-01f, 6.104956e-01f, 6.172066e-01f, 6.239604e-01f, 6.307572e-01f, 6.375968e-01f,
86 6.444797e-01f, 6.514056e-01f, 6.583748e-01f, 6.653873e-01f, 6.724432e-01f, 6.795425e-01f,
87 6.866853e-01f, 6.938717e-01f, 7.011019e-01f, 7.083758e-01f, 7.156935e-01f, 7.230551e-01f,
88 7.304608e-01f, 7.379104e-01f, 7.454042e-01f, 7.529422e-01f, 7.605245e-01f, 7.681512e-01f,
89 7.758222e-01f, 7.835378e-01f, 7.912979e-01f, 7.991027e-01f, 8.069522e-01f, 8.148466e-01f,
90 8.227857e-01f, 8.307699e-01f, 8.387990e-01f, 8.468732e-01f, 8.549926e-01f, 8.631572e-01f,
91 8.713671e-01f, 8.796224e-01f, 8.879231e-01f, 8.962694e-01f, 9.046612e-01f, 9.130986e-01f,
92 9.215819e-01f, 9.301109e-01f, 9.386857e-01f, 9.473065e-01f, 9.559733e-01f, 9.646863e-01f,
93 9.734453e-01f, 9.822506e-01f, 9.911021e-01f, 1.000000e+00f};
94
95constexpr std::array<f32, 256> RGB_TO_SRGB_LUT = {
96 0.000000e+00f, 4.984009e-02f, 8.494473e-02f, 1.107021e-01f, 1.318038e-01f, 1.500052e-01f,
97 1.661857e-01f, 1.808585e-01f, 1.943532e-01f, 2.068957e-01f, 2.186491e-01f, 2.297351e-01f,
98 2.402475e-01f, 2.502604e-01f, 2.598334e-01f, 2.690152e-01f, 2.778465e-01f, 2.863614e-01f,
99 2.945889e-01f, 3.025538e-01f, 3.102778e-01f, 3.177796e-01f, 3.250757e-01f, 3.321809e-01f,
100 3.391081e-01f, 3.458689e-01f, 3.524737e-01f, 3.589320e-01f, 3.652521e-01f, 3.714419e-01f,
101 3.775084e-01f, 3.834581e-01f, 3.892968e-01f, 3.950301e-01f, 4.006628e-01f, 4.061998e-01f,
102 4.116451e-01f, 4.170030e-01f, 4.222770e-01f, 4.274707e-01f, 4.325873e-01f, 4.376298e-01f,
103 4.426010e-01f, 4.475037e-01f, 4.523403e-01f, 4.571131e-01f, 4.618246e-01f, 4.664766e-01f,
104 4.710712e-01f, 4.756104e-01f, 4.800958e-01f, 4.845292e-01f, 4.889122e-01f, 4.932462e-01f,
105 4.975329e-01f, 5.017734e-01f, 5.059693e-01f, 5.101216e-01f, 5.142317e-01f, 5.183006e-01f,
106 5.223295e-01f, 5.263194e-01f, 5.302714e-01f, 5.341862e-01f, 5.380651e-01f, 5.419087e-01f,
107 5.457181e-01f, 5.494938e-01f, 5.532369e-01f, 5.569480e-01f, 5.606278e-01f, 5.642771e-01f,
108 5.678965e-01f, 5.714868e-01f, 5.750484e-01f, 5.785821e-01f, 5.820884e-01f, 5.855680e-01f,
109 5.890211e-01f, 5.924487e-01f, 5.958509e-01f, 5.992285e-01f, 6.025819e-01f, 6.059114e-01f,
110 6.092176e-01f, 6.125010e-01f, 6.157619e-01f, 6.190008e-01f, 6.222180e-01f, 6.254140e-01f,
111 6.285890e-01f, 6.317436e-01f, 6.348780e-01f, 6.379926e-01f, 6.410878e-01f, 6.441637e-01f,
112 6.472208e-01f, 6.502595e-01f, 6.532799e-01f, 6.562824e-01f, 6.592672e-01f, 6.622347e-01f,
113 6.651851e-01f, 6.681187e-01f, 6.710356e-01f, 6.739363e-01f, 6.768209e-01f, 6.796897e-01f,
114 6.825429e-01f, 6.853807e-01f, 6.882034e-01f, 6.910111e-01f, 6.938041e-01f, 6.965826e-01f,
115 6.993468e-01f, 7.020969e-01f, 7.048331e-01f, 7.075556e-01f, 7.102645e-01f, 7.129600e-01f,
116 7.156424e-01f, 7.183118e-01f, 7.209683e-01f, 7.236121e-01f, 7.262435e-01f, 7.288625e-01f,
117 7.314693e-01f, 7.340640e-01f, 7.366470e-01f, 7.392181e-01f, 7.417776e-01f, 7.443256e-01f,
118 7.468624e-01f, 7.493880e-01f, 7.519025e-01f, 7.544061e-01f, 7.568989e-01f, 7.593810e-01f,
119 7.618526e-01f, 7.643137e-01f, 7.667645e-01f, 7.692052e-01f, 7.716358e-01f, 7.740564e-01f,
120 7.764671e-01f, 7.788681e-01f, 7.812595e-01f, 7.836413e-01f, 7.860138e-01f, 7.883768e-01f,
121 7.907307e-01f, 7.930754e-01f, 7.954110e-01f, 7.977377e-01f, 8.000556e-01f, 8.023647e-01f,
122 8.046651e-01f, 8.069569e-01f, 8.092403e-01f, 8.115152e-01f, 8.137818e-01f, 8.160402e-01f,
123 8.182903e-01f, 8.205324e-01f, 8.227665e-01f, 8.249926e-01f, 8.272109e-01f, 8.294214e-01f,
124 8.316242e-01f, 8.338194e-01f, 8.360070e-01f, 8.381871e-01f, 8.403597e-01f, 8.425251e-01f,
125 8.446831e-01f, 8.468339e-01f, 8.489776e-01f, 8.511142e-01f, 8.532437e-01f, 8.553662e-01f,
126 8.574819e-01f, 8.595907e-01f, 8.616927e-01f, 8.637881e-01f, 8.658767e-01f, 8.679587e-01f,
127 8.700342e-01f, 8.721032e-01f, 8.741657e-01f, 8.762218e-01f, 8.782716e-01f, 8.803151e-01f,
128 8.823524e-01f, 8.843835e-01f, 8.864085e-01f, 8.884274e-01f, 8.904402e-01f, 8.924471e-01f,
129 8.944480e-01f, 8.964431e-01f, 8.984324e-01f, 9.004158e-01f, 9.023935e-01f, 9.043654e-01f,
130 9.063318e-01f, 9.082925e-01f, 9.102476e-01f, 9.121972e-01f, 9.141413e-01f, 9.160800e-01f,
131 9.180133e-01f, 9.199412e-01f, 9.218637e-01f, 9.237810e-01f, 9.256931e-01f, 9.276000e-01f,
132 9.295017e-01f, 9.313982e-01f, 9.332896e-01f, 9.351761e-01f, 9.370575e-01f, 9.389339e-01f,
133 9.408054e-01f, 9.426719e-01f, 9.445336e-01f, 9.463905e-01f, 9.482424e-01f, 9.500897e-01f,
134 9.519322e-01f, 9.537700e-01f, 9.556032e-01f, 9.574316e-01f, 9.592555e-01f, 9.610748e-01f,
135 9.628896e-01f, 9.646998e-01f, 9.665055e-01f, 9.683068e-01f, 9.701037e-01f, 9.718961e-01f,
136 9.736842e-01f, 9.754679e-01f, 9.772474e-01f, 9.790225e-01f, 9.807934e-01f, 9.825601e-01f,
137 9.843225e-01f, 9.860808e-01f, 9.878350e-01f, 9.895850e-01f, 9.913309e-01f, 9.930727e-01f,
138 9.948106e-01f, 9.965444e-01f, 9.982741e-01f, 1.000000e+00f};
139
140} // namespace
141
142struct R32G32B32A32_FLOATTraits {
143 static constexpr size_t num_components = 4;
144 static constexpr std::array<ComponentType, num_components> component_types = {
145 ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT};
146 static constexpr std::array<size_t, num_components> component_sizes = {32, 32, 32, 32};
147 static constexpr std::array<Swizzle, num_components> component_swizzle = {
148 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A};
149};
150
151struct R32G32B32A32_SINTTraits {
152 static constexpr size_t num_components = 4;
153 static constexpr std::array<ComponentType, num_components> component_types = {
154 ComponentType::SINT, ComponentType::SINT, ComponentType::SINT, ComponentType::SINT};
155 static constexpr std::array<size_t, num_components> component_sizes = {32, 32, 32, 32};
156 static constexpr std::array<Swizzle, num_components> component_swizzle = {
157 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A};
158};
159
160struct R32G32B32A32_UINTTraits {
161 static constexpr size_t num_components = 4;
162 static constexpr std::array<ComponentType, num_components> component_types = {
163 ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT};
164 static constexpr std::array<size_t, num_components> component_sizes = {32, 32, 32, 32};
165 static constexpr std::array<Swizzle, num_components> component_swizzle = {
166 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A};
167};
168
169struct R32G32B32X32_FLOATTraits {
170 static constexpr size_t num_components = 4;
171 static constexpr std::array<ComponentType, num_components> component_types = {
172 ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT};
173 static constexpr std::array<size_t, num_components> component_sizes = {32, 32, 32, 32};
174 static constexpr std::array<Swizzle, num_components> component_swizzle = {
175 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::None};
176};
177
178struct R32G32B32X32_SINTTraits {
179 static constexpr size_t num_components = 4;
180 static constexpr std::array<ComponentType, num_components> component_types = {
181 ComponentType::SINT, ComponentType::SINT, ComponentType::SINT, ComponentType::SINT};
182 static constexpr std::array<size_t, num_components> component_sizes = {32, 32, 32, 32};
183 static constexpr std::array<Swizzle, num_components> component_swizzle = {
184 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::None};
185};
186
187struct R32G32B32X32_UINTTraits {
188 static constexpr size_t num_components = 4;
189 static constexpr std::array<ComponentType, num_components> component_types = {
190 ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT};
191 static constexpr std::array<size_t, num_components> component_sizes = {32, 32, 32, 32};
192 static constexpr std::array<Swizzle, num_components> component_swizzle = {
193 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::None};
194};
195
196struct R16G16B16A16_UNORMTraits {
197 static constexpr size_t num_components = 4;
198 static constexpr std::array<ComponentType, num_components> component_types = {
199 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
200 static constexpr std::array<size_t, num_components> component_sizes = {16, 16, 16, 16};
201 static constexpr std::array<Swizzle, num_components> component_swizzle = {
202 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A};
203};
204
205struct R16G16B16A16_SNORMTraits {
206 static constexpr size_t num_components = 4;
207 static constexpr std::array<ComponentType, num_components> component_types = {
208 ComponentType::SNORM, ComponentType::SNORM, ComponentType::SNORM, ComponentType::SNORM};
209 static constexpr std::array<size_t, num_components> component_sizes = {16, 16, 16, 16};
210 static constexpr std::array<Swizzle, num_components> component_swizzle = {
211 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A};
212};
213
214struct R16G16B16A16_SINTTraits {
215 static constexpr size_t num_components = 4;
216 static constexpr std::array<ComponentType, num_components> component_types = {
217 ComponentType::SINT, ComponentType::SINT, ComponentType::SINT, ComponentType::SINT};
218 static constexpr std::array<size_t, num_components> component_sizes = {16, 16, 16, 16};
219 static constexpr std::array<Swizzle, num_components> component_swizzle = {
220 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A};
221};
222
223struct R16G16B16A16_UINTTraits {
224 static constexpr size_t num_components = 4;
225 static constexpr std::array<ComponentType, num_components> component_types = {
226 ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT};
227 static constexpr std::array<size_t, num_components> component_sizes = {16, 16, 16, 16};
228 static constexpr std::array<Swizzle, num_components> component_swizzle = {
229 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A};
230};
231
232struct R16G16B16A16_FLOATTraits {
233 static constexpr size_t num_components = 4;
234 static constexpr std::array<ComponentType, num_components> component_types = {
235 ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT};
236 static constexpr std::array<size_t, num_components> component_sizes = {16, 16, 16, 16};
237 static constexpr std::array<Swizzle, num_components> component_swizzle = {
238 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::A};
239};
240
241struct R32G32_FLOATTraits {
242 static constexpr size_t num_components = 2;
243 static constexpr std::array<ComponentType, num_components> component_types = {
244 ComponentType::FLOAT, ComponentType::FLOAT};
245 static constexpr std::array<size_t, num_components> component_sizes = {32, 32};
246 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
247 Swizzle::G};
248};
249
250struct R32G32_SINTTraits {
251 static constexpr size_t num_components = 2;
252 static constexpr std::array<ComponentType, num_components> component_types = {
253 ComponentType::SINT, ComponentType::SINT};
254 static constexpr std::array<size_t, num_components> component_sizes = {32, 32};
255 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
256 Swizzle::G};
257};
258
259struct R32G32_UINTTraits {
260 static constexpr size_t num_components = 2;
261 static constexpr std::array<ComponentType, num_components> component_types = {
262 ComponentType::UINT, ComponentType::UINT};
263 static constexpr std::array<size_t, num_components> component_sizes = {32, 32};
264 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
265 Swizzle::G};
266};
267
268struct R16G16B16X16_FLOATTraits {
269 static constexpr size_t num_components = 4;
270 static constexpr std::array<ComponentType, num_components> component_types = {
271 ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT};
272 static constexpr std::array<size_t, num_components> component_sizes = {16, 16, 16, 16};
273 static constexpr std::array<Swizzle, num_components> component_swizzle = {
274 Swizzle::R, Swizzle::G, Swizzle::B, Swizzle::None};
275};
276
277struct A8R8G8B8_UNORMTraits {
278 static constexpr size_t num_components = 4;
279 static constexpr std::array<ComponentType, num_components> component_types = {
280 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
281 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
282 static constexpr std::array<Swizzle, num_components> component_swizzle = {
283 Swizzle::A, Swizzle::R, Swizzle::G, Swizzle::B};
284};
285
286struct A8R8G8B8_SRGBTraits {
287 static constexpr size_t num_components = 4;
288 static constexpr std::array<ComponentType, num_components> component_types = {
289 ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB};
290 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
291 static constexpr std::array<Swizzle, num_components> component_swizzle = {
292 Swizzle::A, Swizzle::R, Swizzle::G, Swizzle::B};
293};
294
295struct A2B10G10R10_UNORMTraits {
296 static constexpr size_t num_components = 4;
297 static constexpr std::array<ComponentType, num_components> component_types = {
298 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
299 static constexpr std::array<size_t, num_components> component_sizes = {2, 10, 10, 10};
300 static constexpr std::array<Swizzle, num_components> component_swizzle = {
301 Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R};
302};
303
304struct A2B10G10R10_UINTTraits {
305 static constexpr size_t num_components = 4;
306 static constexpr std::array<ComponentType, num_components> component_types = {
307 ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT};
308 static constexpr std::array<size_t, num_components> component_sizes = {2, 10, 10, 10};
309 static constexpr std::array<Swizzle, num_components> component_swizzle = {
310 Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R};
311};
312
313struct A2R10G10B10_UNORMTraits {
314 static constexpr size_t num_components = 4;
315 static constexpr std::array<ComponentType, num_components> component_types = {
316 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
317 static constexpr std::array<size_t, num_components> component_sizes = {2, 10, 10, 10};
318 static constexpr std::array<Swizzle, num_components> component_swizzle = {
319 Swizzle::A, Swizzle::R, Swizzle::G, Swizzle::B};
320};
321
322struct A8B8G8R8_UNORMTraits {
323 static constexpr size_t num_components = 4;
324 static constexpr std::array<ComponentType, num_components> component_types = {
325 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
326 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
327 static constexpr std::array<Swizzle, num_components> component_swizzle = {
328 Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R};
329};
330
331struct A8B8G8R8_SRGBTraits {
332 static constexpr size_t num_components = 4;
333 static constexpr std::array<ComponentType, num_components> component_types = {
334 ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB};
335 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
336 static constexpr std::array<Swizzle, num_components> component_swizzle = {
337 Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R};
338};
339
340struct A8B8G8R8_SNORMTraits {
341 static constexpr size_t num_components = 4;
342 static constexpr std::array<ComponentType, num_components> component_types = {
343 ComponentType::SNORM, ComponentType::SNORM, ComponentType::SNORM, ComponentType::SNORM};
344 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
345 static constexpr std::array<Swizzle, num_components> component_swizzle = {
346 Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R};
347};
348
349struct A8B8G8R8_SINTTraits {
350 static constexpr size_t num_components = 4;
351 static constexpr std::array<ComponentType, num_components> component_types = {
352 ComponentType::SINT, ComponentType::SINT, ComponentType::SINT, ComponentType::SINT};
353 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
354 static constexpr std::array<Swizzle, num_components> component_swizzle = {
355 Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R};
356};
357
358struct A8B8G8R8_UINTTraits {
359 static constexpr size_t num_components = 4;
360 static constexpr std::array<ComponentType, num_components> component_types = {
361 ComponentType::UINT, ComponentType::UINT, ComponentType::UINT, ComponentType::UINT};
362 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
363 static constexpr std::array<Swizzle, num_components> component_swizzle = {
364 Swizzle::A, Swizzle::B, Swizzle::G, Swizzle::R};
365};
366
367struct R16G16_UNORMTraits {
368 static constexpr size_t num_components = 2;
369 static constexpr std::array<ComponentType, num_components> component_types = {
370 ComponentType::UNORM, ComponentType::UNORM};
371 static constexpr std::array<size_t, num_components> component_sizes = {16, 16};
372 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
373 Swizzle::G};
374};
375
376struct R16G16_SNORMTraits {
377 static constexpr size_t num_components = 2;
378 static constexpr std::array<ComponentType, num_components> component_types = {
379 ComponentType::SNORM, ComponentType::SNORM};
380 static constexpr std::array<size_t, num_components> component_sizes = {16, 16};
381 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
382 Swizzle::G};
383};
384
385struct R16G16_SINTTraits {
386 static constexpr size_t num_components = 2;
387 static constexpr std::array<ComponentType, num_components> component_types = {
388 ComponentType::SINT, ComponentType::SINT};
389 static constexpr std::array<size_t, num_components> component_sizes = {16, 16};
390 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
391 Swizzle::G};
392};
393
394struct R16G16_UINTTraits {
395 static constexpr size_t num_components = 2;
396 static constexpr std::array<ComponentType, num_components> component_types = {
397 ComponentType::UINT, ComponentType::UINT};
398 static constexpr std::array<size_t, num_components> component_sizes = {16, 16};
399 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
400 Swizzle::G};
401};
402
403struct R16G16_FLOATTraits {
404 static constexpr size_t num_components = 2;
405 static constexpr std::array<ComponentType, num_components> component_types = {
406 ComponentType::FLOAT, ComponentType::FLOAT};
407 static constexpr std::array<size_t, num_components> component_sizes = {16, 16};
408 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
409 Swizzle::G};
410};
411
412struct B10G11R11_FLOATTraits {
413 static constexpr size_t num_components = 3;
414 static constexpr std::array<ComponentType, num_components> component_types = {
415 ComponentType::FLOAT, ComponentType::FLOAT, ComponentType::FLOAT};
416 static constexpr std::array<size_t, num_components> component_sizes = {10, 11, 11};
417 static constexpr std::array<Swizzle, num_components> component_swizzle = {
418 Swizzle::B, Swizzle::G, Swizzle::R};
419};
420
421struct R32_SINTTraits {
422 static constexpr size_t num_components = 1;
423 static constexpr std::array<ComponentType, num_components> component_types = {
424 ComponentType::SINT};
425 static constexpr std::array<size_t, num_components> component_sizes = {32};
426 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
427};
428
429struct R32_UINTTraits {
430 static constexpr size_t num_components = 1;
431 static constexpr std::array<ComponentType, num_components> component_types = {
432 ComponentType::UINT};
433 static constexpr std::array<size_t, num_components> component_sizes = {32};
434 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
435};
436
437struct R32_FLOATTraits {
438 static constexpr size_t num_components = 1;
439 static constexpr std::array<ComponentType, num_components> component_types = {
440 ComponentType::FLOAT};
441 static constexpr std::array<size_t, num_components> component_sizes = {32};
442 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
443};
444
445struct X8R8G8B8_UNORMTraits {
446 static constexpr size_t num_components = 4;
447 static constexpr std::array<ComponentType, num_components> component_types = {
448 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
449 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
450 static constexpr std::array<Swizzle, num_components> component_swizzle = {
451 Swizzle::None, Swizzle::R, Swizzle::G, Swizzle::B};
452};
453
454struct X8R8G8B8_SRGBTraits {
455 static constexpr size_t num_components = 4;
456 static constexpr std::array<ComponentType, num_components> component_types = {
457 ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB};
458 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
459 static constexpr std::array<Swizzle, num_components> component_swizzle = {
460 Swizzle::None, Swizzle::R, Swizzle::G, Swizzle::B};
461};
462
463struct R5G6B5_UNORMTraits {
464 static constexpr size_t num_components = 3;
465 static constexpr std::array<ComponentType, num_components> component_types = {
466 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
467 static constexpr std::array<size_t, num_components> component_sizes = {5, 6, 5};
468 static constexpr std::array<Swizzle, num_components> component_swizzle = {
469 Swizzle::R, Swizzle::G, Swizzle::B};
470};
471
472struct A1R5G5B5_UNORMTraits {
473 static constexpr size_t num_components = 4;
474 static constexpr std::array<ComponentType, num_components> component_types = {
475 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
476 static constexpr std::array<size_t, num_components> component_sizes = {1, 5, 5, 5};
477 static constexpr std::array<Swizzle, num_components> component_swizzle = {
478 Swizzle::A, Swizzle::R, Swizzle::G, Swizzle::B};
479};
480
481struct R8G8_UNORMTraits {
482 static constexpr size_t num_components = 2;
483 static constexpr std::array<ComponentType, num_components> component_types = {
484 ComponentType::UNORM, ComponentType::UNORM};
485 static constexpr std::array<size_t, num_components> component_sizes = {8, 8};
486 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
487 Swizzle::G};
488};
489
490struct R8G8_SNORMTraits {
491 static constexpr size_t num_components = 2;
492 static constexpr std::array<ComponentType, num_components> component_types = {
493 ComponentType::SNORM, ComponentType::SNORM};
494 static constexpr std::array<size_t, num_components> component_sizes = {8, 8};
495 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
496 Swizzle::G};
497};
498
499struct R8G8_SINTTraits {
500 static constexpr size_t num_components = 2;
501 static constexpr std::array<ComponentType, num_components> component_types = {
502 ComponentType::SINT, ComponentType::SINT};
503 static constexpr std::array<size_t, num_components> component_sizes = {8, 8};
504 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
505 Swizzle::G};
506};
507
508struct R8G8_UINTTraits {
509 static constexpr size_t num_components = 2;
510 static constexpr std::array<ComponentType, num_components> component_types = {
511 ComponentType::UINT, ComponentType::UINT};
512 static constexpr std::array<size_t, num_components> component_sizes = {8, 8};
513 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R,
514 Swizzle::G};
515};
516
517struct R16_UNORMTraits {
518 static constexpr size_t num_components = 1;
519 static constexpr std::array<ComponentType, num_components> component_types = {
520 ComponentType::UNORM};
521 static constexpr std::array<size_t, num_components> component_sizes = {16};
522 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
523};
524
525struct R16_SNORMTraits {
526 static constexpr size_t num_components = 1;
527 static constexpr std::array<ComponentType, num_components> component_types = {
528 ComponentType::SNORM};
529 static constexpr std::array<size_t, num_components> component_sizes = {16};
530 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
531};
532
533struct R16_SINTTraits {
534 static constexpr size_t num_components = 1;
535 static constexpr std::array<ComponentType, num_components> component_types = {
536 ComponentType::SINT};
537 static constexpr std::array<size_t, num_components> component_sizes = {16};
538 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
539};
540
541struct R16_UINTTraits {
542 static constexpr size_t num_components = 1;
543 static constexpr std::array<ComponentType, num_components> component_types = {
544 ComponentType::UINT};
545 static constexpr std::array<size_t, num_components> component_sizes = {16};
546 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
547};
548
549struct R16_FLOATTraits {
550 static constexpr size_t num_components = 1;
551 static constexpr std::array<ComponentType, num_components> component_types = {
552 ComponentType::FLOAT};
553 static constexpr std::array<size_t, num_components> component_sizes = {16};
554 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
555};
556
557struct R8_UNORMTraits {
558 static constexpr size_t num_components = 1;
559 static constexpr std::array<ComponentType, num_components> component_types = {
560 ComponentType::UNORM};
561 static constexpr std::array<size_t, num_components> component_sizes = {8};
562 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
563};
564
565struct R8_SNORMTraits {
566 static constexpr size_t num_components = 1;
567 static constexpr std::array<ComponentType, num_components> component_types = {
568 ComponentType::SNORM};
569 static constexpr std::array<size_t, num_components> component_sizes = {8};
570 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
571};
572
573struct R8_SINTTraits {
574 static constexpr size_t num_components = 1;
575 static constexpr std::array<ComponentType, num_components> component_types = {
576 ComponentType::SINT};
577 static constexpr std::array<size_t, num_components> component_sizes = {8};
578 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
579};
580
581struct R8_UINTTraits {
582 static constexpr size_t num_components = 1;
583 static constexpr std::array<ComponentType, num_components> component_types = {
584 ComponentType::UINT};
585 static constexpr std::array<size_t, num_components> component_sizes = {8};
586 static constexpr std::array<Swizzle, num_components> component_swizzle = {Swizzle::R};
587};
588
589struct X1R5G5B5_UNORMTraits {
590 static constexpr size_t num_components = 4;
591 static constexpr std::array<ComponentType, num_components> component_types = {
592 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
593 static constexpr std::array<size_t, num_components> component_sizes = {1, 5, 5, 5};
594 static constexpr std::array<Swizzle, num_components> component_swizzle = {
595 Swizzle::None, Swizzle::R, Swizzle::G, Swizzle::B};
596};
597
598struct X8B8G8R8_UNORMTraits {
599 static constexpr size_t num_components = 4;
600 static constexpr std::array<ComponentType, num_components> component_types = {
601 ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM, ComponentType::UNORM};
602 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
603 static constexpr std::array<Swizzle, num_components> component_swizzle = {
604 Swizzle::None, Swizzle::B, Swizzle::G, Swizzle::R};
605};
606
607struct X8B8G8R8_SRGBTraits {
608 static constexpr size_t num_components = 4;
609 static constexpr std::array<ComponentType, num_components> component_types = {
610 ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB, ComponentType::SRGB};
611 static constexpr std::array<size_t, num_components> component_sizes = {8, 8, 8, 8};
612 static constexpr std::array<Swizzle, num_components> component_swizzle = {
613 Swizzle::None, Swizzle::B, Swizzle::G, Swizzle::R};
614};
615
616template <class ConverterTraits>
617class ConverterImpl : public Converter {
618private:
619 static constexpr size_t num_components = ConverterTraits::num_components;
620 static constexpr std::array<ComponentType, num_components> component_types =
621 ConverterTraits::component_types;
622 static constexpr std::array<size_t, num_components> component_sizes =
623 ConverterTraits::component_sizes;
624 static constexpr std::array<Swizzle, num_components> component_swizzle =
625 ConverterTraits::component_swizzle;
626
627 static constexpr size_t CalculateByteSize() {
628 size_t size = 0;
629 for (const size_t component_size : component_sizes) {
630 size += component_size;
631 }
632 const size_t power = (sizeof(size_t) * 8) - std::countl_zero(size) - 1ULL;
633 const size_t base_size = 1ULL << power;
634 const size_t mask = base_size - 1ULL;
635 return ((size & mask) != 0 ? base_size << 1ULL : base_size) / 8;
636 }
637
638 static constexpr size_t total_bytes_per_pixel = CalculateByteSize();
639 static constexpr size_t total_words_per_pixel =
640 (total_bytes_per_pixel + sizeof(u32) - 1U) / sizeof(u32);
641 static constexpr size_t components_per_ir_rep = 4;
642
643 template <bool get_offsets>
644 static constexpr std::array<size_t, num_components> GetBoundWordsOffsets() {
645 std::array<size_t, num_components> result;
646 result.fill(0);
647 constexpr size_t total_bits_per_word = sizeof(u32) * 8;
648 size_t accumulated_size = 0;
649 size_t count = 0;
650 for (size_t i = 0; i < num_components; i++) {
651 if constexpr (get_offsets) {
652 result[i] = accumulated_size;
653 } else {
654 result[i] = count;
655 }
656 accumulated_size += component_sizes[i];
657 if (accumulated_size > total_bits_per_word) {
658 if constexpr (get_offsets) {
659 result[i] = 0;
660 } else {
661 result[i]++;
662 }
663 count++;
664 accumulated_size = component_sizes[i];
665 }
666 }
667 return result;
668 }
669
670 static constexpr std::array<size_t, num_components> bound_words = GetBoundWordsOffsets<false>();
671 static constexpr std::array<size_t, num_components> bound_offsets =
672 GetBoundWordsOffsets<true>();
673
674 static constexpr std::array<u32, num_components> GetComponentsMask() {
675 std::array<u32, num_components> result;
676 for (size_t i = 0; i < num_components; i++) {
677 result[i] = (((u32)~0) >> (8 * sizeof(u32) - component_sizes[i])) << bound_offsets[i];
678 }
679 return result;
680 }
681
682 static constexpr std::array<u32, num_components> component_mask = GetComponentsMask();
683
684 // We are forcing inline so the compiler can SIMD the conversations, since it may do 4 function
685 // calls, it may fail to detect the benefit of inlining.
686 template <size_t which_component>
687 FORCE_INLINE void ConvertToComponent(u32 which_word, f32& out_component) {
688 const u32 value = (which_word >> bound_offsets[which_component]) &
689 static_cast<u32>((1ULL << component_sizes[which_component]) - 1ULL);
690 const auto sign_extend = [](u32 base_value, size_t bits) {
691 const size_t shift_amount = sizeof(u32) * 8 - bits;
692 s32 shifted_value = static_cast<s32>(base_value << shift_amount);
693 return shifted_value >> shift_amount;
694 };
695 const auto force_to_fp16 = [](f32 base_value) {
696 u32 tmp = std::bit_cast<u32>(base_value);
697 constexpr size_t fp32_mantissa_bits = 23;
698 constexpr size_t fp16_mantissa_bits = 10;
699 constexpr size_t mantissa_mask =
700 ~((1ULL << (fp32_mantissa_bits - fp16_mantissa_bits)) - 1ULL);
701 tmp = tmp & static_cast<u32>(mantissa_mask);
702 // TODO: force the exponent within the range of half float. Not needed in UNORM / SNORM
703 return std::bit_cast<f32>(tmp);
704 };
705 const auto from_fp_n = [&sign_extend](u32 base_value, size_t bits, size_t mantissa) {
706 constexpr size_t fp32_mantissa_bits = 23;
707 size_t shift_towards = fp32_mantissa_bits - mantissa;
708 const u32 new_value =
709 static_cast<u32>(sign_extend(base_value, bits) << shift_towards) & (~(1U << 31));
710 return std::bit_cast<f32>(new_value);
711 };
712 const auto calculate_snorm = [&]() {
713 return static_cast<f32>(
714 static_cast<f32>(sign_extend(value, component_sizes[which_component])) /
715 static_cast<f32>((1ULL << (component_sizes[which_component] - 1ULL)) - 1ULL));
716 };
717 const auto calculate_unorm = [&]() {
718 return static_cast<f32>(
719 static_cast<f32>(value) /
720 static_cast<f32>((1ULL << (component_sizes[which_component])) - 1ULL));
721 };
722 if constexpr (component_types[which_component] == ComponentType::SNORM) {
723 out_component = calculate_snorm();
724 } else if constexpr (component_types[which_component] == ComponentType::UNORM) {
725 out_component = calculate_unorm();
726 } else if constexpr (component_types[which_component] == ComponentType::SINT) {
727 out_component = static_cast<f32>(
728 static_cast<s32>(sign_extend(value, component_sizes[which_component])));
729 } else if constexpr (component_types[which_component] == ComponentType::UINT) {
730 out_component = static_cast<f32>(
731 static_cast<s32>(sign_extend(value, component_sizes[which_component])));
732 } else if constexpr (component_types[which_component] == ComponentType::SNORM_FORCE_FP16) {
733 out_component = calculate_snorm();
734 out_component = force_to_fp16(out_component);
735 } else if constexpr (component_types[which_component] == ComponentType::UNORM_FORCE_FP16) {
736 out_component = calculate_unorm();
737 out_component = force_to_fp16(out_component);
738 } else if constexpr (component_types[which_component] == ComponentType::FLOAT) {
739 if constexpr (component_sizes[which_component] == 32) {
740 out_component = std::bit_cast<f32>(value);
741 } else if constexpr (component_sizes[which_component] == 16) {
742 static constexpr u32 sign_mask = 0x8000;
743 static constexpr u32 mantissa_mask = 0x8000;
744 out_component = std::bit_cast<f32>(((value & sign_mask) << 16) |
745 (((value & 0x7c00) + 0x1C000) << 13) |
746 ((value & mantissa_mask) << 13));
747 } else {
748 out_component = from_fp_n(value, component_sizes[which_component],
749 component_sizes[which_component] - 5);
750 }
751 } else if constexpr (component_types[which_component] == ComponentType::SRGB) {
752 if constexpr (component_swizzle[which_component] == Swizzle::A) {
753 out_component = calculate_unorm();
754 } else if constexpr (component_sizes[which_component] == 8) {
755 out_component = SRGB_TO_RGB_LUT[value];
756 } else {
757 out_component = calculate_unorm();
758 UNIMPLEMENTED_MSG("SRGB Conversion with component sizes of {} is unimplemented",
759 component_sizes[which_component]);
760 }
761 }
762 }
763
764 // We are forcing inline so the compiler can SIMD the conversations, since it may do 4 function
765 // calls, it may fail to detect the benefit of inlining.
766 template <size_t which_component>
767 FORCE_INLINE void ConvertFromComponent(u32& which_word, f32 in_component) {
768 const auto insert_to_word = [&]<typename T>(T new_word) {
769 which_word |= (static_cast<u32>(new_word) << bound_offsets[which_component]) &
770 component_mask[which_component];
771 };
772 const auto to_fp_n = [](f32 base_value, size_t bits, size_t mantissa) {
773 constexpr size_t fp32_mantissa_bits = 23;
774 u32 tmp_value = std::bit_cast<u32>(std::max(base_value, 0.0f));
775 size_t shift_towards = fp32_mantissa_bits - mantissa;
776 return tmp_value >> shift_towards;
777 };
778 const auto calculate_unorm = [&]() {
779 return static_cast<u32>(
780 static_cast<f32>(in_component) *
781 static_cast<f32>((1ULL << (component_sizes[which_component])) - 1ULL));
782 };
783 if constexpr (component_types[which_component] == ComponentType::SNORM ||
784 component_types[which_component] == ComponentType::SNORM_FORCE_FP16) {
785 s32 tmp_word = static_cast<s32>(
786 static_cast<f32>(in_component) *
787 static_cast<f32>((1ULL << (component_sizes[which_component] - 1ULL)) - 1ULL));
788 insert_to_word(tmp_word);
789
790 } else if constexpr (component_types[which_component] == ComponentType::UNORM ||
791 component_types[which_component] == ComponentType::UNORM_FORCE_FP16) {
792 u32 tmp_word = calculate_unorm();
793 insert_to_word(tmp_word);
794 } else if constexpr (component_types[which_component] == ComponentType::SINT) {
795 s32 tmp_word = static_cast<s32>(in_component);
796 insert_to_word(tmp_word);
797 } else if constexpr (component_types[which_component] == ComponentType::UINT) {
798 u32 tmp_word = static_cast<u32>(in_component);
799 insert_to_word(tmp_word);
800 } else if constexpr (component_types[which_component] == ComponentType::FLOAT) {
801 if constexpr (component_sizes[which_component] == 32) {
802 u32 tmp_word = std::bit_cast<u32>(in_component);
803 insert_to_word(tmp_word);
804 } else if constexpr (component_sizes[which_component] == 16) {
805 static constexpr u32 sign_mask = 0x8000;
806 static constexpr u32 mantissa_mask = 0x03ff;
807 static constexpr u32 exponent_mask = 0x7c00;
808 const u32 tmp_word = std::bit_cast<u32>(in_component);
809 const u32 half = ((tmp_word >> 16) & sign_mask) |
810 ((((tmp_word & 0x7f800000) - 0x38000000) >> 13) & exponent_mask) |
811 ((tmp_word >> 13) & mantissa_mask);
812 insert_to_word(half);
813 } else {
814 insert_to_word(to_fp_n(in_component, component_sizes[which_component],
815 component_sizes[which_component] - 5));
816 }
817 } else if constexpr (component_types[which_component] == ComponentType::SRGB) {
818 if constexpr (component_swizzle[which_component] != Swizzle::A) {
819 if constexpr (component_sizes[which_component] == 8) {
820 const u32 index = calculate_unorm();
821 in_component = RGB_TO_SRGB_LUT[index];
822 } else {
823 UNIMPLEMENTED_MSG("SRGB Conversion with component sizes of {} is unimplemented",
824 component_sizes[which_component]);
825 }
826 }
827 const u32 tmp_word = calculate_unorm();
828 insert_to_word(tmp_word);
829 }
830 }
831
832public:
833 void ConvertTo(std::span<const u8> input, std::span<f32> output) override {
834 const size_t num_pixels = output.size() / components_per_ir_rep;
835 for (size_t pixel = 0; pixel < num_pixels; pixel++) {
836 std::array<u32, total_words_per_pixel> words{};
837
838 std::memcpy(words.data(), &input[pixel * total_bytes_per_pixel], total_bytes_per_pixel);
839 std::span<f32> new_components(&output[pixel * components_per_ir_rep],
840 components_per_ir_rep);
841 if constexpr (component_swizzle[0] != Swizzle::None) {
842 ConvertToComponent<0>(words[bound_words[0]],
843 new_components[static_cast<size_t>(component_swizzle[0])]);
844 } else {
845 new_components[0] = 0.0f;
846 }
847 if constexpr (num_components >= 2) {
848 if constexpr (component_swizzle[1] != Swizzle::None) {
849 ConvertToComponent<1>(
850 words[bound_words[1]],
851 new_components[static_cast<size_t>(component_swizzle[1])]);
852 } else {
853 new_components[1] = 0.0f;
854 }
855 } else {
856 new_components[1] = 0.0f;
857 }
858 if constexpr (num_components >= 3) {
859 if constexpr (component_swizzle[2] != Swizzle::None) {
860 ConvertToComponent<2>(
861 words[bound_words[2]],
862 new_components[static_cast<size_t>(component_swizzle[2])]);
863 } else {
864 new_components[2] = 0.0f;
865 }
866 } else {
867 new_components[2] = 0.0f;
868 }
869 if constexpr (num_components >= 4) {
870 if constexpr (component_swizzle[3] != Swizzle::None) {
871 ConvertToComponent<3>(
872 words[bound_words[3]],
873 new_components[static_cast<size_t>(component_swizzle[3])]);
874 } else {
875 new_components[3] = 0.0f;
876 }
877 } else {
878 new_components[3] = 0.0f;
879 }
880 }
881 }
882
883 void ConvertFrom(std::span<const f32> input, std::span<u8> output) override {
884 const size_t num_pixels = output.size() / total_bytes_per_pixel;
885 for (size_t pixel = 0; pixel < num_pixels; pixel++) {
886 std::span<const f32> old_components(&input[pixel * components_per_ir_rep],
887 components_per_ir_rep);
888 std::array<u32, total_words_per_pixel> words{};
889 if constexpr (component_swizzle[0] != Swizzle::None) {
890 ConvertFromComponent<0>(words[bound_words[0]],
891 old_components[static_cast<size_t>(component_swizzle[0])]);
892 }
893 if constexpr (num_components >= 2) {
894 if constexpr (component_swizzle[1] != Swizzle::None) {
895 ConvertFromComponent<1>(
896 words[bound_words[1]],
897 old_components[static_cast<size_t>(component_swizzle[1])]);
898 }
899 }
900 if constexpr (num_components >= 3) {
901 if constexpr (component_swizzle[2] != Swizzle::None) {
902 ConvertFromComponent<2>(
903 words[bound_words[2]],
904 old_components[static_cast<size_t>(component_swizzle[2])]);
905 }
906 }
907 if constexpr (num_components >= 4) {
908 if constexpr (component_swizzle[3] != Swizzle::None) {
909 ConvertFromComponent<3>(
910 words[bound_words[3]],
911 old_components[static_cast<size_t>(component_swizzle[3])]);
912 }
913 }
914 std::memcpy(&output[pixel * total_bytes_per_pixel], words.data(),
915 total_bytes_per_pixel);
916 }
917 }
918
919 ConverterImpl() = default;
920 ~ConverterImpl() override = default;
921};
922
923struct ConverterFactory::ConverterFactoryImpl {
924 std::unordered_map<RenderTargetFormat, std::unique_ptr<Converter>> converters_cache;
925};
926
927ConverterFactory::ConverterFactory() {
928 impl = std::make_unique<ConverterFactoryImpl>();
929}
930
931ConverterFactory::~ConverterFactory() = default;
932
933Converter* ConverterFactory::GetFormatConverter(RenderTargetFormat format) {
934 auto it = impl->converters_cache.find(format);
935 if (it == impl->converters_cache.end()) [[unlikely]] {
936 return BuildConverter(format);
937 }
938 return it->second.get();
939}
940
941class NullConverter : public Converter {
942public:
943 void ConvertTo([[maybe_unused]] std::span<const u8> input, std::span<f32> output) override {
944 std::fill(output.begin(), output.end(), 0.0f);
945 }
946 void ConvertFrom([[maybe_unused]] std::span<const f32> input, std::span<u8> output) override {
947 const u8 fill_value = 0U;
948 std::fill(output.begin(), output.end(), fill_value);
949 }
950 NullConverter() = default;
951 ~NullConverter() = default;
952};
953
954Converter* ConverterFactory::BuildConverter(RenderTargetFormat format) {
955 switch (format) {
956 case RenderTargetFormat::R32G32B32A32_FLOAT:
957 return impl->converters_cache
958 .emplace(format, std::make_unique<ConverterImpl<R32G32B32A32_FLOATTraits>>())
959 .first->second.get();
960 break;
961 case RenderTargetFormat::R32G32B32A32_SINT:
962 return impl->converters_cache
963 .emplace(format, std::make_unique<ConverterImpl<R32G32B32A32_SINTTraits>>())
964 .first->second.get();
965 break;
966 case RenderTargetFormat::R32G32B32A32_UINT:
967 return impl->converters_cache
968 .emplace(format, std::make_unique<ConverterImpl<R32G32B32A32_UINTTraits>>())
969 .first->second.get();
970 break;
971 case RenderTargetFormat::R32G32B32X32_FLOAT:
972 return impl->converters_cache
973 .emplace(format, std::make_unique<ConverterImpl<R32G32B32X32_FLOATTraits>>())
974 .first->second.get();
975 break;
976 case RenderTargetFormat::R32G32B32X32_SINT:
977 return impl->converters_cache
978 .emplace(format, std::make_unique<ConverterImpl<R32G32B32X32_SINTTraits>>())
979 .first->second.get();
980 break;
981 case RenderTargetFormat::R32G32B32X32_UINT:
982 return impl->converters_cache
983 .emplace(format, std::make_unique<ConverterImpl<R32G32B32X32_UINTTraits>>())
984 .first->second.get();
985 break;
986 case RenderTargetFormat::R16G16B16A16_UNORM:
987 return impl->converters_cache
988 .emplace(format, std::make_unique<ConverterImpl<R16G16B16A16_UNORMTraits>>())
989 .first->second.get();
990 break;
991 case RenderTargetFormat::R16G16B16A16_SNORM:
992 return impl->converters_cache
993 .emplace(format, std::make_unique<ConverterImpl<R16G16B16A16_SNORMTraits>>())
994 .first->second.get();
995 break;
996 case RenderTargetFormat::R16G16B16A16_SINT:
997 return impl->converters_cache
998 .emplace(format, std::make_unique<ConverterImpl<R16G16B16A16_SINTTraits>>())
999 .first->second.get();
1000 break;
1001 case RenderTargetFormat::R16G16B16A16_UINT:
1002 return impl->converters_cache
1003 .emplace(format, std::make_unique<ConverterImpl<R16G16B16A16_UINTTraits>>())
1004 .first->second.get();
1005 break;
1006 case RenderTargetFormat::R16G16B16A16_FLOAT:
1007 return impl->converters_cache
1008 .emplace(format, std::make_unique<ConverterImpl<R16G16B16A16_FLOATTraits>>())
1009 .first->second.get();
1010 break;
1011 case RenderTargetFormat::R32G32_FLOAT:
1012 return impl->converters_cache
1013 .emplace(format, std::make_unique<ConverterImpl<R32G32_FLOATTraits>>())
1014 .first->second.get();
1015 break;
1016 case RenderTargetFormat::R32G32_SINT:
1017 return impl->converters_cache
1018 .emplace(format, std::make_unique<ConverterImpl<R32G32_SINTTraits>>())
1019 .first->second.get();
1020 break;
1021 case RenderTargetFormat::R32G32_UINT:
1022 return impl->converters_cache
1023 .emplace(format, std::make_unique<ConverterImpl<R32G32_UINTTraits>>())
1024 .first->second.get();
1025 break;
1026 case RenderTargetFormat::R16G16B16X16_FLOAT:
1027 return impl->converters_cache
1028 .emplace(format, std::make_unique<ConverterImpl<R16G16B16X16_FLOATTraits>>())
1029 .first->second.get();
1030 break;
1031 case RenderTargetFormat::A8R8G8B8_UNORM:
1032 return impl->converters_cache
1033 .emplace(format, std::make_unique<ConverterImpl<A8R8G8B8_UNORMTraits>>())
1034 .first->second.get();
1035 break;
1036 case RenderTargetFormat::A8R8G8B8_SRGB:
1037 return impl->converters_cache
1038 .emplace(format, std::make_unique<ConverterImpl<A8R8G8B8_SRGBTraits>>())
1039 .first->second.get();
1040 break;
1041 case RenderTargetFormat::A2B10G10R10_UNORM:
1042 return impl->converters_cache
1043 .emplace(format, std::make_unique<ConverterImpl<A2B10G10R10_UNORMTraits>>())
1044 .first->second.get();
1045 break;
1046 case RenderTargetFormat::A2B10G10R10_UINT:
1047 return impl->converters_cache
1048 .emplace(format, std::make_unique<ConverterImpl<A2B10G10R10_UINTTraits>>())
1049 .first->second.get();
1050 break;
1051 case RenderTargetFormat::A2R10G10B10_UNORM:
1052 return impl->converters_cache
1053 .emplace(format, std::make_unique<ConverterImpl<A2R10G10B10_UNORMTraits>>())
1054 .first->second.get();
1055 break;
1056 case RenderTargetFormat::A8B8G8R8_UNORM:
1057 return impl->converters_cache
1058 .emplace(format, std::make_unique<ConverterImpl<A8B8G8R8_UNORMTraits>>())
1059 .first->second.get();
1060 break;
1061 case RenderTargetFormat::A8B8G8R8_SRGB:
1062 return impl->converters_cache
1063 .emplace(format, std::make_unique<ConverterImpl<A8B8G8R8_SRGBTraits>>())
1064 .first->second.get();
1065 break;
1066 case RenderTargetFormat::A8B8G8R8_SNORM:
1067 return impl->converters_cache
1068 .emplace(format, std::make_unique<ConverterImpl<A8B8G8R8_SNORMTraits>>())
1069 .first->second.get();
1070 break;
1071 case RenderTargetFormat::A8B8G8R8_SINT:
1072 return impl->converters_cache
1073 .emplace(format, std::make_unique<ConverterImpl<A8B8G8R8_SINTTraits>>())
1074 .first->second.get();
1075 break;
1076 case RenderTargetFormat::A8B8G8R8_UINT:
1077 return impl->converters_cache
1078 .emplace(format, std::make_unique<ConverterImpl<A8B8G8R8_UINTTraits>>())
1079 .first->second.get();
1080 break;
1081 case RenderTargetFormat::R16G16_UNORM:
1082 return impl->converters_cache
1083 .emplace(format, std::make_unique<ConverterImpl<R16G16_UNORMTraits>>())
1084 .first->second.get();
1085 break;
1086 case RenderTargetFormat::R16G16_SNORM:
1087 return impl->converters_cache
1088 .emplace(format, std::make_unique<ConverterImpl<R16G16_SNORMTraits>>())
1089 .first->second.get();
1090 break;
1091 case RenderTargetFormat::R16G16_SINT:
1092 return impl->converters_cache
1093 .emplace(format, std::make_unique<ConverterImpl<R16G16_SINTTraits>>())
1094 .first->second.get();
1095 break;
1096 case RenderTargetFormat::R16G16_UINT:
1097 return impl->converters_cache
1098 .emplace(format, std::make_unique<ConverterImpl<R16G16_UINTTraits>>())
1099 .first->second.get();
1100 break;
1101 case RenderTargetFormat::R16G16_FLOAT:
1102 return impl->converters_cache
1103 .emplace(format, std::make_unique<ConverterImpl<R16G16_FLOATTraits>>())
1104 .first->second.get();
1105 break;
1106 case RenderTargetFormat::B10G11R11_FLOAT:
1107 return impl->converters_cache
1108 .emplace(format, std::make_unique<ConverterImpl<B10G11R11_FLOATTraits>>())
1109 .first->second.get();
1110 break;
1111 case RenderTargetFormat::R32_SINT:
1112 return impl->converters_cache
1113 .emplace(format, std::make_unique<ConverterImpl<R32_SINTTraits>>())
1114 .first->second.get();
1115 break;
1116 case RenderTargetFormat::R32_UINT:
1117 return impl->converters_cache
1118 .emplace(format, std::make_unique<ConverterImpl<R32_UINTTraits>>())
1119 .first->second.get();
1120 break;
1121 case RenderTargetFormat::R32_FLOAT:
1122 return impl->converters_cache
1123 .emplace(format, std::make_unique<ConverterImpl<R32_FLOATTraits>>())
1124 .first->second.get();
1125 break;
1126 case RenderTargetFormat::X8R8G8B8_UNORM:
1127 return impl->converters_cache
1128 .emplace(format, std::make_unique<ConverterImpl<X8R8G8B8_UNORMTraits>>())
1129 .first->second.get();
1130 break;
1131 case RenderTargetFormat::X8R8G8B8_SRGB:
1132 return impl->converters_cache
1133 .emplace(format, std::make_unique<ConverterImpl<X8R8G8B8_SRGBTraits>>())
1134 .first->second.get();
1135 break;
1136 case RenderTargetFormat::R5G6B5_UNORM:
1137 return impl->converters_cache
1138 .emplace(format, std::make_unique<ConverterImpl<R5G6B5_UNORMTraits>>())
1139 .first->second.get();
1140 break;
1141 case RenderTargetFormat::A1R5G5B5_UNORM:
1142 return impl->converters_cache
1143 .emplace(format, std::make_unique<ConverterImpl<A1R5G5B5_UNORMTraits>>())
1144 .first->second.get();
1145 break;
1146 case RenderTargetFormat::R8G8_UNORM:
1147 return impl->converters_cache
1148 .emplace(format, std::make_unique<ConverterImpl<R8G8_UNORMTraits>>())
1149 .first->second.get();
1150 break;
1151 case RenderTargetFormat::R8G8_SNORM:
1152 return impl->converters_cache
1153 .emplace(format, std::make_unique<ConverterImpl<R8G8_SNORMTraits>>())
1154 .first->second.get();
1155 break;
1156 case RenderTargetFormat::R8G8_SINT:
1157 return impl->converters_cache
1158 .emplace(format, std::make_unique<ConverterImpl<R8G8_SINTTraits>>())
1159 .first->second.get();
1160 break;
1161 case RenderTargetFormat::R8G8_UINT:
1162 return impl->converters_cache
1163 .emplace(format, std::make_unique<ConverterImpl<R8G8_UINTTraits>>())
1164 .first->second.get();
1165 break;
1166 case RenderTargetFormat::R16_UNORM:
1167 return impl->converters_cache
1168 .emplace(format, std::make_unique<ConverterImpl<R16_UNORMTraits>>())
1169 .first->second.get();
1170 break;
1171 case RenderTargetFormat::R16_SNORM:
1172 return impl->converters_cache
1173 .emplace(format, std::make_unique<ConverterImpl<R16_SNORMTraits>>())
1174 .first->second.get();
1175 break;
1176 case RenderTargetFormat::R16_SINT:
1177 return impl->converters_cache
1178 .emplace(format, std::make_unique<ConverterImpl<R16_SINTTraits>>())
1179 .first->second.get();
1180 break;
1181 case RenderTargetFormat::R16_UINT:
1182 return impl->converters_cache
1183 .emplace(format, std::make_unique<ConverterImpl<R16_UINTTraits>>())
1184 .first->second.get();
1185 break;
1186 case RenderTargetFormat::R16_FLOAT:
1187 return impl->converters_cache
1188 .emplace(format, std::make_unique<ConverterImpl<R16_FLOATTraits>>())
1189 .first->second.get();
1190 break;
1191 case RenderTargetFormat::R8_UNORM:
1192 return impl->converters_cache
1193 .emplace(format, std::make_unique<ConverterImpl<R8_UNORMTraits>>())
1194 .first->second.get();
1195 break;
1196 case RenderTargetFormat::R8_SNORM:
1197 return impl->converters_cache
1198 .emplace(format, std::make_unique<ConverterImpl<R8_SNORMTraits>>())
1199 .first->second.get();
1200 break;
1201 case RenderTargetFormat::R8_SINT:
1202 return impl->converters_cache
1203 .emplace(format, std::make_unique<ConverterImpl<R8_SINTTraits>>())
1204 .first->second.get();
1205 break;
1206 case RenderTargetFormat::R8_UINT:
1207 return impl->converters_cache
1208 .emplace(format, std::make_unique<ConverterImpl<R8_UINTTraits>>())
1209 .first->second.get();
1210 break;
1211 case RenderTargetFormat::X1R5G5B5_UNORM:
1212 return impl->converters_cache
1213 .emplace(format, std::make_unique<ConverterImpl<X1R5G5B5_UNORMTraits>>())
1214 .first->second.get();
1215 break;
1216 case RenderTargetFormat::X8B8G8R8_UNORM:
1217 return impl->converters_cache
1218 .emplace(format, std::make_unique<ConverterImpl<X8B8G8R8_UNORMTraits>>())
1219 .first->second.get();
1220 break;
1221 case RenderTargetFormat::X8B8G8R8_SRGB:
1222 return impl->converters_cache
1223 .emplace(format, std::make_unique<ConverterImpl<X8B8G8R8_SRGBTraits>>())
1224 .first->second.get();
1225 break;
1226 default: {
1227 UNIMPLEMENTED_MSG("This format {} converter is not implemented", format);
1228 return impl->converters_cache.emplace(format, std::make_unique<NullConverter>())
1229 .first->second.get();
1230 }
1231 }
1232}
1233
1234} // namespace Tegra::Engines::Blitter
diff --git a/src/video_core/engines/sw_blitter/converter.h b/src/video_core/engines/sw_blitter/converter.h
new file mode 100644
index 000000000..f9bdc516e
--- /dev/null
+++ b/src/video_core/engines/sw_blitter/converter.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <span>
8
9#include "common/common_types.h"
10
11#include "video_core/gpu.h"
12
13namespace Tegra::Engines::Blitter {
14
15class Converter {
16public:
17 virtual void ConvertTo(std::span<const u8> input, std::span<f32> output) = 0;
18 virtual void ConvertFrom(std::span<const f32> input, std::span<u8> output) = 0;
19 virtual ~Converter() = default;
20};
21
22class ConverterFactory {
23public:
24 ConverterFactory();
25 ~ConverterFactory();
26
27 Converter* GetFormatConverter(RenderTargetFormat format);
28
29private:
30 Converter* BuildConverter(RenderTargetFormat format);
31
32 struct ConverterFactoryImpl;
33 std::unique_ptr<ConverterFactoryImpl> impl;
34};
35
36} // namespace Tegra::Engines::Blitter
diff --git a/src/video_core/engines/sw_blitter/generate_converters.py b/src/video_core/engines/sw_blitter/generate_converters.py
new file mode 100644
index 000000000..f641564f7
--- /dev/null
+++ b/src/video_core/engines/sw_blitter/generate_converters.py
@@ -0,0 +1,136 @@
1# SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-3.0-or-later
3
4import re
5
6class Format:
7 def __init__(self, string_value):
8 self.name = string_value
9 tmp = string_value.split('_')
10 self.component_type = tmp[1]
11 component_data = re.findall(r"\w\d+", tmp[0])
12 self.num_components = len(component_data)
13 sizes = []
14 swizzle = []
15 for data in component_data:
16 swizzle.append(data[0])
17 sizes.append(int(data[1:]))
18 self.sizes = sizes
19 self.swizzle = swizzle
20
21 def build_component_type_array(self):
22 result = "{ "
23 b = False
24 for i in range(0, self.num_components):
25 if b:
26 result += ", "
27 b = True
28 result += "ComponentType::" + self.component_type
29 result += " }"
30 return result
31
32 def build_component_sizes_array(self):
33 result = "{ "
34 b = False
35 for i in range(0, self.num_components):
36 if b:
37 result += ", "
38 b = True
39 result += str(self.sizes[i])
40 result += " }"
41 return result
42
43 def build_component_swizzle_array(self):
44 result = "{ "
45 b = False
46 for i in range(0, self.num_components):
47 if b:
48 result += ", "
49 b = True
50 swizzle = self.swizzle[i]
51 if swizzle == "X":
52 swizzle = "None"
53 result += "Swizzle::" + swizzle
54 result += " }"
55 return result
56
57 def print_declaration(self):
58 print("struct " + self.name + "Traits {")
59 print(" static constexpr size_t num_components = " + str(self.num_components) + ";")
60 print(" static constexpr std::array<ComponentType, num_components> component_types = " + self.build_component_type_array() + ";")
61 print(" static constexpr std::array<size_t, num_components> component_sizes = " + self.build_component_sizes_array() + ";")
62 print(" static constexpr std::array<Swizzle, num_components> component_swizzle = " + self.build_component_swizzle_array() + ";")
63 print("};\n")
64
65 def print_case(self):
66 print("case RenderTargetFormat::" + self.name + ":")
67 print(" return impl->converters_cache")
68 print(" .emplace(format, std::make_unique<ConverterImpl<" + self.name + "Traits>>())")
69 print(" .first->second.get();")
70 print(" break;")
71
72txt = """
73R32G32B32A32_FLOAT
74R32G32B32A32_SINT
75R32G32B32A32_UINT
76R32G32B32X32_FLOAT
77R32G32B32X32_SINT
78R32G32B32X32_UINT
79R16G16B16A16_UNORM
80R16G16B16A16_SNORM
81R16G16B16A16_SINT
82R16G16B16A16_UINT
83R16G16B16A16_FLOAT
84R32G32_FLOAT
85R32G32_SINT
86R32G32_UINT
87R16G16B16X16_FLOAT
88A8R8G8B8_UNORM
89A8R8G8B8_SRGB
90A2B10G10R10_UNORM
91A2B10G10R10_UINT
92A2R10G10B10_UNORM
93A8B8G8R8_UNORM
94A8B8G8R8_SRGB
95A8B8G8R8_SNORM
96A8B8G8R8_SINT
97A8B8G8R8_UINT
98R16G16_UNORM
99R16G16_SNORM
100R16G16_SINT
101R16G16_UINT
102R16G16_FLOAT
103B10G11R11_FLOAT
104R32_SINT
105R32_UINT
106R32_FLOAT
107X8R8G8B8_UNORM
108X8R8G8B8_SRGB
109R5G6B5_UNORM
110A1R5G5B5_UNORM
111R8G8_UNORM
112R8G8_SNORM
113R8G8_SINT
114R8G8_UINT
115R16_UNORM
116R16_SNORM
117R16_SINT
118R16_UINT
119R16_FLOAT
120R8_UNORM
121R8_SNORM
122R8_SINT
123R8_UINT
124X1R5G5B5_UNORM
125X8B8G8R8_UNORM
126X8B8G8R8_SRGB
127"""
128
129x = txt.split()
130y = list(map(lambda a: Format(a), x))
131formats = list(y)
132for format in formats:
133 format.print_declaration()
134
135for format in formats:
136 format.print_case()
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index d0709dc69..8a871593a 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -27,12 +27,12 @@ struct CommandList;
27// TODO: Implement the commented ones 27// TODO: Implement the commented ones
28enum class RenderTargetFormat : u32 { 28enum class RenderTargetFormat : u32 {
29 NONE = 0x0, 29 NONE = 0x0,
30 R32B32G32A32_FLOAT = 0xC0, 30 R32G32B32A32_FLOAT = 0xC0,
31 R32G32B32A32_SINT = 0xC1, 31 R32G32B32A32_SINT = 0xC1,
32 R32G32B32A32_UINT = 0xC2, 32 R32G32B32A32_UINT = 0xC2,
33 // R32G32B32X32_FLOAT = 0xC3, 33 R32G32B32X32_FLOAT = 0xC3,
34 // R32G32B32X32_SINT = 0xC4, 34 R32G32B32X32_SINT = 0xC4,
35 // R32G32B32X32_UINT = 0xC5, 35 R32G32B32X32_UINT = 0xC5,
36 R16G16B16A16_UNORM = 0xC6, 36 R16G16B16A16_UNORM = 0xC6,
37 R16G16B16A16_SNORM = 0xC7, 37 R16G16B16A16_SNORM = 0xC7,
38 R16G16B16A16_SINT = 0xC8, 38 R16G16B16A16_SINT = 0xC8,
@@ -56,13 +56,13 @@ enum class RenderTargetFormat : u32 {
56 R16G16_SINT = 0xDC, 56 R16G16_SINT = 0xDC,
57 R16G16_UINT = 0xDD, 57 R16G16_UINT = 0xDD,
58 R16G16_FLOAT = 0xDE, 58 R16G16_FLOAT = 0xDE,
59 // A2R10G10B10_UNORM = 0xDF, 59 A2R10G10B10_UNORM = 0xDF,
60 B10G11R11_FLOAT = 0xE0, 60 B10G11R11_FLOAT = 0xE0,
61 R32_SINT = 0xE3, 61 R32_SINT = 0xE3,
62 R32_UINT = 0xE4, 62 R32_UINT = 0xE4,
63 R32_FLOAT = 0xE5, 63 R32_FLOAT = 0xE5,
64 // X8R8G8B8_UNORM = 0xE6, 64 X8R8G8B8_UNORM = 0xE6,
65 // X8R8G8B8_SRGB = 0xE7, 65 X8R8G8B8_SRGB = 0xE7,
66 R5G6B5_UNORM = 0xE8, 66 R5G6B5_UNORM = 0xE8,
67 A1R5G5B5_UNORM = 0xE9, 67 A1R5G5B5_UNORM = 0xE9,
68 R8G8_UNORM = 0xEA, 68 R8G8_UNORM = 0xEA,
@@ -79,11 +79,11 @@ enum class RenderTargetFormat : u32 {
79 R8_SINT = 0xF5, 79 R8_SINT = 0xF5,
80 R8_UINT = 0xF6, 80 R8_UINT = 0xF6,
81 81
82 /* 82 // A8_UNORM = 0xF7,
83 A8_UNORM = 0xF7,
84 X1R5G5B5_UNORM = 0xF8, 83 X1R5G5B5_UNORM = 0xF8,
85 X8B8G8R8_UNORM = 0xF9, 84 X8B8G8R8_UNORM = 0xF9,
86 X8B8G8R8_SRGB = 0xFA, 85 X8B8G8R8_SRGB = 0xFA,
86 /*
87 Z1R5G5B5_UNORM = 0xFB, 87 Z1R5G5B5_UNORM = 0xFB,
88 O1R5G5B5_UNORM = 0xFC, 88 O1R5G5B5_UNORM = 0xFC,
89 Z8R8G8B8_UNORM = 0xFD, 89 Z8R8G8B8_UNORM = 0xFD,
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_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 354c6e429..f71a316b6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -466,8 +466,7 @@ bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf
466 const Tegra::Engines::Fermi2D::Config& copy_config) { 466 const Tegra::Engines::Fermi2D::Config& copy_config) {
467 MICROPROFILE_SCOPE(OpenGL_Blits); 467 MICROPROFILE_SCOPE(OpenGL_Blits);
468 std::scoped_lock lock{texture_cache.mutex}; 468 std::scoped_lock lock{texture_cache.mutex};
469 texture_cache.BlitImage(dst, src, copy_config); 469 return texture_cache.BlitImage(dst, src, copy_config);
470 return true;
471} 470}
472 471
473Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() { 472Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() {
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_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index e14f9b2db..ef1190e1f 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -28,6 +28,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
28 {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1R5G5B5_UNORM 28 {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1R5G5B5_UNORM
29 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM 29 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM
30 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT 30 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT
31 {GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2R10G10B10_UNORM
31 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM 32 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM
32 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A5B5G5R1_UNORM 33 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A5B5G5R1_UNORM
33 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM 34 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 5c156087b..430a84272 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -125,6 +125,7 @@ struct FormatTuple {
125 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM 125 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM
126 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM 126 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM
127 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT 127 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT
128 {VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable | Storage}, // A2R10G10B10_UNORM
128 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle) 129 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle)
129 {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled) 130 {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled)
130 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM 131 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM
@@ -149,7 +150,7 @@ struct FormatTuple {
149 {VK_FORMAT_BC6H_UFLOAT_BLOCK}, // BC6H_UFLOAT 150 {VK_FORMAT_BC6H_UFLOAT_BLOCK}, // BC6H_UFLOAT
150 {VK_FORMAT_BC6H_SFLOAT_BLOCK}, // BC6H_SFLOAT 151 {VK_FORMAT_BC6H_SFLOAT_BLOCK}, // BC6H_SFLOAT
151 {VK_FORMAT_ASTC_4x4_UNORM_BLOCK}, // ASTC_2D_4X4_UNORM 152 {VK_FORMAT_ASTC_4x4_UNORM_BLOCK}, // ASTC_2D_4X4_UNORM
152 {VK_FORMAT_B8G8R8A8_UNORM, Attachable}, // B8G8R8A8_UNORM 153 {VK_FORMAT_B8G8R8A8_UNORM, Attachable | Storage}, // B8G8R8A8_UNORM
153 {VK_FORMAT_R32G32B32A32_SFLOAT, Attachable | Storage}, // R32G32B32A32_FLOAT 154 {VK_FORMAT_R32G32B32A32_SFLOAT, Attachable | Storage}, // R32G32B32A32_FLOAT
154 {VK_FORMAT_R32G32B32A32_SINT, Attachable | Storage}, // R32G32B32A32_SINT 155 {VK_FORMAT_R32G32B32A32_SINT, Attachable | Storage}, // R32G32B32A32_SINT
155 {VK_FORMAT_R32G32_SFLOAT, Attachable | Storage}, // R32G32_FLOAT 156 {VK_FORMAT_R32G32_SFLOAT, Attachable | Storage}, // R32G32_FLOAT
@@ -159,7 +160,7 @@ struct FormatTuple {
159 {VK_FORMAT_R16_UNORM, Attachable | Storage}, // R16_UNORM 160 {VK_FORMAT_R16_UNORM, Attachable | Storage}, // R16_UNORM
160 {VK_FORMAT_R16_SNORM, Attachable | Storage}, // R16_SNORM 161 {VK_FORMAT_R16_SNORM, Attachable | Storage}, // R16_SNORM
161 {VK_FORMAT_R16_UINT, Attachable | Storage}, // R16_UINT 162 {VK_FORMAT_R16_UINT, Attachable | Storage}, // R16_UINT
162 {VK_FORMAT_UNDEFINED}, // R16_SINT 163 {VK_FORMAT_R16_SINT, Attachable | Storage}, // R16_SINT
163 {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // R16G16_UNORM 164 {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // R16G16_UNORM
164 {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // R16G16_FLOAT 165 {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // R16G16_FLOAT
165 {VK_FORMAT_R16G16_UINT, Attachable | Storage}, // R16G16_UINT 166 {VK_FORMAT_R16G16_UINT, Attachable | Storage}, // R16G16_UINT
@@ -183,7 +184,7 @@ struct FormatTuple {
183 {VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB 184 {VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB
184 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB 185 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
185 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB 186 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB
186 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM 187 {VK_FORMAT_R4G4B4A4_UNORM_PACK16}, // A4B4G4R4_UNORM
187 {VK_FORMAT_R4G4_UNORM_PACK8}, // G4R4_UNORM 188 {VK_FORMAT_R4G4_UNORM_PACK8}, // G4R4_UNORM
188 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB 189 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB
189 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB 190 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB
diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp
index dd450169e..33daa8c1c 100644
--- a/src/video_core/renderer_vulkan/vk_fsr.cpp
+++ b/src/video_core/renderer_vulkan/vk_fsr.cpp
@@ -5,6 +5,7 @@
5#include "common/bit_cast.h" 5#include "common/bit_cast.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/div_ceil.h" 7#include "common/div_ceil.h"
8#include "common/settings.h"
8 9
9#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h" 10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h"
10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h" 11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h"
@@ -227,7 +228,10 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView imag
227 228
228 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *rcas_pipeline); 229 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *rcas_pipeline);
229 230
230 FsrRcasCon(push_constants.data(), 0.25f); 231 const float sharpening =
232 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
233
234 FsrRcasCon(push_constants.data(), sharpening);
231 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); 235 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants);
232 236
233 { 237 {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 5995aeff0..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;
@@ -60,6 +61,17 @@ auto 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:
@@ -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/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 12b13cc59..d8ad8815c 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -542,8 +542,7 @@ bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf
542 const Tegra::Engines::Fermi2D::Surface& dst, 542 const Tegra::Engines::Fermi2D::Surface& dst,
543 const Tegra::Engines::Fermi2D::Config& copy_config) { 543 const Tegra::Engines::Fermi2D::Config& copy_config) {
544 std::scoped_lock lock{texture_cache.mutex}; 544 std::scoped_lock lock{texture_cache.mutex};
545 texture_cache.BlitImage(dst, src, copy_config); 545 return texture_cache.BlitImage(dst, src, copy_config);
546 return true;
547} 546}
548 547
549Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() { 548Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() {
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 6bd133d10..1a76d4178 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -93,11 +93,14 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
93 93
94PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { 94PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
95 switch (format) { 95 switch (format) {
96 case Tegra::RenderTargetFormat::R32B32G32A32_FLOAT: 96 case Tegra::RenderTargetFormat::R32G32B32A32_FLOAT:
97 case Tegra::RenderTargetFormat::R32G32B32X32_FLOAT:
97 return PixelFormat::R32G32B32A32_FLOAT; 98 return PixelFormat::R32G32B32A32_FLOAT;
98 case Tegra::RenderTargetFormat::R32G32B32A32_SINT: 99 case Tegra::RenderTargetFormat::R32G32B32A32_SINT:
100 case Tegra::RenderTargetFormat::R32G32B32X32_SINT:
99 return PixelFormat::R32G32B32A32_SINT; 101 return PixelFormat::R32G32B32A32_SINT;
100 case Tegra::RenderTargetFormat::R32G32B32A32_UINT: 102 case Tegra::RenderTargetFormat::R32G32B32A32_UINT:
103 case Tegra::RenderTargetFormat::R32G32B32X32_UINT:
101 return PixelFormat::R32G32B32A32_UINT; 104 return PixelFormat::R32G32B32A32_UINT;
102 case Tegra::RenderTargetFormat::R16G16B16A16_UNORM: 105 case Tegra::RenderTargetFormat::R16G16B16A16_UNORM:
103 return PixelFormat::R16G16B16A16_UNORM; 106 return PixelFormat::R16G16B16A16_UNORM;
@@ -118,16 +121,22 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
118 case Tegra::RenderTargetFormat::R16G16B16X16_FLOAT: 121 case Tegra::RenderTargetFormat::R16G16B16X16_FLOAT:
119 return PixelFormat::R16G16B16X16_FLOAT; 122 return PixelFormat::R16G16B16X16_FLOAT;
120 case Tegra::RenderTargetFormat::A8R8G8B8_UNORM: 123 case Tegra::RenderTargetFormat::A8R8G8B8_UNORM:
124 case Tegra::RenderTargetFormat::X8R8G8B8_UNORM:
121 return PixelFormat::B8G8R8A8_UNORM; 125 return PixelFormat::B8G8R8A8_UNORM;
122 case Tegra::RenderTargetFormat::A8R8G8B8_SRGB: 126 case Tegra::RenderTargetFormat::A8R8G8B8_SRGB:
127 case Tegra::RenderTargetFormat::X8R8G8B8_SRGB:
123 return PixelFormat::B8G8R8A8_SRGB; 128 return PixelFormat::B8G8R8A8_SRGB;
124 case Tegra::RenderTargetFormat::A2B10G10R10_UNORM: 129 case Tegra::RenderTargetFormat::A2B10G10R10_UNORM:
125 return PixelFormat::A2B10G10R10_UNORM; 130 return PixelFormat::A2B10G10R10_UNORM;
126 case Tegra::RenderTargetFormat::A2B10G10R10_UINT: 131 case Tegra::RenderTargetFormat::A2B10G10R10_UINT:
127 return PixelFormat::A2B10G10R10_UINT; 132 return PixelFormat::A2B10G10R10_UINT;
133 case Tegra::RenderTargetFormat::A2R10G10B10_UNORM:
134 return PixelFormat::A2R10G10B10_UNORM;
128 case Tegra::RenderTargetFormat::A8B8G8R8_UNORM: 135 case Tegra::RenderTargetFormat::A8B8G8R8_UNORM:
136 case Tegra::RenderTargetFormat::X8B8G8R8_UNORM:
129 return PixelFormat::A8B8G8R8_UNORM; 137 return PixelFormat::A8B8G8R8_UNORM;
130 case Tegra::RenderTargetFormat::A8B8G8R8_SRGB: 138 case Tegra::RenderTargetFormat::A8B8G8R8_SRGB:
139 case Tegra::RenderTargetFormat::X8B8G8R8_SRGB:
131 return PixelFormat::A8B8G8R8_SRGB; 140 return PixelFormat::A8B8G8R8_SRGB;
132 case Tegra::RenderTargetFormat::A8B8G8R8_SNORM: 141 case Tegra::RenderTargetFormat::A8B8G8R8_SNORM:
133 return PixelFormat::A8B8G8R8_SNORM; 142 return PixelFormat::A8B8G8R8_SNORM;
@@ -156,6 +165,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
156 case Tegra::RenderTargetFormat::R5G6B5_UNORM: 165 case Tegra::RenderTargetFormat::R5G6B5_UNORM:
157 return PixelFormat::R5G6B5_UNORM; 166 return PixelFormat::R5G6B5_UNORM;
158 case Tegra::RenderTargetFormat::A1R5G5B5_UNORM: 167 case Tegra::RenderTargetFormat::A1R5G5B5_UNORM:
168 case Tegra::RenderTargetFormat::X1R5G5B5_UNORM:
159 return PixelFormat::A1R5G5B5_UNORM; 169 return PixelFormat::A1R5G5B5_UNORM;
160 case Tegra::RenderTargetFormat::R8G8_UNORM: 170 case Tegra::RenderTargetFormat::R8G8_UNORM:
161 return PixelFormat::R8G8_UNORM; 171 return PixelFormat::R8G8_UNORM;
@@ -204,23 +214,16 @@ PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format)
204} 214}
205 215
206SurfaceType GetFormatType(PixelFormat pixel_format) { 216SurfaceType GetFormatType(PixelFormat pixel_format) {
207 if (static_cast<std::size_t>(pixel_format) < 217 if (pixel_format < PixelFormat::MaxColorFormat) {
208 static_cast<std::size_t>(PixelFormat::MaxColorFormat)) {
209 return SurfaceType::ColorTexture; 218 return SurfaceType::ColorTexture;
210 } 219 }
211 220 if (pixel_format < PixelFormat::MaxDepthFormat) {
212 if (static_cast<std::size_t>(pixel_format) <
213 static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) {
214 return SurfaceType::Depth; 221 return SurfaceType::Depth;
215 } 222 }
216 223 if (pixel_format < PixelFormat::MaxStencilFormat) {
217 if (static_cast<std::size_t>(pixel_format) <
218 static_cast<std::size_t>(PixelFormat::MaxStencilFormat)) {
219 return SurfaceType::Stencil; 224 return SurfaceType::Stencil;
220 } 225 }
221 226 if (pixel_format < PixelFormat::MaxDepthStencilFormat) {
222 if (static_cast<std::size_t>(pixel_format) <
223 static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
224 return SurfaceType::DepthStencil; 227 return SurfaceType::DepthStencil;
225 } 228 }
226 229
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 57ca7f597..44b79af20 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -23,6 +23,7 @@ enum class PixelFormat {
23 A1R5G5B5_UNORM, 23 A1R5G5B5_UNORM,
24 A2B10G10R10_UNORM, 24 A2B10G10R10_UNORM,
25 A2B10G10R10_UINT, 25 A2B10G10R10_UINT,
26 A2R10G10B10_UNORM,
26 A1B5G5R5_UNORM, 27 A1B5G5R5_UNORM,
27 A5B5G5R1_UNORM, 28 A5B5G5R1_UNORM,
28 R8_UNORM, 29 R8_UNORM,
@@ -159,6 +160,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
159 1, // A1R5G5B5_UNORM 160 1, // A1R5G5B5_UNORM
160 1, // A2B10G10R10_UNORM 161 1, // A2B10G10R10_UNORM
161 1, // A2B10G10R10_UINT 162 1, // A2B10G10R10_UINT
163 1, // A2R10G10B10_UNORM
162 1, // A1B5G5R5_UNORM 164 1, // A1B5G5R5_UNORM
163 1, // A5B5G5R1_UNORM 165 1, // A5B5G5R1_UNORM
164 1, // R8_UNORM 166 1, // R8_UNORM
@@ -264,6 +266,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
264 1, // A1R5G5B5_UNORM 266 1, // A1R5G5B5_UNORM
265 1, // A2B10G10R10_UNORM 267 1, // A2B10G10R10_UNORM
266 1, // A2B10G10R10_UINT 268 1, // A2B10G10R10_UINT
269 1, // A2R10G10B10_UNORM
267 1, // A1B5G5R5_UNORM 270 1, // A1B5G5R5_UNORM
268 1, // A5B5G5R1_UNORM 271 1, // A5B5G5R1_UNORM
269 1, // R8_UNORM 272 1, // R8_UNORM
@@ -369,6 +372,7 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
369 16, // A1R5G5B5_UNORM 372 16, // A1R5G5B5_UNORM
370 32, // A2B10G10R10_UNORM 373 32, // A2B10G10R10_UNORM
371 32, // A2B10G10R10_UINT 374 32, // A2B10G10R10_UINT
375 32, // A2R10G10B10_UNORM
372 16, // A1B5G5R5_UNORM 376 16, // A1B5G5R5_UNORM
373 16, // A5B5G5R1_UNORM 377 16, // A5B5G5R1_UNORM
374 8, // R8_UNORM 378 8, // R8_UNORM
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index acc854715..f1f0a057b 100644
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -35,6 +35,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
35 return "A2B10G10R10_UNORM"; 35 return "A2B10G10R10_UNORM";
36 case PixelFormat::A2B10G10R10_UINT: 36 case PixelFormat::A2B10G10R10_UINT:
37 return "A2B10G10R10_UINT"; 37 return "A2B10G10R10_UINT";
38 case PixelFormat::A2R10G10B10_UNORM:
39 return "A2R10G10B10_UNORM";
38 case PixelFormat::A1B5G5R5_UNORM: 40 case PixelFormat::A1B5G5R5_UNORM:
39 return "A1B5G5R5_UNORM"; 41 return "A1B5G5R5_UNORM";
40 case PixelFormat::A5B5G5R1_UNORM: 42 case PixelFormat::A5B5G5R1_UNORM:
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 8ef75fe73..8e68a2e53 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -506,10 +506,14 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
506} 506}
507 507
508template <class P> 508template <class P>
509void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, 509bool TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
510 const Tegra::Engines::Fermi2D::Surface& src, 510 const Tegra::Engines::Fermi2D::Surface& src,
511 const Tegra::Engines::Fermi2D::Config& copy) { 511 const Tegra::Engines::Fermi2D::Config& copy) {
512 const BlitImages images = GetBlitImages(dst, src, copy); 512 const auto result = GetBlitImages(dst, src, copy);
513 if (!result) {
514 return false;
515 }
516 const BlitImages images = *result;
513 const ImageId dst_id = images.dst_id; 517 const ImageId dst_id = images.dst_id;
514 const ImageId src_id = images.src_id; 518 const ImageId src_id = images.src_id;
515 519
@@ -596,6 +600,7 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
596 runtime.BlitImage(dst_framebuffer, dst_view, src_view, dst_region, src_region, copy.filter, 600 runtime.BlitImage(dst_framebuffer, dst_view, src_view, dst_region, src_region, copy.filter,
597 copy.operation); 601 copy.operation);
598 } 602 }
603 return true;
599} 604}
600 605
601template <class P> 606template <class P>
@@ -1133,7 +1138,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1133} 1138}
1134 1139
1135template <class P> 1140template <class P>
1136typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages( 1141std::optional<typename TextureCache<P>::BlitImages> TextureCache<P>::GetBlitImages(
1137 const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src, 1142 const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src,
1138 const Tegra::Engines::Fermi2D::Config& copy) { 1143 const Tegra::Engines::Fermi2D::Config& copy) {
1139 1144
@@ -1154,6 +1159,20 @@ typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
1154 has_deleted_images = false; 1159 has_deleted_images = false;
1155 src_id = FindImage(src_info, src_addr, try_options); 1160 src_id = FindImage(src_info, src_addr, try_options);
1156 dst_id = FindImage(dst_info, dst_addr, try_options); 1161 dst_id = FindImage(dst_info, dst_addr, try_options);
1162 if (!copy.must_accelerate) {
1163 do {
1164 if (!src_id && !dst_id) {
1165 return std::nullopt;
1166 }
1167 if (src_id && True(slot_images[src_id].flags & ImageFlagBits::GpuModified)) {
1168 break;
1169 }
1170 if (dst_id && True(slot_images[dst_id].flags & ImageFlagBits::GpuModified)) {
1171 break;
1172 }
1173 return std::nullopt;
1174 } while (false);
1175 }
1157 const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr; 1176 const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr;
1158 if (src_image && src_image->info.num_samples > 1) { 1177 if (src_image && src_image->info.num_samples > 1) {
1159 RelaxedOptions find_options{FIND_OPTIONS | RelaxedOptions::ForceBrokenViews}; 1178 RelaxedOptions find_options{FIND_OPTIONS | RelaxedOptions::ForceBrokenViews};
@@ -1194,12 +1213,12 @@ typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
1194 dst_id = FindOrInsertImage(dst_info, dst_addr, RelaxedOptions{}); 1213 dst_id = FindOrInsertImage(dst_info, dst_addr, RelaxedOptions{});
1195 } while (has_deleted_images); 1214 } while (has_deleted_images);
1196 } 1215 }
1197 return BlitImages{ 1216 return {BlitImages{
1198 .dst_id = dst_id, 1217 .dst_id = dst_id,
1199 .src_id = src_id, 1218 .src_id = src_id,
1200 .dst_format = dst_info.format, 1219 .dst_format = dst_info.format,
1201 .src_format = src_info.format, 1220 .src_format = src_info.format,
1202 }; 1221 }};
1203} 1222}
1204 1223
1205template <class P> 1224template <class P>
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 2fa8445eb..9db7195bf 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -174,7 +174,7 @@ public:
174 void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size); 174 void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size);
175 175
176 /// Blit an image with the given parameters 176 /// Blit an image with the given parameters
177 void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, 177 bool BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
178 const Tegra::Engines::Fermi2D::Surface& src, 178 const Tegra::Engines::Fermi2D::Surface& src,
179 const Tegra::Engines::Fermi2D::Config& copy); 179 const Tegra::Engines::Fermi2D::Config& copy);
180 180
@@ -285,9 +285,9 @@ private:
285 [[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr); 285 [[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
286 286
287 /// Return a blit image pair from the given guest blit parameters 287 /// Return a blit image pair from the given guest blit parameters
288 [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst, 288 [[nodiscard]] std::optional<BlitImages> GetBlitImages(
289 const Tegra::Engines::Fermi2D::Surface& src, 289 const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src,
290 const Tegra::Engines::Fermi2D::Config& copy); 290 const Tegra::Engines::Fermi2D::Config& copy);
291 291
292 /// Find or create a sampler from a guest descriptor sampler 292 /// Find or create a sampler from a guest descriptor sampler
293 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); 293 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index adad36221..060de0259 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -295,7 +295,7 @@ if (APPLE)
295 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) 295 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
296elseif(WIN32) 296elseif(WIN32)
297 # compile as a win32 gui application instead of a console application 297 # compile as a win32 gui application instead of a console application
298 if (QT_VERSION VERSION_GREATER 6) 298 if (QT_VERSION VERSION_GREATER_EQUAL 6)
299 target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate) 299 target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate)
300 else() 300 else()
301 target_link_libraries(yuzu PRIVATE Qt5::WinMain) 301 target_link_libraries(yuzu PRIVATE Qt5::WinMain)
@@ -311,15 +311,15 @@ endif()
311create_target_directory_groups(yuzu) 311create_target_directory_groups(yuzu)
312 312
313target_link_libraries(yuzu PRIVATE common core input_common network video_core) 313target_link_libraries(yuzu PRIVATE common core input_common network video_core)
314target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets Qt::Multimedia) 314target_link_libraries(yuzu PRIVATE Boost::boost glad Qt${QT_MAJOR_VERSION}::Widgets)
315target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 315target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
316 316
317target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) 317target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
318if (NOT WIN32) 318if (NOT WIN32)
319 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 319 target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
320endif() 320endif()
321if (UNIX AND NOT APPLE) 321if (UNIX AND NOT APPLE)
322 target_link_libraries(yuzu PRIVATE Qt::DBus) 322 target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus)
323endif() 323endif()
324 324
325target_compile_definitions(yuzu PRIVATE 325target_compile_definitions(yuzu PRIVATE
@@ -358,8 +358,13 @@ if (ENABLE_WEB_SERVICE)
358 target_compile_definitions(yuzu PRIVATE -DENABLE_WEB_SERVICE) 358 target_compile_definitions(yuzu PRIVATE -DENABLE_WEB_SERVICE)
359endif() 359endif()
360 360
361if (YUZU_USE_QT_MULTIMEDIA)
362 target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::Multimedia)
363 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_MULTIMEDIA)
364endif ()
365
361if (YUZU_USE_QT_WEB_ENGINE) 366if (YUZU_USE_QT_WEB_ENGINE)
362 target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets) 367 target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::WebEngineCore Qt${QT_MAJOR_VERSION}::WebEngineWidgets)
363 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) 368 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
364endif () 369endif ()
365 370
@@ -367,13 +372,26 @@ if(UNIX AND NOT APPLE)
367 install(TARGETS yuzu) 372 install(TARGETS yuzu)
368endif() 373endif()
369 374
370if (YUZU_USE_BUNDLED_QT) 375if (WIN32 AND QT_VERSION VERSION_GREATER_EQUAL 6)
376 if (MSVC AND NOT ${CMAKE_GENERATOR} STREQUAL "Ninja")
377 set(YUZU_EXE_DIR "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
378 else()
379 set(YUZU_EXE_DIR "${CMAKE_BINARY_DIR}/bin")
380 endif()
381 add_custom_command(TARGET yuzu POST_BUILD COMMAND ${WINDEPLOYQT_EXECUTABLE} "${YUZU_EXE_DIR}/yuzu.exe" --dir "${YUZU_EXE_DIR}" --libdir "${YUZU_EXE_DIR}" --plugindir "${YUZU_EXE_DIR}/plugins" --no-compiler-runtime --no-opengl-sw --no-system-d3d-compiler --no-translations --verbose 0)
382endif()
383
384if (YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6)
371 include(CopyYuzuQt5Deps) 385 include(CopyYuzuQt5Deps)
372 copy_yuzu_Qt5_deps(yuzu) 386 copy_yuzu_Qt5_deps(yuzu)
373endif() 387endif()
374 388
375if (ENABLE_SDL2) 389if (ENABLE_SDL2)
376 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()
377 target_compile_definitions(yuzu PRIVATE HAVE_SDL2) 395 target_compile_definitions(yuzu PRIVATE HAVE_SDL2)
378endif() 396endif()
379 397
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index bbb3ee553..ccf1c50f4 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -4,8 +4,10 @@
4#include <glad/glad.h> 4#include <glad/glad.h>
5 5
6#include <QApplication> 6#include <QApplication>
7#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
7#include <QCameraImageCapture> 8#include <QCameraImageCapture>
8#include <QCameraInfo> 9#include <QCameraInfo>
10#endif
9#include <QHBoxLayout> 11#include <QHBoxLayout>
10#include <QMessageBox> 12#include <QMessageBox>
11#include <QPainter> 13#include <QPainter>
@@ -711,6 +713,7 @@ void GRenderWindow::TouchEndEvent() {
711} 713}
712 714
713void GRenderWindow::InitializeCamera() { 715void GRenderWindow::InitializeCamera() {
716#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
714 constexpr auto camera_update_ms = std::chrono::milliseconds{50}; // (50ms, 20Hz) 717 constexpr auto camera_update_ms = std::chrono::milliseconds{50}; // (50ms, 20Hz)
715 if (!Settings::values.enable_ir_sensor) { 718 if (!Settings::values.enable_ir_sensor) {
716 return; 719 return;
@@ -764,18 +767,22 @@ void GRenderWindow::InitializeCamera() {
764 connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); }); 767 connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); });
765 // This timer should be dependent of camera resolution 5ms for every 100 pixels 768 // This timer should be dependent of camera resolution 5ms for every 100 pixels
766 camera_timer->start(camera_update_ms); 769 camera_timer->start(camera_update_ms);
770#endif
767} 771}
768 772
769void GRenderWindow::FinalizeCamera() { 773void GRenderWindow::FinalizeCamera() {
774#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
770 if (camera_timer) { 775 if (camera_timer) {
771 camera_timer->stop(); 776 camera_timer->stop();
772 } 777 }
773 if (camera) { 778 if (camera) {
774 camera->unload(); 779 camera->unload();
775 } 780 }
781#endif
776} 782}
777 783
778void GRenderWindow::RequestCameraCapture() { 784void GRenderWindow::RequestCameraCapture() {
785#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
779 if (!Settings::values.enable_ir_sensor) { 786 if (!Settings::values.enable_ir_sensor) {
780 return; 787 return;
781 } 788 }
@@ -792,6 +799,7 @@ void GRenderWindow::RequestCameraCapture() {
792 799
793 pending_camera_snapshots++; 800 pending_camera_snapshots++;
794 camera_capture->capture(); 801 camera_capture->capture();
802#endif
795} 803}
796 804
797void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { 805void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index c45ebf1a2..4a01481cd 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -241,8 +241,10 @@ private:
241 241
242 bool is_virtual_camera; 242 bool is_virtual_camera;
243 int pending_camera_snapshots; 243 int pending_camera_snapshots;
244#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
244 std::unique_ptr<QCamera> camera; 245 std::unique_ptr<QCamera> camera;
245 std::unique_ptr<QCameraImageCapture> camera_capture; 246 std::unique_ptr<QCameraImageCapture> camera_capture;
247#endif
246 std::unique_ptr<QTimer> camera_timer; 248 std::unique_ptr<QTimer> camera_timer;
247 249
248 Core::System& system; 250 Core::System& system;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 343f3b8e5..0c93df428 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -672,6 +672,7 @@ void Config::ReadRendererValues() {
672 ReadGlobalSetting(Settings::values.aspect_ratio); 672 ReadGlobalSetting(Settings::values.aspect_ratio);
673 ReadGlobalSetting(Settings::values.resolution_setup); 673 ReadGlobalSetting(Settings::values.resolution_setup);
674 ReadGlobalSetting(Settings::values.scaling_filter); 674 ReadGlobalSetting(Settings::values.scaling_filter);
675 ReadGlobalSetting(Settings::values.fsr_sharpening_slider);
675 ReadGlobalSetting(Settings::values.anti_aliasing); 676 ReadGlobalSetting(Settings::values.anti_aliasing);
676 ReadGlobalSetting(Settings::values.max_anisotropy); 677 ReadGlobalSetting(Settings::values.max_anisotropy);
677 ReadGlobalSetting(Settings::values.use_speed_limit); 678 ReadGlobalSetting(Settings::values.use_speed_limit);
@@ -1282,6 +1283,10 @@ void Config::SaveRendererValues() {
1282 static_cast<u32>(Settings::values.scaling_filter.GetValue(global)), 1283 static_cast<u32>(Settings::values.scaling_filter.GetValue(global)),
1283 static_cast<u32>(Settings::values.scaling_filter.GetDefault()), 1284 static_cast<u32>(Settings::values.scaling_filter.GetDefault()),
1284 Settings::values.scaling_filter.UsingGlobal()); 1285 Settings::values.scaling_filter.UsingGlobal());
1286 WriteSetting(QString::fromStdString(Settings::values.fsr_sharpening_slider.GetLabel()),
1287 static_cast<u32>(Settings::values.fsr_sharpening_slider.GetValue(global)),
1288 static_cast<u32>(Settings::values.fsr_sharpening_slider.GetDefault()),
1289 Settings::values.fsr_sharpening_slider.UsingGlobal());
1285 WriteSetting(QString::fromStdString(Settings::values.anti_aliasing.GetLabel()), 1290 WriteSetting(QString::fromStdString(Settings::values.anti_aliasing.GetLabel()),
1286 static_cast<u32>(Settings::values.anti_aliasing.GetValue(global)), 1291 static_cast<u32>(Settings::values.anti_aliasing.GetValue(global)),
1287 static_cast<u32>(Settings::values.anti_aliasing.GetDefault()), 1292 static_cast<u32>(Settings::values.anti_aliasing.GetDefault()),
diff --git a/src/yuzu/configuration/configure_camera.cpp b/src/yuzu/configuration/configure_camera.cpp
index 2a61de2a1..d95e96696 100644
--- a/src/yuzu/configuration/configure_camera.cpp
+++ b/src/yuzu/configuration/configure_camera.cpp
@@ -2,8 +2,11 @@
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <memory> 4#include <memory>
5#include <QtCore>
6#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
5#include <QCameraImageCapture> 7#include <QCameraImageCapture>
6#include <QCameraInfo> 8#include <QCameraInfo>
9#endif
7#include <QStandardItemModel> 10#include <QStandardItemModel>
8#include <QTimer> 11#include <QTimer>
9 12
@@ -33,6 +36,7 @@ ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* i
33ConfigureCamera::~ConfigureCamera() = default; 36ConfigureCamera::~ConfigureCamera() = default;
34 37
35void ConfigureCamera::PreviewCamera() { 38void ConfigureCamera::PreviewCamera() {
39#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
36 const auto index = ui->ir_sensor_combo_box->currentIndex(); 40 const auto index = ui->ir_sensor_combo_box->currentIndex();
37 bool camera_found = false; 41 bool camera_found = false;
38 const QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); 42 const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
@@ -101,6 +105,7 @@ void ConfigureCamera::PreviewCamera() {
101 }); 105 });
102 106
103 camera_timer->start(250); 107 camera_timer->start(250);
108#endif
104} 109}
105 110
106void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) { 111void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) {
@@ -133,11 +138,13 @@ void ConfigureCamera::LoadConfiguration() {
133 ui->ir_sensor_combo_box->clear(); 138 ui->ir_sensor_combo_box->clear();
134 input_devices.push_back("Auto"); 139 input_devices.push_back("Auto");
135 ui->ir_sensor_combo_box->addItem(tr("Auto")); 140 ui->ir_sensor_combo_box->addItem(tr("Auto"));
141#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
136 const auto cameras = QCameraInfo::availableCameras(); 142 const auto cameras = QCameraInfo::availableCameras();
137 for (const QCameraInfo& cameraInfo : cameras) { 143 for (const QCameraInfo& cameraInfo : cameras) {
138 input_devices.push_back(cameraInfo.deviceName().toStdString()); 144 input_devices.push_back(cameraInfo.deviceName().toStdString());
139 ui->ir_sensor_combo_box->addItem(cameraInfo.description()); 145 ui->ir_sensor_combo_box->addItem(cameraInfo.description());
140 } 146 }
147#endif
141 148
142 const auto current_device = Settings::values.ir_sensor_device.GetValue(); 149 const auto current_device = Settings::values.ir_sensor_device.GetValue();
143 150
diff --git a/src/yuzu/configuration/configure_camera.h b/src/yuzu/configuration/configure_camera.h
index db9833b5c..9a90512b3 100644
--- a/src/yuzu/configuration/configure_camera.h
+++ b/src/yuzu/configuration/configure_camera.h
@@ -46,8 +46,10 @@ private:
46 46
47 bool is_virtual_camera; 47 bool is_virtual_camera;
48 int pending_snapshots; 48 int pending_snapshots;
49#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
49 std::unique_ptr<QCamera> camera; 50 std::unique_ptr<QCamera> camera;
50 std::unique_ptr<QCameraImageCapture> camera_capture; 51 std::unique_ptr<QCameraImageCapture> camera_capture;
52#endif
51 std::unique_ptr<QTimer> camera_timer; 53 std::unique_ptr<QTimer> camera_timer;
52 std::vector<std::string> input_devices; 54 std::vector<std::string> input_devices;
53 std::unique_ptr<Ui::ConfigureCamera> ui; 55 std::unique_ptr<Ui::ConfigureCamera> ui;
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 45cbce4ac..20bc651f1 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -63,6 +63,11 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
63 ui->api_widget->isEnabled()); 63 ui->api_widget->isEnabled());
64 ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); 64 ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
65 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); 65 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
66
67 connect(ui->fsr_sharpening_slider, &QSlider::valueChanged, this,
68 &ConfigureGraphics::SetFSRIndicatorText);
69 ui->fsr_sharpening_combobox->setVisible(!Settings::IsConfiguringGlobal());
70 ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal());
66} 71}
67 72
68void ConfigureGraphics::UpdateDeviceSelection(int device) { 73void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -110,6 +115,7 @@ void ConfigureGraphics::SetConfiguration() {
110 static_cast<int>(Settings::values.resolution_setup.GetValue())); 115 static_cast<int>(Settings::values.resolution_setup.GetValue()));
111 ui->scaling_filter_combobox->setCurrentIndex( 116 ui->scaling_filter_combobox->setCurrentIndex(
112 static_cast<int>(Settings::values.scaling_filter.GetValue())); 117 static_cast<int>(Settings::values.scaling_filter.GetValue()));
118 ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue());
113 ui->anti_aliasing_combobox->setCurrentIndex( 119 ui->anti_aliasing_combobox->setCurrentIndex(
114 static_cast<int>(Settings::values.anti_aliasing.GetValue())); 120 static_cast<int>(Settings::values.anti_aliasing.GetValue()));
115 } else { 121 } else {
@@ -147,6 +153,15 @@ void ConfigureGraphics::SetConfiguration() {
147 ConfigurationShared::SetHighlight(ui->anti_aliasing_label, 153 ConfigurationShared::SetHighlight(ui->anti_aliasing_label,
148 !Settings::values.anti_aliasing.UsingGlobal()); 154 !Settings::values.anti_aliasing.UsingGlobal());
149 155
156 ui->fsr_sharpening_combobox->setCurrentIndex(
157 Settings::values.fsr_sharpening_slider.UsingGlobal() ? 0 : 1);
158 ui->fsr_sharpening_slider->setEnabled(
159 !Settings::values.fsr_sharpening_slider.UsingGlobal());
160 ui->fsr_sharpening_value->setEnabled(!Settings::values.fsr_sharpening_slider.UsingGlobal());
161 ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout,
162 !Settings::values.fsr_sharpening_slider.UsingGlobal());
163 ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue());
164
150 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); 165 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
151 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); 166 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
152 ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal()); 167 ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
@@ -155,6 +170,12 @@ void ConfigureGraphics::SetConfiguration() {
155 Settings::values.bg_green.GetValue(), 170 Settings::values.bg_green.GetValue(),
156 Settings::values.bg_blue.GetValue())); 171 Settings::values.bg_blue.GetValue()));
157 UpdateAPILayout(); 172 UpdateAPILayout();
173 SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition());
174}
175
176void ConfigureGraphics::SetFSRIndicatorText(int percentage) {
177 ui->fsr_sharpening_value->setText(
178 tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2)));
158} 179}
159 180
160void ConfigureGraphics::ApplyConfiguration() { 181void ConfigureGraphics::ApplyConfiguration() {
@@ -210,6 +231,7 @@ void ConfigureGraphics::ApplyConfiguration() {
210 if (Settings::values.anti_aliasing.UsingGlobal()) { 231 if (Settings::values.anti_aliasing.UsingGlobal()) {
211 Settings::values.anti_aliasing.SetValue(anti_aliasing); 232 Settings::values.anti_aliasing.SetValue(anti_aliasing);
212 } 233 }
234 Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value());
213 } else { 235 } else {
214 if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { 236 if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
215 Settings::values.resolution_setup.SetGlobal(true); 237 Settings::values.resolution_setup.SetGlobal(true);
@@ -269,6 +291,13 @@ void ConfigureGraphics::ApplyConfiguration() {
269 Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green())); 291 Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
270 Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue())); 292 Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
271 } 293 }
294
295 if (ui->fsr_sharpening_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
296 Settings::values.fsr_sharpening_slider.SetGlobal(true);
297 } else {
298 Settings::values.fsr_sharpening_slider.SetGlobal(false);
299 Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value());
300 }
272 } 301 }
273} 302}
274 303
@@ -380,6 +409,7 @@ void ConfigureGraphics::SetupPerGameUI() {
380 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); 409 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
381 ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal()); 410 ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal());
382 ui->scaling_filter_combobox->setEnabled(Settings::values.scaling_filter.UsingGlobal()); 411 ui->scaling_filter_combobox->setEnabled(Settings::values.scaling_filter.UsingGlobal());
412 ui->fsr_sharpening_slider->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal());
383 ui->anti_aliasing_combobox->setEnabled(Settings::values.anti_aliasing.UsingGlobal()); 413 ui->anti_aliasing_combobox->setEnabled(Settings::values.anti_aliasing.UsingGlobal());
384 ui->use_asynchronous_gpu_emulation->setEnabled( 414 ui->use_asynchronous_gpu_emulation->setEnabled(
385 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); 415 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
@@ -387,6 +417,7 @@ void ConfigureGraphics::SetupPerGameUI() {
387 ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal()); 417 ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal());
388 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); 418 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
389 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); 419 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
420 ui->fsr_slider_layout->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal());
390 421
391 return; 422 return;
392 } 423 }
@@ -396,6 +427,13 @@ void ConfigureGraphics::SetupPerGameUI() {
396 ConfigurationShared::SetHighlight(ui->bg_layout, index == 1); 427 ConfigurationShared::SetHighlight(ui->bg_layout, index == 1);
397 }); 428 });
398 429
430 connect(ui->fsr_sharpening_combobox, qOverload<int>(&QComboBox::activated), this,
431 [this](int index) {
432 ui->fsr_sharpening_slider->setEnabled(index == 1);
433 ui->fsr_sharpening_value->setEnabled(index == 1);
434 ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout, index == 1);
435 });
436
399 ConfigurationShared::SetColoredTristate( 437 ConfigurationShared::SetColoredTristate(
400 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache); 438 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
401 ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc, 439 ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc,
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 70034eb1b..d98d6624e 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -42,6 +42,8 @@ private:
42 42
43 void RetrieveVulkanDevices(); 43 void RetrieveVulkanDevices();
44 44
45 void SetFSRIndicatorText(int percentage);
46
45 void SetupPerGameUI(); 47 void SetupPerGameUI();
46 48
47 Settings::RendererBackend GetCurrentGraphicsBackend() const; 49 Settings::RendererBackend GetCurrentGraphicsBackend() const;
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index fdbb33372..37271f956 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -152,6 +152,12 @@
152 </item> 152 </item>
153 <item> 153 <item>
154 <widget class="QGroupBox" name="groupBox"> 154 <widget class="QGroupBox" name="groupBox">
155 <property name="maximumSize">
156 <size>
157 <width>16777215</width>
158 <height>16777215</height>
159 </size>
160 </property>
155 <property name="title"> 161 <property name="title">
156 <string>Graphics Settings</string> 162 <string>Graphics Settings</string>
157 </property> 163 </property>
@@ -482,6 +488,146 @@
482 </widget> 488 </widget>
483 </item> 489 </item>
484 <item> 490 <item>
491 <widget class="QWidget" name="fsr_sharpening_layout" native="true">
492 <property name="enabled">
493 <bool>true</bool>
494 </property>
495 <property name="sizePolicy">
496 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
497 <horstretch>0</horstretch>
498 <verstretch>0</verstretch>
499 </sizepolicy>
500 </property>
501 <layout class="QHBoxLayout" name="horizontalLayout">
502 <property name="spacing">
503 <number>6</number>
504 </property>
505 <property name="sizeConstraint">
506 <enum>QLayout::SetDefaultConstraint</enum>
507 </property>
508 <property name="leftMargin">
509 <number>0</number>
510 </property>
511 <property name="topMargin">
512 <number>0</number>
513 </property>
514 <property name="rightMargin">
515 <number>0</number>
516 </property>
517 <property name="bottomMargin">
518 <number>0</number>
519 </property>
520 <item>
521 <layout class="QHBoxLayout" name="fsr_sharpening_label_group">
522 <item>
523 <widget class="QComboBox" name="fsr_sharpening_combobox">
524 <property name="sizePolicy">
525 <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
526 <horstretch>0</horstretch>
527 <verstretch>0</verstretch>
528 </sizepolicy>
529 </property>
530 <item>
531 <property name="text">
532 <string>Use global FSR Sharpness</string>
533 </property>
534 </item>
535 <item>
536 <property name="text">
537 <string>Set FSR Sharpness</string>
538 </property>
539 </item>
540 </widget>
541 </item>
542 <item>
543 <widget class="QLabel" name="fsr_sharpening_label">
544 <property name="sizePolicy">
545 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
546 <horstretch>0</horstretch>
547 <verstretch>0</verstretch>
548 </sizepolicy>
549 </property>
550 <property name="text">
551 <string>FSR Sharpness:</string>
552 </property>
553 </widget>
554 </item>
555 <item>
556 <spacer name="horizontalSpacer_2">
557 <property name="orientation">
558 <enum>Qt::Horizontal</enum>
559 </property>
560 <property name="sizeHint" stdset="0">
561 <size>
562 <width>40</width>
563 <height>20</height>
564 </size>
565 </property>
566 </spacer>
567 </item>
568 </layout>
569 </item>
570 <item>
571 <layout class="QHBoxLayout" name="fsr_slider_layout">
572 <property name="spacing">
573 <number>6</number>
574 </property>
575 <item>
576 <widget class="QSlider" name="fsr_sharpening_slider">
577 <property name="sizePolicy">
578 <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
579 <horstretch>0</horstretch>
580 <verstretch>0</verstretch>
581 </sizepolicy>
582 </property>
583 <property name="baseSize">
584 <size>
585 <width>0</width>
586 <height>0</height>
587 </size>
588 </property>
589 <property name="maximum">
590 <number>200</number>
591 </property>
592 <property name="sliderPosition">
593 <number>25</number>
594 </property>
595 <property name="orientation">
596 <enum>Qt::Horizontal</enum>
597 </property>
598 <property name="invertedAppearance">
599 <bool>true</bool>
600 </property>
601 </widget>
602 </item>
603 <item>
604 <widget class="QLabel" name="fsr_sharpening_value">
605 <property name="sizePolicy">
606 <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
607 <horstretch>0</horstretch>
608 <verstretch>0</verstretch>
609 </sizepolicy>
610 </property>
611 <property name="minimumSize">
612 <size>
613 <width>32</width>
614 <height>0</height>
615 </size>
616 </property>
617 <property name="text">
618 <string>100%</string>
619 </property>
620 <property name="alignment">
621 <set>Qt::AlignCenter</set>
622 </property>
623 </widget>
624 </item>
625 </layout>
626 </item>
627 </layout>
628 </widget>
629 </item>
630 <item>
485 <widget class="QWidget" name="bg_layout" native="true"> 631 <widget class="QWidget" name="bg_layout" native="true">
486 <property name="sizePolicy"> 632 <property name="sizePolicy">
487 <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> 633 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 10f841b98..235b813d9 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -194,4 +194,8 @@ void ConfigureInputAdvanced::UpdateUIEnabled() {
194 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked()); 194 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());
195 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked()); 195 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked());
196 ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked()); 196 ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked());
197#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || !defined(YUZU_USE_QT_MULTIMEDIA)
198 ui->enable_ir_sensor->setEnabled(false);
199 ui->camera_configure->setEnabled(false);
200#endif
197} 201}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 4081af391..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
@@ -237,6 +238,7 @@ static void LogRuntimes() {
237 LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name); 238 LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
238 } 239 }
239#endif 240#endif
241 LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion());
240} 242}
241 243
242static QString PrettyProductName() { 244static QString PrettyProductName() {
@@ -404,6 +406,10 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
404 mouse_center_timer.setInterval(default_mouse_center_timeout); 406 mouse_center_timer.setInterval(default_mouse_center_timeout);
405 connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor); 407 connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor);
406 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
407 MigrateConfigFiles(); 413 MigrateConfigFiles();
408 414
409 if (has_broken_vulkan) { 415 if (has_broken_vulkan) {
@@ -3636,6 +3642,13 @@ void GMainWindow::UpdateUISettings() {
3636 UISettings::values.first_start = false; 3642 UISettings::values.first_start = false;
3637} 3643}
3638 3644
3645void GMainWindow::UpdateInputDrivers() {
3646 if (!input_subsystem) {
3647 return;
3648 }
3649 input_subsystem->PumpEvents();
3650}
3651
3639void GMainWindow::HideMouseCursor() { 3652void GMainWindow::HideMouseCursor() {
3640 if (emu_thread == nullptr && UISettings::values.hide_mouse) { 3653 if (emu_thread == nullptr && UISettings::values.hide_mouse) {
3641 mouse_hide_timer.stop(); 3654 mouse_hide_timer.stop();
@@ -4036,7 +4049,6 @@ void GMainWindow::UpdateUITheme() {
4036 const QString default_theme = 4049 const QString default_theme =
4037 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);
4038 QString current_theme = UISettings::values.theme; 4051 QString current_theme = UISettings::values.theme;
4039 QStringList theme_paths(default_theme_paths);
4040 4052
4041 if (current_theme.isEmpty()) { 4053 if (current_theme.isEmpty()) {
4042 current_theme = default_theme; 4054 current_theme = default_theme;
@@ -4049,7 +4061,7 @@ void GMainWindow::UpdateUITheme() {
4049 if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) { 4061 if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) {
4050 QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme 4062 QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme
4051 : startup_icon_theme); 4063 : startup_icon_theme);
4052 QIcon::setThemeSearchPaths(theme_paths); 4064 QIcon::setThemeSearchPaths(QStringList(default_theme_paths));
4053 if (CheckDarkMode()) { 4065 if (CheckDarkMode()) {
4054 current_theme = QStringLiteral("default_dark"); 4066 current_theme = QStringLiteral("default_dark");
4055 } 4067 }
@@ -4217,10 +4229,12 @@ int main(int argc, char* argv[]) {
4217 // so we can see if we get \u3008 instead 4229 // so we can see if we get \u3008 instead
4218 // TL;DR all other number formats are consecutive in unicode code points 4230 // TL;DR all other number formats are consecutive in unicode code points
4219 // This bug is fixed in Qt6, specifically 6.0.0-alpha1 4231 // This bug is fixed in Qt6, specifically 6.0.0-alpha1
4232#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
4220 const QLocale locale = QLocale::system(); 4233 const QLocale locale = QLocale::system();
4221 if (QStringLiteral("\u3008") == locale.toString(1)) { 4234 if (QStringLiteral("\u3008") == locale.toString(1)) {
4222 QLocale::setDefault(QLocale::system().name()); 4235 QLocale::setDefault(QLocale::system().name());
4223 } 4236 }
4237#endif
4224 4238
4225 // Qt changes the locale and causes issues in float conversion using std::to_string() when 4239 // Qt changes the locale and causes issues in float conversion using std::to_string() when
4226 // generating shaders 4240 // generating shaders
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/main.ui b/src/yuzu/main.ui
index e670acc30..013ba0ceb 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -231,6 +231,9 @@
231 <property name="text"> 231 <property name="text">
232 <string>Con&amp;figure...</string> 232 <string>Con&amp;figure...</string>
233 </property> 233 </property>
234 <property name="menuRole">
235 <enum>QAction::PreferencesRole</enum>
236 </property>
234 </action> 237 </action>
235 <action name="action_Display_Dock_Widget_Headers"> 238 <action name="action_Display_Dock_Widget_Headers">
236 <property name="checkable"> 239 <property name="checkable">
@@ -363,6 +366,9 @@
363 <property name="text"> 366 <property name="text">
364 <string>&amp;Configure TAS...</string> 367 <string>&amp;Configure TAS...</string>
365 </property> 368 </property>
369 <property name="menuRole">
370 <enum>QAction::NoRole</enum>
371 </property>
366 </action> 372 </action>
367 <action name="action_Configure_Current_Game"> 373 <action name="action_Configure_Current_Game">
368 <property name="enabled"> 374 <property name="enabled">
@@ -371,6 +377,9 @@
371 <property name="text"> 377 <property name="text">
372 <string>Configure C&amp;urrent Game...</string> 378 <string>Configure C&amp;urrent Game...</string>
373 </property> 379 </property>
380 <property name="menuRole">
381 <enum>QAction::NoRole</enum>
382 </property>
374 </action> 383 </action>
375 <action name="action_TAS_Start"> 384 <action name="action_TAS_Start">
376 <property name="enabled"> 385 <property name="enabled">
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index 10bf0a4fb..cbd52da85 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -4,7 +4,7 @@
4#include <QComboBox> 4#include <QComboBox>
5#include <QFuture> 5#include <QFuture>
6#include <QIntValidator> 6#include <QIntValidator>
7#include <QRegExpValidator> 7#include <QRegularExpressionValidator>
8#include <QString> 8#include <QString>
9#include <QtConcurrent/QtConcurrentRun> 9#include <QtConcurrent/QtConcurrentRun>
10#include "common/settings.h" 10#include "common/settings.h"
diff --git a/src/yuzu/multiplayer/validation.h b/src/yuzu/multiplayer/validation.h
index dabf860be..dd25af280 100644
--- a/src/yuzu/multiplayer/validation.h
+++ b/src/yuzu/multiplayer/validation.h
@@ -3,7 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <QRegExp> 6#include <QRegularExpression>
7#include <QString> 7#include <QString>
8#include <QValidator> 8#include <QValidator>
9 9
@@ -29,19 +29,21 @@ public:
29 29
30private: 30private:
31 /// room name can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20 31 /// room name can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20
32 QRegExp room_name_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$")); 32 QRegularExpression room_name_regex =
33 QRegExpValidator room_name; 33 QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}"));
34 QRegularExpressionValidator room_name;
34 35
35 /// nickname can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20 36 /// nickname can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20
36 QRegExp nickname_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$")); 37 const QRegularExpression nickname_regex =
37 QRegExpValidator nickname; 38 QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}"));
39 QRegularExpressionValidator nickname;
38 40
39 /// ipv4 address only 41 /// ipv4 address only
40 // TODO remove this when we support hostnames in direct connect 42 // TODO remove this when we support hostnames in direct connect
41 QRegExp ip_regex = QRegExp(QStringLiteral( 43 QRegularExpression ip_regex = QRegularExpression(QStringLiteral(
42 "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|" 44 "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|"
43 "2[0-4][0-9]|25[0-5])")); 45 "2[0-4][0-9]|25[0-5])"));
44 QRegExpValidator ip; 46 QRegularExpressionValidator ip;
45 47
46 /// port must be between 0 and 65535 48 /// port must be between 0 and 65535
47 QIntValidator port; 49 QIntValidator port;
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index 612f29d2c..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"
@@ -27,7 +30,7 @@ void CheckVulkan() {
27 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1); 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 66dd0dc15..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 <>
@@ -299,6 +303,7 @@ void Config::ReadValues() {
299 303
300 ReadSetting("Renderer", Settings::values.resolution_setup); 304 ReadSetting("Renderer", Settings::values.resolution_setup);
301 ReadSetting("Renderer", Settings::values.scaling_filter); 305 ReadSetting("Renderer", Settings::values.scaling_filter);
306 ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
302 ReadSetting("Renderer", Settings::values.anti_aliasing); 307 ReadSetting("Renderer", Settings::values.anti_aliasing);
303 ReadSetting("Renderer", Settings::values.fullscreen_mode); 308 ReadSetting("Renderer", Settings::values.fullscreen_mode);
304 ReadSetting("Renderer", Settings::values.aspect_ratio); 309 ReadSetting("Renderer", Settings::values.aspect_ratio);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index d214771b0..5bbc3f532 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -6,16 +6,22 @@
6namespace DefaultINI { 6namespace DefaultINI {
7 7
8const char* sdl2_config_file = R"( 8const char* sdl2_config_file = R"(
9[ControlsGeneral] 9
10[ControlsP0]
10# The input devices and parameters for each Switch native input 11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
11# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." 13# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
12# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values 14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
13 15
16# Indicates if this player should be connected at boot
17connected=
18
14# for button input, the following devices are available: 19# for button input, the following devices are available:
15# - "keyboard" (default) for keyboard input. Required parameters: 20# - "keyboard" (default) for keyboard input. Required parameters:
16# - "code": the code of the key to bind 21# - "code": the code of the key to bind
17# - "sdl" for joystick input using SDL. Required parameters: 22# - "sdl" for joystick input using SDL. Required parameters:
18# - "joystick": the index of the joystick to bind 23# - "guid": SDL identification GUID of the joystick
24# - "port": the index of the joystick to bind
19# - "button"(optional): the index of the button to bind 25# - "button"(optional): the index of the button to bind
20# - "hat"(optional): the index of the hat to bind as direction buttons 26# - "hat"(optional): the index of the hat to bind as direction buttons
21# - "axis"(optional): the index of the axis to bind 27# - "axis"(optional): the index of the axis to bind
@@ -58,12 +64,29 @@ button_screenshot=
58# - "modifier_scale": a float number representing the applied modifier scale to the analog input. 64# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
59# Must be in range of 0.0-1.0. Defaults to 0.5 65# Must be in range of 0.0-1.0. Defaults to 0.5
60# - "sdl" for joystick input using SDL. Required parameters: 66# - "sdl" for joystick input using SDL. Required parameters:
61# - "joystick": the index of the joystick to bind 67# - "guid": SDL identification GUID of the joystick
68# - "port": the index of the joystick to bind
62# - "axis_x": the index of the axis to bind as x-axis (default to 0) 69# - "axis_x": the index of the axis to bind as x-axis (default to 0)
63# - "axis_y": the index of the axis to bind as y-axis (default to 1) 70# - "axis_y": the index of the axis to bind as y-axis (default to 1)
64lstick= 71lstick=
65rstick= 72rstick=
66 73
74# for motion input, the following devices are available:
75# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
76# - "code": the code of the key to bind
77# - "sdl" for motion input using SDL. Required parameters:
78# - "guid": SDL identification GUID of the joystick
79# - "port": the index of the joystick to bind
80# - "motion": the index of the motion sensor to bind
81# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
82# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
83# - "port": the port of the cemu hook server
84# - "pad": the index of the joystick
85# - "motion": the index of the motion sensor of the joystick to bind
86motionleft=
87motionright=
88
89[ControlsGeneral]
67# To use the debug_pad, prepend `debug_pad_` before each button setting above. 90# To use the debug_pad, prepend `debug_pad_` before each button setting above.
68# i.e. debug_pad_button_a= 91# i.e. debug_pad_button_a=
69 92
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 4ac72c2f6..37dd1747c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -7,6 +7,7 @@
7#include "common/scm_rev.h" 7#include "common/scm_rev.h"
8#include "common/settings.h" 8#include "common/settings.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hid/hid_core.h"
10#include "core/perf_stats.h" 11#include "core/perf_stats.h"
11#include "input_common/drivers/keyboard.h" 12#include "input_common/drivers/keyboard.h"
12#include "input_common/drivers/mouse.h" 13#include "input_common/drivers/mouse.h"
@@ -26,6 +27,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Co
26} 27}
27 28
28EmuWindow_SDL2::~EmuWindow_SDL2() { 29EmuWindow_SDL2::~EmuWindow_SDL2() {
30 system.HIDCore().UnloadInputDevices();
29 input_subsystem->Shutdown(); 31 input_subsystem->Shutdown();
30 SDL_Quit(); 32 SDL_Quit();
31} 33}