summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/audio_core/CMakeLists.txt15
-rw-r--r--src/audio_core/audio_event.cpp1
-rw-r--r--src/audio_core/audio_manager.h2
-rw-r--r--src/audio_core/audio_render_manager.h2
-rw-r--r--src/audio_core/common/feature_support.h1
-rw-r--r--src/audio_core/precompiled_headers.h6
-rw-r--r--src/audio_core/renderer/command/effect/i3dl2_reverb.cpp1
-rw-r--r--src/audio_core/renderer/command/effect/reverb.cpp1
-rw-r--r--src/audio_core/renderer/mix/mix_context.cpp1
-rw-r--r--src/audio_core/renderer/voice/voice_context.cpp1
-rw-r--r--src/audio_core/sink/sink_stream.cpp8
-rw-r--r--src/common/CMakeLists.txt18
-rw-r--r--src/common/common_precompiled_headers.h14
-rw-r--r--src/common/fs/file.cpp2
-rw-r--r--src/common/fs/fs_util.cpp1
-rw-r--r--src/common/fs/path_util.cpp1
-rw-r--r--src/common/input.h1
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/polyfill_ranges.h530
-rw-r--r--src/common/polyfill_thread.h323
-rw-r--r--src/common/precompiled_headers.h6
-rw-r--r--src/common/settings.h4
-rw-r--r--src/common/settings_input.h1
-rw-r--r--src/common/string_util.cpp4
-rw-r--r--src/common/thread_worker.h5
-rw-r--r--src/common/threadsafe_queue.h4
-rw-r--r--src/core/CMakeLists.txt9
-rw-r--r--src/core/arm/arm_interface.cpp8
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp21
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp21
-rw-r--r--src/core/cpu_manager.h1
-rw-r--r--src/core/debugger/debugger.cpp1
-rw-r--r--src/core/file_sys/content_archive.cpp1
-rw-r--r--src/core/frontend/applets/controller.cpp2
-rw-r--r--src/core/frontend/applets/controller.h7
-rw-r--r--src/core/frontend/applets/error.cpp6
-rw-r--r--src/core/frontend/applets/error.h14
-rw-r--r--src/core/frontend/applets/mii_edit.cpp2
-rw-r--r--src/core/frontend/applets/mii_edit.h6
-rw-r--r--src/core/frontend/applets/profile_select.cpp3
-rw-r--r--src/core/frontend/applets/profile_select.h6
-rw-r--r--src/core/frontend/applets/software_keyboard.cpp5
-rw-r--r--src/core/frontend/applets/software_keyboard.h32
-rw-r--r--src/core/frontend/applets/web_browser.cpp11
-rw-r--r--src/core/frontend/applets/web_browser.h24
-rw-r--r--src/core/frontend/emu_window.h2
-rw-r--r--src/core/hid/emulated_console.cpp10
-rw-r--r--src/core/hid/emulated_controller.cpp19
-rw-r--r--src/core/hid/emulated_controller.h3
-rw-r--r--src/core/hid/emulated_devices.h1
-rw-r--r--src/core/hle/kernel/k_memory_block.h33
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.h9
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp4
-rw-r--r--src/core/hle/kernel/k_shared_memory.h2
-rw-r--r--src/core/hle/kernel/k_slab_heap.h6
-rw-r--r--src/core/hle/kernel/k_thread.h4
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h1
-rw-r--r--src/core/hle/kernel/kernel.cpp2
-rw-r--r--src/core/hle/kernel/physical_core.h2
-rw-r--r--src/core/hle/kernel/service_thread.cpp15
-rw-r--r--src/core/hle/kernel/svc_wrap.h4
-rw-r--r--src/core/hle/service/acc/acc.cpp1
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp1
-rw-r--r--src/core/hle/service/am/am.cpp4
-rw-r--r--src/core/hle/service/audio/audin_u.cpp5
-rw-r--r--src/core/hle/service/audio/audout_u.cpp10
-rw-r--r--src/core/hle/service/audio/audren_u.cpp16
-rw-r--r--src/core/hle/service/nfc/mifare_user.cpp400
-rw-r--r--src/core/hle/service/nfc/mifare_user.h52
-rw-r--r--src/core/hle/service/nfc/nfc.cpp27
-rw-r--r--src/core/hle/service/nfc/nfc_device.cpp84
-rw-r--r--src/core/hle/service/nfc/nfc_device.h13
-rw-r--r--src/core/hle/service/nfc/nfc_result.h8
-rw-r--r--src/core/hle/service/nfc/nfc_user.cpp4
-rw-r--r--src/core/hle/service/nfp/nfp_types.h46
-rw-r--r--src/core/hle/service/nifm/nifm.cpp41
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h1
-rw-r--r--src/core/internal_network/network_interface.cpp1
-rw-r--r--src/core/precompiled_headers.h11
-rw-r--r--src/core/reporter.cpp35
-rw-r--r--src/core/reporter.h16
-rw-r--r--src/core/telemetry_session.cpp2
-rw-r--r--src/dedicated_room/CMakeLists.txt5
-rw-r--r--src/dedicated_room/precompiled_headers.h6
-rw-r--r--src/input_common/CMakeLists.txt15
-rw-r--r--src/input_common/drivers/gc_adapter.h2
-rw-r--r--src/input_common/drivers/mouse.cpp1
-rw-r--r--src/input_common/drivers/mouse.h2
-rw-r--r--src/input_common/drivers/tas_input.cpp1
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp38
-rw-r--r--src/input_common/drivers/virtual_amiibo.h7
-rw-r--r--src/input_common/precompiled_headers.h6
-rw-r--r--src/network/CMakeLists.txt7
-rw-r--r--src/network/precompiled_headers.h6
-rw-r--r--src/shader_recompiler/CMakeLists.txt7
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp13
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp35
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.h1
-rw-r--r--src/shader_recompiler/frontend/ir/value.h1
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/decode.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp7
-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/precompiled_headers.h7
-rw-r--r--src/shader_recompiler/shader_info.h3
-rw-r--r--src/tests/CMakeLists.txt5
-rw-r--r--src/tests/precompiled_headers.h6
-rw-r--r--src/video_core/CMakeLists.txt26
-rw-r--r--src/video_core/buffer_cache/buffer_base.h2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h1
-rw-r--r--src/video_core/control/channel_state_cache.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp116
-rw-r--r--src/video_core/engines/maxwell_3d.h1
-rw-r--r--src/video_core/engines/sw_blitter/converter.cpp22
-rw-r--r--src/video_core/gpu_thread.cpp2
-rw-r--r--src/video_core/gpu_thread.h1
-rw-r--r--src/video_core/precompiled_headers.h6
-rw-r--r--src/video_core/rasterizer_interface.h2
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp90
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h78
-rw-r--r--src/video_core/renderer_null/renderer_null.cpp24
-rw-r--r--src/video_core/renderer_null/renderer_null.h36
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp37
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h1
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp3
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h2
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp2
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h14
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp55
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp1
-rw-r--r--src/video_core/shader_cache.h1
-rw-r--r--src/video_core/shader_environment.cpp1
-rw-r--r--src/video_core/shader_environment.h2
-rw-r--r--src/video_core/texture_cache/formatter.cpp1
-rw-r--r--src/video_core/texture_cache/render_targets.h2
-rw-r--r--src/video_core/texture_cache/slot_vector.h1
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h3
-rw-r--r--src/video_core/textures/astc.cpp1
-rw-r--r--src/video_core/transform_feedback.cpp1
-rw-r--r--src/video_core/video_core.cpp4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp290
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h18
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp28
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_surface.cpp38
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp76
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h49
-rw-r--r--src/web_service/CMakeLists.txt7
-rw-r--r--src/web_service/precompiled_headers.h6
-rw-r--r--src/yuzu/CMakeLists.txt20
-rw-r--r--src/yuzu/applets/qt_controller.cpp2
-rw-r--r--src/yuzu/applets/qt_controller.h4
-rw-r--r--src/yuzu/applets/qt_error.cpp6
-rw-r--r--src/yuzu/applets/qt_error.h8
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp3
-rw-r--r--src/yuzu/applets/qt_profile_select.h4
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp5
-rw-r--r--src/yuzu/applets/qt_software_keyboard.h16
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp11
-rw-r--r--src/yuzu/applets/qt_web_browser.h13
-rw-r--r--src/yuzu/bootmanager.cpp24
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/configuration/config.cpp75
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.cpp4
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.ui13
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp9
-rw-r--r--src/yuzu/configuration/configure_graphics.ui5
-rw-r--r--src/yuzu/configuration/configure_input_per_game.cpp115
-rw-r--r--src/yuzu/configuration/configure_input_per_game.h45
-rw-r--r--src/yuzu/configuration/configure_input_per_game.ui333
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp11
-rw-r--r--src/yuzu/configuration/configure_input_player.h2
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp5
-rw-r--r--src/yuzu/configuration/configure_per_game.h6
-rw-r--r--src/yuzu/main.cpp76
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/multiplayer/chat_room.h1
-rw-r--r--src/yuzu/precompiled_headers.h6
-rw-r--r--src/yuzu/startup_checks.cpp2
-rw-r--r--src/yuzu_cmd/CMakeLists.txt18
-rw-r--r--src/yuzu_cmd/config.cpp3
-rw-r--r--src/yuzu_cmd/default_ini.h4
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp51
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_null.h26
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp33
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h2
-rw-r--r--src/yuzu_cmd/precompiled_headers.h6
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
213 files changed, 3722 insertions, 749 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0ac3d254e..140415474 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -82,8 +82,9 @@ if (MSVC)
82 /wd4324 # 'struct_name': structure was padded due to __declspec(align()) 82 /wd4324 # 'struct_name': structure was padded due to __declspec(align())
83 ) 83 )
84 84
85 if (USE_CCACHE) 85 if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS)
86 # when caching, we need to use /Z7 to downgrade debug info to use an older but more cachable format 86 # when caching, we need to use /Z7 to downgrade debug info to use an older but more cachable format
87 # Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21
87 add_compile_options(/Z7) 88 add_compile_options(/Z7)
88 else() 89 else()
89 add_compile_options(/Zi) 90 add_compile_options(/Zi)
@@ -112,6 +113,8 @@ else()
112 113
113 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init> 114 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
114 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field> 115 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
116 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init>
117 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field>
115 ) 118 )
116 119
117 if (ARCHITECTURE_x86_64) 120 if (ARCHITECTURE_x86_64)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 75416c53a..420ba62e0 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -31,6 +31,7 @@ add_library(audio_core STATIC
31 out/audio_out.h 31 out/audio_out.h
32 out/audio_out_system.cpp 32 out/audio_out_system.cpp
33 out/audio_out_system.h 33 out/audio_out_system.h
34 precompiled_headers.h
34 renderer/adsp/adsp.cpp 35 renderer/adsp/adsp.cpp
35 renderer/adsp/adsp.h 36 renderer/adsp/adsp.h
36 renderer/adsp/audio_renderer.cpp 37 renderer/adsp/audio_renderer.cpp
@@ -218,18 +219,18 @@ endif()
218 219
219target_link_libraries(audio_core PUBLIC common core) 220target_link_libraries(audio_core PUBLIC common core)
220if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 221if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
221 target_link_libraries(audio_core PRIVATE dynarmic) 222 target_link_libraries(audio_core PRIVATE dynarmic::dynarmic)
222endif() 223endif()
223 224
224if(ENABLE_CUBEB) 225if(ENABLE_CUBEB)
225 target_link_libraries(audio_core PRIVATE cubeb) 226 target_link_libraries(audio_core PRIVATE cubeb::cubeb)
226 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1) 227 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
227endif() 228endif()
228if(ENABLE_SDL2) 229if(ENABLE_SDL2)
229 if (YUZU_USE_EXTERNAL_SDL2) 230 target_link_libraries(audio_core PRIVATE SDL2::SDL2)
230 target_link_libraries(audio_core PRIVATE SDL2-static)
231 else()
232 target_link_libraries(audio_core PRIVATE SDL2)
233 endif()
234 target_compile_definitions(audio_core PRIVATE HAVE_SDL2) 231 target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
235endif() 232endif()
233
234if (YUZU_USE_PRECOMPILED_HEADERS)
235 target_precompile_headers(audio_core PRIVATE precompiled_headers.h)
236endif()
diff --git a/src/audio_core/audio_event.cpp b/src/audio_core/audio_event.cpp
index 424049c7a..d15568e1f 100644
--- a/src/audio_core/audio_event.cpp
+++ b/src/audio_core/audio_event.cpp
@@ -3,6 +3,7 @@
3 3
4#include "audio_core/audio_event.h" 4#include "audio_core/audio_event.h"
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/polyfill_ranges.h"
6 7
7namespace AudioCore { 8namespace AudioCore {
8 9
diff --git a/src/audio_core/audio_manager.h b/src/audio_core/audio_manager.h
index abf077de4..02270242a 100644
--- a/src/audio_core/audio_manager.h
+++ b/src/audio_core/audio_manager.h
@@ -9,6 +9,8 @@
9#include <mutex> 9#include <mutex>
10#include <thread> 10#include <thread>
11 11
12#include "common/polyfill_thread.h"
13
12#include "audio_core/audio_event.h" 14#include "audio_core/audio_event.h"
13 15
14union Result; 16union Result;
diff --git a/src/audio_core/audio_render_manager.h b/src/audio_core/audio_render_manager.h
index bf4837190..fffa5944d 100644
--- a/src/audio_core/audio_render_manager.h
+++ b/src/audio_core/audio_render_manager.h
@@ -7,6 +7,8 @@
7#include <memory> 7#include <memory>
8#include <mutex> 8#include <mutex>
9 9
10#include "common/polyfill_thread.h"
11
10#include "audio_core/common/common.h" 12#include "audio_core/common/common.h"
11#include "audio_core/renderer/system_manager.h" 13#include "audio_core/renderer/system_manager.h"
12#include "core/hle/service/audio/errors.h" 14#include "core/hle/service/audio/errors.h"
diff --git a/src/audio_core/common/feature_support.h b/src/audio_core/common/feature_support.h
index 55c9e690d..e71905ae8 100644
--- a/src/audio_core/common/feature_support.h
+++ b/src/audio_core/common/feature_support.h
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/polyfill_ranges.h"
13 14
14namespace AudioCore { 15namespace AudioCore {
15constexpr u32 CurrentRevision = 11; 16constexpr u32 CurrentRevision = 11;
diff --git a/src/audio_core/precompiled_headers.h b/src/audio_core/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/audio_core/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
index c4bf3943a..2187d8a65 100644
--- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
+++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
@@ -5,6 +5,7 @@
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/effect/i3dl2_reverb.h" 7#include "audio_core/renderer/command/effect/i3dl2_reverb.h"
8#include "common/polyfill_ranges.h"
8 9
9namespace AudioCore::AudioRenderer { 10namespace AudioCore::AudioRenderer {
10 11
diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp
index fe2b1eb43..427489214 100644
--- a/src/audio_core/renderer/command/effect/reverb.cpp
+++ b/src/audio_core/renderer/command/effect/reverb.cpp
@@ -6,6 +6,7 @@
6 6
7#include "audio_core/renderer/adsp/command_list_processor.h" 7#include "audio_core/renderer/adsp/command_list_processor.h"
8#include "audio_core/renderer/command/effect/reverb.h" 8#include "audio_core/renderer/command/effect/reverb.h"
9#include "common/polyfill_ranges.h"
9 10
10namespace AudioCore::AudioRenderer { 11namespace AudioCore::AudioRenderer {
11 12
diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp
index 2427c83ed..35b748ede 100644
--- a/src/audio_core/renderer/mix/mix_context.cpp
+++ b/src/audio_core/renderer/mix/mix_context.cpp
@@ -5,6 +5,7 @@
5 5
6#include "audio_core/renderer/mix/mix_context.h" 6#include "audio_core/renderer/mix/mix_context.h"
7#include "audio_core/renderer/splitter/splitter_context.h" 7#include "audio_core/renderer/splitter/splitter_context.h"
8#include "common/polyfill_ranges.h"
8 9
9namespace AudioCore::AudioRenderer { 10namespace AudioCore::AudioRenderer {
10 11
diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp
index a501a677d..16a3e839d 100644
--- a/src/audio_core/renderer/voice/voice_context.cpp
+++ b/src/audio_core/renderer/voice/voice_context.cpp
@@ -4,6 +4,7 @@
4#include <ranges> 4#include <ranges>
5 5
6#include "audio_core/renderer/voice/voice_context.h" 6#include "audio_core/renderer/voice/voice_context.h"
7#include "common/polyfill_ranges.h"
7 8
8namespace AudioCore::AudioRenderer { 9namespace AudioCore::AudioRenderer {
9 10
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index 67e194e3c..06c2a876e 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -170,8 +170,8 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
170 170
171 // Get the minimum frames available between the currently playing buffer, and the 171 // Get the minimum frames available between the currently playing buffer, and the
172 // amount we have left to fill 172 // amount we have left to fill
173 size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, 173 size_t frames_available{std::min<u64>(playing_buffer.frames - playing_buffer.frames_played,
174 num_frames - frames_written)}; 174 num_frames - frames_written)};
175 175
176 samples_buffer.Push(&input_buffer[frames_written * frame_size], 176 samples_buffer.Push(&input_buffer[frames_written * frame_size],
177 frames_available * frame_size); 177 frames_available * frame_size);
@@ -241,8 +241,8 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
241 241
242 // Get the minimum frames available between the currently playing buffer, and the 242 // Get the minimum frames available between the currently playing buffer, and the
243 // amount we have left to fill 243 // amount we have left to fill
244 size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, 244 size_t frames_available{std::min<u64>(playing_buffer.frames - playing_buffer.frames_played,
245 num_frames - frames_written)}; 245 num_frames - frames_written)};
246 246
247 samples_buffer.Pop(&output_buffer[frames_written * frame_size], 247 samples_buffer.Pop(&output_buffer[frames_written * frame_size],
248 frames_available * frame_size); 248 frames_available * frame_size);
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index b7c15c191..6bdffcb7a 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -37,6 +37,7 @@ add_library(common STATIC
37 cache_management.cpp 37 cache_management.cpp
38 cache_management.h 38 cache_management.h
39 common_funcs.h 39 common_funcs.h
40 common_precompiled_headers.h
40 common_types.h 41 common_types.h
41 concepts.h 42 concepts.h
42 div_ceil.h 43 div_ceil.h
@@ -95,6 +96,7 @@ add_library(common STATIC
95 param_package.h 96 param_package.h
96 parent_of_member.h 97 parent_of_member.h
97 point.h 98 point.h
99 precompiled_headers.h
98 quaternion.h 100 quaternion.h
99 reader_writer_queue.h 101 reader_writer_queue.h
100 ring_buffer.h 102 ring_buffer.h
@@ -147,7 +149,7 @@ if(ARCHITECTURE_x86_64)
147 x64/xbyak_abi.h 149 x64/xbyak_abi.h
148 x64/xbyak_util.h 150 x64/xbyak_util.h
149 ) 151 )
150 target_link_libraries(common PRIVATE xbyak) 152 target_link_libraries(common PRIVATE xbyak::xbyak)
151endif() 153endif()
152 154
153if (MSVC) 155if (MSVC)
@@ -172,14 +174,8 @@ endif()
172create_target_directory_groups(common) 174create_target_directory_groups(common)
173 175
174target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) 176target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
175if (TARGET lz4::lz4) 177target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd)
176 target_link_libraries(common PRIVATE lz4::lz4) 178
177else() 179if (YUZU_USE_PRECOMPILED_HEADERS)
178 target_link_libraries(common PRIVATE LZ4::lz4_shared) 180 target_precompile_headers(common PRIVATE precompiled_headers.h)
179endif()
180if (TARGET zstd::zstd)
181 target_link_libraries(common PRIVATE zstd::zstd)
182else()
183 target_link_libraries(common PRIVATE
184 $<IF:$<TARGET_EXISTS:zstd::libzstd_shared>,zstd::libzstd_shared,zstd::libzstd_static>)
185endif() 181endif()
diff --git a/src/common/common_precompiled_headers.h b/src/common/common_precompiled_headers.h
new file mode 100644
index 000000000..be7e5b5f9
--- /dev/null
+++ b/src/common/common_precompiled_headers.h
@@ -0,0 +1,14 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <algorithm>
7#include <array>
8#include <chrono>
9#include <memory>
10
11#include <fmt/format.h>
12
13#include "common/assert.h"
14#include "common/common_types.h"
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index fa8422c41..656b03cc5 100644
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.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 <vector>
5
4#include "common/fs/file.h" 6#include "common/fs/file.h"
5#include "common/fs/fs.h" 7#include "common/fs/fs.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp
index eb4ac1deb..813a713c3 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -4,6 +4,7 @@
4#include <algorithm> 4#include <algorithm>
5 5
6#include "common/fs/fs_util.h" 6#include "common/fs/fs_util.h"
7#include "common/polyfill_ranges.h"
7 8
8namespace Common::FS { 9namespace Common::FS {
9 10
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 1074f2421..defa3e918 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <sstream>
5#include <unordered_map> 6#include <unordered_map>
6 7
7#include "common/fs/fs.h" 8#include "common/fs/fs.h"
diff --git a/src/common/input.h b/src/common/input.h
index 449e0193f..fc14fd7bf 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -8,6 +8,7 @@
8#include <string> 8#include <string>
9#include <unordered_map> 9#include <unordered_map>
10#include <utility> 10#include <utility>
11#include <vector>
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12#include "common/param_package.h" 13#include "common/param_package.h"
13#include "common/uuid.h" 14#include "common/uuid.h"
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 15d92505e..2a3bded40 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -4,7 +4,6 @@
4#include <atomic> 4#include <atomic>
5#include <chrono> 5#include <chrono>
6#include <climits> 6#include <climits>
7#include <stop_token>
8#include <thread> 7#include <thread>
9 8
10#include <fmt/format.h> 9#include <fmt/format.h>
@@ -18,6 +17,7 @@
18#include "common/fs/fs_paths.h" 17#include "common/fs/fs_paths.h"
19#include "common/fs/path_util.h" 18#include "common/fs/path_util.h"
20#include "common/literals.h" 19#include "common/literals.h"
20#include "common/polyfill_thread.h"
21#include "common/thread.h" 21#include "common/thread.h"
22 22
23#include "common/logging/backend.h" 23#include "common/logging/backend.h"
diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h
new file mode 100644
index 000000000..ca44bfaef
--- /dev/null
+++ b/src/common/polyfill_ranges.h
@@ -0,0 +1,530 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4//
5// TODO: remove this file when ranges are supported by all compilation targets
6//
7
8#pragma once
9
10#include <algorithm>
11#include <utility>
12#include <version>
13
14#ifndef __cpp_lib_ranges
15
16namespace std {
17namespace ranges {
18
19template <typename T>
20concept range = requires(T& t) {
21 begin(t);
22 end(t);
23};
24
25template <typename T>
26concept input_range = range<T>;
27
28template <typename T>
29concept output_range = range<T>;
30
31template <range R>
32using range_difference_t = ptrdiff_t;
33
34//
35// find, find_if, find_if_not
36//
37
38struct find_fn {
39 template <typename Iterator, typename T, typename Proj = std::identity>
40 constexpr Iterator operator()(Iterator first, Iterator last, const T& value,
41 Proj proj = {}) const {
42 for (; first != last; ++first) {
43 if (std::invoke(proj, *first) == value) {
44 return first;
45 }
46 }
47 return first;
48 }
49
50 template <ranges::input_range R, typename T, typename Proj = std::identity>
51 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Proj proj = {}) const {
52 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
53 }
54};
55
56struct find_if_fn {
57 template <typename Iterator, typename Proj = std::identity, typename Pred>
58 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
59 for (; first != last; ++first) {
60 if (std::invoke(pred, std::invoke(proj, *first))) {
61 return first;
62 }
63 }
64 return first;
65 }
66
67 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
68 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
69 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
70 }
71};
72
73struct find_if_not_fn {
74 template <typename Iterator, typename Proj = std::identity, typename Pred>
75 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
76 for (; first != last; ++first) {
77 if (!std::invoke(pred, std::invoke(proj, *first))) {
78 return first;
79 }
80 }
81 return first;
82 }
83
84 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
85 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
86 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
87 }
88};
89
90inline constexpr find_fn find;
91inline constexpr find_if_fn find_if;
92inline constexpr find_if_not_fn find_if_not;
93
94//
95// any_of, all_of, none_of
96//
97
98struct all_of_fn {
99 template <typename Iterator, typename Proj = std::identity, typename Pred>
100 constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
101 return ranges::find_if_not(first, last, std::ref(pred), std::ref(proj)) == last;
102 }
103
104 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
105 constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
106 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
107 }
108};
109
110struct any_of_fn {
111 template <typename Iterator, typename Proj = std::identity, typename Pred>
112 constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
113 return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) != last;
114 }
115
116 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
117 constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
118 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
119 }
120};
121
122struct none_of_fn {
123 template <typename Iterator, typename Proj = std::identity, typename Pred>
124 constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
125 return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) == last;
126 }
127
128 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
129 constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
130 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
131 }
132};
133
134inline constexpr any_of_fn any_of;
135inline constexpr all_of_fn all_of;
136inline constexpr none_of_fn none_of;
137
138//
139// count, count_if
140//
141
142struct count_fn {
143 template <typename Iterator, typename T, typename Proj = std::identity>
144 constexpr ptrdiff_t operator()(Iterator first, Iterator last, const T& value,
145 Proj proj = {}) const {
146 ptrdiff_t counter = 0;
147 for (; first != last; ++first)
148 if (std::invoke(proj, *first) == value)
149 ++counter;
150 return counter;
151 }
152
153 template <ranges::input_range R, typename T, typename Proj = std::identity>
154 constexpr ptrdiff_t operator()(R&& r, const T& value, Proj proj = {}) const {
155 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
156 }
157};
158
159struct count_if_fn {
160 template <typename Iterator, typename Proj = std::identity, typename Pred>
161 constexpr ptrdiff_t operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
162 ptrdiff_t counter = 0;
163 for (; first != last; ++first)
164 if (std::invoke(pred, std::invoke(proj, *first)))
165 ++counter;
166 return counter;
167 }
168
169 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
170 constexpr ptrdiff_t operator()(R&& r, Pred pred, Proj proj = {}) const {
171 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
172 }
173};
174
175inline constexpr count_fn count;
176inline constexpr count_if_fn count_if;
177
178//
179// transform
180//
181
182struct transform_fn {
183 template <typename InputIterator, typename OutputIterator, typename F,
184 typename Proj = std::identity>
185 constexpr void operator()(InputIterator first1, InputIterator last1, OutputIterator result,
186 F op, Proj proj = {}) const {
187 for (; first1 != last1; ++first1, (void)++result) {
188 *result = std::invoke(op, std::invoke(proj, *first1));
189 }
190 }
191
192 template <ranges::input_range R, typename OutputIterator, typename F,
193 typename Proj = std::identity>
194 constexpr void operator()(R&& r, OutputIterator result, F op, Proj proj = {}) const {
195 return operator()(ranges::begin(r), ranges::end(r), result, std::ref(op), std::ref(proj));
196 }
197};
198
199inline constexpr transform_fn transform;
200
201//
202// sort
203//
204
205struct sort_fn {
206 template <typename Iterator, typename Comp = ranges::less, typename Proj = std::identity>
207 constexpr void operator()(Iterator first, Iterator last, Comp comp = {}, Proj proj = {}) const {
208 if (first == last)
209 return;
210
211 Iterator last_iter = ranges::next(first, last);
212 std::sort(first, last_iter,
213 [&](auto& lhs, auto& rhs) { return comp(proj(lhs), proj(rhs)); });
214 }
215
216 template <ranges::input_range R, typename Comp = ranges::less, typename Proj = std::identity>
217 constexpr void operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
218 return operator()(ranges::begin(r), ranges::end(r), std::move(comp), std::move(proj));
219 }
220};
221
222inline constexpr sort_fn sort;
223
224//
225// fill
226//
227
228struct fill_fn {
229 template <typename T, typename OutputIterator>
230 constexpr OutputIterator operator()(OutputIterator first, OutputIterator last,
231 const T& value) const {
232 while (first != last) {
233 *first++ = value;
234 }
235
236 return first;
237 }
238
239 template <typename T, ranges::output_range R>
240 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value) const {
241 return operator()(ranges::begin(r), ranges::end(r), value);
242 }
243};
244
245inline constexpr fill_fn fill;
246
247//
248// for_each
249//
250
251struct for_each_fn {
252 template <typename Iterator, typename Proj = std::identity, typename Fun>
253 constexpr void operator()(Iterator first, Iterator last, Fun f, Proj proj = {}) const {
254 for (; first != last; ++first) {
255 std::invoke(f, std::invoke(proj, *first));
256 }
257 }
258
259 template <ranges::input_range R, typename Proj = std::identity, typename Fun>
260 constexpr void operator()(R&& r, Fun f, Proj proj = {}) const {
261 return operator()(ranges::begin(r), ranges::end(r), std::move(f), std::ref(proj));
262 }
263};
264
265inline constexpr for_each_fn for_each;
266
267//
268// min_element, max_element
269//
270
271struct min_element_fn {
272 template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
273 constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
274 Proj proj = {}) const {
275 if (first == last) {
276 return last;
277 }
278
279 auto smallest = first;
280 ++first;
281 for (; first != last; ++first) {
282 if (!std::invoke(comp, std::invoke(proj, *smallest), std::invoke(proj, *first))) {
283 smallest = first;
284 }
285 }
286 return smallest;
287 }
288
289 template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
290 constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
291 return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
292 }
293};
294
295struct max_element_fn {
296 template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
297 constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
298 Proj proj = {}) const {
299 if (first == last) {
300 return last;
301 }
302
303 auto largest = first;
304 ++first;
305 for (; first != last; ++first) {
306 if (std::invoke(comp, std::invoke(proj, *largest), std::invoke(proj, *first))) {
307 largest = first;
308 }
309 }
310 return largest;
311 }
312
313 template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
314 constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
315 return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
316 }
317};
318
319inline constexpr min_element_fn min_element;
320inline constexpr max_element_fn max_element;
321
322//
323// replace, replace_if
324//
325
326struct replace_fn {
327 template <typename Iterator, typename T1, typename T2, typename Proj = std::identity>
328 constexpr Iterator operator()(Iterator first, Iterator last, const T1& old_value,
329 const T2& new_value, Proj proj = {}) const {
330 for (; first != last; ++first) {
331 if (old_value == std::invoke(proj, *first)) {
332 *first = new_value;
333 }
334 }
335 return first;
336 }
337
338 template <ranges::input_range R, typename T1, typename T2, typename Proj = std::identity>
339 constexpr ranges::iterator_t<R> operator()(R&& r, const T1& old_value, const T2& new_value,
340 Proj proj = {}) const {
341 return operator()(ranges::begin(r), ranges::end(r), old_value, new_value, std::move(proj));
342 }
343};
344
345struct replace_if_fn {
346 template <typename Iterator, typename T, typename Proj = std::identity, typename Pred>
347 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, const T& new_value,
348 Proj proj = {}) const {
349 for (; first != last; ++first) {
350 if (!!std::invoke(pred, std::invoke(proj, *first))) {
351 *first = new_value;
352 }
353 }
354 return std::move(first);
355 }
356
357 template <ranges::input_range R, typename T, typename Proj = std::identity, typename Pred>
358 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, const T& new_value,
359 Proj proj = {}) const {
360 return operator()(ranges::begin(r), ranges::end(r), std::move(pred), new_value,
361 std::move(proj));
362 }
363};
364
365inline constexpr replace_fn replace;
366inline constexpr replace_if_fn replace_if;
367
368//
369// copy, copy_if
370//
371
372struct copy_fn {
373 template <typename InputIterator, typename OutputIterator>
374 constexpr void operator()(InputIterator first, InputIterator last,
375 OutputIterator result) const {
376 for (; first != last; ++first, (void)++result) {
377 *result = *first;
378 }
379 }
380
381 template <ranges::input_range R, typename OutputIterator>
382 constexpr void operator()(R&& r, OutputIterator result) const {
383 return operator()(ranges::begin(r), ranges::end(r), std::move(result));
384 }
385};
386
387struct copy_if_fn {
388 template <typename InputIterator, typename OutputIterator, typename Proj = std::identity,
389 typename Pred>
390 constexpr void operator()(InputIterator first, InputIterator last, OutputIterator result,
391 Pred pred, Proj proj = {}) const {
392 for (; first != last; ++first) {
393 if (std::invoke(pred, std::invoke(proj, *first))) {
394 *result = *first;
395 ++result;
396 }
397 }
398 }
399
400 template <ranges::input_range R, typename OutputIterator, typename Proj = std::identity,
401 typename Pred>
402 constexpr void operator()(R&& r, OutputIterator result, Pred pred, Proj proj = {}) const {
403 return operator()(ranges::begin(r), ranges::end(r), std::move(result), std::ref(pred),
404 std::ref(proj));
405 }
406};
407
408inline constexpr copy_fn copy;
409inline constexpr copy_if_fn copy_if;
410
411//
412// generate
413//
414
415struct generate_fn {
416 template <typename Iterator, typename F>
417 constexpr Iterator operator()(Iterator first, Iterator last, F gen) const {
418 for (; first != last; *first = std::invoke(gen), ++first)
419 ;
420 return first;
421 }
422
423 template <typename R, std::copy_constructible F>
424 requires std::invocable<F&> && ranges::output_range<R>
425 constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
426 return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
427 }
428};
429
430inline constexpr generate_fn generate;
431
432//
433// lower_bound, upper_bound
434//
435
436struct lower_bound_fn {
437 template <typename Iterator, typename T, typename Proj = std::identity,
438 typename Comp = ranges::less>
439 constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
440 Proj proj = {}) const {
441 Iterator it;
442 std::ptrdiff_t _count, _step;
443 _count = std::distance(first, last);
444
445 while (_count > 0) {
446 it = first;
447 _step = _count / 2;
448 ranges::advance(it, _step, last);
449 if (comp(std::invoke(proj, *it), value)) {
450 first = ++it;
451 _count -= _step + 1;
452 } else {
453 _count = _step;
454 }
455 }
456 return first;
457 }
458
459 template <ranges::input_range R, typename T, typename Proj = std::identity,
460 typename Comp = ranges::less>
461 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
462 Proj proj = {}) const {
463 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
464 }
465};
466
467struct upper_bound_fn {
468 template <typename Iterator, typename T, typename Proj = std::identity,
469 typename Comp = ranges::less>
470 constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
471 Proj proj = {}) const {
472 Iterator it;
473 std::ptrdiff_t _count, _step;
474 _count = std::distance(first, last);
475
476 while (_count > 0) {
477 it = first;
478 _step = _count / 2;
479 ranges::advance(it, _step, last);
480 if (!comp(value, std::invoke(proj, *it))) {
481 first = ++it;
482 _count -= _step + 1;
483 } else {
484 _count = _step;
485 }
486 }
487 return first;
488 }
489
490 template <ranges::input_range R, typename T, typename Proj = std::identity,
491 typename Comp = ranges::less>
492 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
493 Proj proj = {}) const {
494 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
495 }
496};
497
498inline constexpr lower_bound_fn lower_bound;
499inline constexpr upper_bound_fn upper_bound;
500
501//
502// adjacent_find
503//
504
505struct adjacent_find_fn {
506 template <typename Iterator, typename Proj = std::identity, typename Pred = ranges::equal_to>
507 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred = {},
508 Proj proj = {}) const {
509 if (first == last)
510 return first;
511 auto _next = ranges::next(first);
512 for (; _next != last; ++_next, ++first)
513 if (std::invoke(pred, std::invoke(proj, *first), std::invoke(proj, *_next)))
514 return first;
515 return _next;
516 }
517
518 template <ranges::input_range R, typename Proj = std::identity,
519 typename Pred = ranges::equal_to>
520 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred = {}, Proj proj = {}) const {
521 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
522 }
523};
524
525inline constexpr adjacent_find_fn adjacent_find;
526
527} // namespace ranges
528} // namespace std
529
530#endif
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
new file mode 100644
index 000000000..5a8d1ce08
--- /dev/null
+++ b/src/common/polyfill_thread.h
@@ -0,0 +1,323 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4//
5// TODO: remove this file when jthread is supported by all compilation targets
6//
7
8#pragma once
9
10#include <version>
11
12#ifdef __cpp_lib_jthread
13
14#include <stop_token>
15#include <thread>
16
17namespace Common {
18
19template <typename Condvar, typename Lock, typename Pred>
20void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
21 cv.wait(lock, token, std::move(pred));
22}
23
24} // namespace Common
25
26#else
27
28#include <atomic>
29#include <functional>
30#include <list>
31#include <memory>
32#include <mutex>
33#include <optional>
34#include <thread>
35#include <type_traits>
36
37namespace std {
38namespace polyfill {
39
40using stop_state_callbacks = list<function<void()>>;
41
42class stop_state {
43public:
44 stop_state() = default;
45 ~stop_state() = default;
46
47 bool request_stop() {
48 stop_state_callbacks callbacks;
49
50 {
51 scoped_lock lk{m_lock};
52
53 if (m_stop_requested.load()) {
54 // Already set, nothing to do
55 return false;
56 }
57
58 // Set as requested
59 m_stop_requested = true;
60
61 // Copy callback list
62 callbacks = m_callbacks;
63 }
64
65 for (auto callback : callbacks) {
66 callback();
67 }
68
69 return true;
70 }
71
72 bool stop_requested() const {
73 return m_stop_requested.load();
74 }
75
76 stop_state_callbacks::const_iterator insert_callback(function<void()> f) {
77 stop_state_callbacks::const_iterator ret{};
78 bool should_run{};
79
80 {
81 scoped_lock lk{m_lock};
82 should_run = m_stop_requested.load();
83 m_callbacks.push_front(f);
84 ret = m_callbacks.begin();
85 }
86
87 if (should_run) {
88 f();
89 }
90
91 return ret;
92 }
93
94 void remove_callback(stop_state_callbacks::const_iterator it) {
95 scoped_lock lk{m_lock};
96 m_callbacks.erase(it);
97 }
98
99private:
100 mutex m_lock;
101 atomic<bool> m_stop_requested;
102 stop_state_callbacks m_callbacks;
103};
104
105} // namespace polyfill
106
107class stop_token;
108class stop_source;
109struct nostopstate_t {
110 explicit nostopstate_t() = default;
111};
112inline constexpr nostopstate_t nostopstate{};
113
114template <class Callback>
115class stop_callback;
116
117class stop_token {
118public:
119 stop_token() noexcept = default;
120
121 stop_token(const stop_token&) noexcept = default;
122 stop_token(stop_token&&) noexcept = default;
123 stop_token& operator=(const stop_token&) noexcept = default;
124 stop_token& operator=(stop_token&&) noexcept = default;
125 ~stop_token() = default;
126
127 void swap(stop_token& other) noexcept {
128 m_stop_state.swap(other.m_stop_state);
129 }
130
131 [[nodiscard]] bool stop_requested() const noexcept {
132 return m_stop_state && m_stop_state->stop_requested();
133 }
134 [[nodiscard]] bool stop_possible() const noexcept {
135 return m_stop_state != nullptr;
136 }
137
138private:
139 friend class stop_source;
140 template <typename Callback>
141 friend class stop_callback;
142 stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
143
144private:
145 shared_ptr<polyfill::stop_state> m_stop_state;
146};
147
148class stop_source {
149public:
150 stop_source() : m_stop_state(make_shared<polyfill::stop_state>()) {}
151 explicit stop_source(nostopstate_t) noexcept {}
152
153 stop_source(const stop_source&) noexcept = default;
154 stop_source(stop_source&&) noexcept = default;
155 stop_source& operator=(const stop_source&) noexcept = default;
156 stop_source& operator=(stop_source&&) noexcept = default;
157 ~stop_source() = default;
158 void swap(stop_source& other) noexcept {
159 m_stop_state.swap(other.m_stop_state);
160 }
161
162 [[nodiscard]] stop_token get_token() const noexcept {
163 return stop_token(m_stop_state);
164 }
165 [[nodiscard]] bool stop_possible() const noexcept {
166 return m_stop_state != nullptr;
167 }
168 [[nodiscard]] bool stop_requested() const noexcept {
169 return m_stop_state && m_stop_state->stop_requested();
170 }
171 bool request_stop() noexcept {
172 return m_stop_state && m_stop_state->request_stop();
173 }
174
175private:
176 friend class jthread;
177 explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
178 : m_stop_state(move(stop_state)) {}
179
180private:
181 shared_ptr<polyfill::stop_state> m_stop_state;
182};
183
184template <typename Callback>
185class stop_callback {
186 static_assert(is_nothrow_destructible_v<Callback>);
187 static_assert(is_invocable_v<Callback>);
188
189public:
190 using callback_type = Callback;
191
192 template <typename C>
193 requires constructible_from<Callback, C>
194 explicit stop_callback(const stop_token& st,
195 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
196 : m_stop_state(st.m_stop_state) {
197 if (m_stop_state) {
198 m_callback = m_stop_state->insert_callback(move(cb));
199 }
200 }
201 template <typename C>
202 requires constructible_from<Callback, C>
203 explicit stop_callback(stop_token&& st,
204 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
205 : m_stop_state(move(st.m_stop_state)) {
206 if (m_stop_state) {
207 m_callback = m_stop_state->insert_callback(move(cb));
208 }
209 }
210 ~stop_callback() {
211 if (m_stop_state && m_callback) {
212 m_stop_state->remove_callback(*m_callback);
213 }
214 }
215
216 stop_callback(const stop_callback&) = delete;
217 stop_callback(stop_callback&&) = delete;
218 stop_callback& operator=(const stop_callback&) = delete;
219 stop_callback& operator=(stop_callback&&) = delete;
220
221private:
222 shared_ptr<polyfill::stop_state> m_stop_state;
223 optional<polyfill::stop_state_callbacks::const_iterator> m_callback;
224};
225
226template <typename Callback>
227stop_callback(stop_token, Callback) -> stop_callback<Callback>;
228
229class jthread {
230public:
231 using id = thread::id;
232 using native_handle_type = thread::native_handle_type;
233
234 jthread() noexcept = default;
235
236 template <typename F, typename... Args,
237 typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
238 explicit jthread(F&& f, Args&&... args)
239 : m_stop_state(make_shared<polyfill::stop_state>()),
240 m_thread(make_thread(move(f), move(args)...)) {}
241
242 ~jthread() {
243 if (joinable()) {
244 request_stop();
245 join();
246 }
247 }
248
249 jthread(const jthread&) = delete;
250 jthread(jthread&&) noexcept = default;
251 jthread& operator=(const jthread&) = delete;
252
253 jthread& operator=(jthread&& other) noexcept {
254 m_thread.swap(other.m_thread);
255 m_stop_state.swap(other.m_stop_state);
256 return *this;
257 }
258
259 void swap(jthread& other) noexcept {
260 m_thread.swap(other.m_thread);
261 m_stop_state.swap(other.m_stop_state);
262 }
263 [[nodiscard]] bool joinable() const noexcept {
264 return m_thread.joinable();
265 }
266 void join() {
267 m_thread.join();
268 }
269 void detach() {
270 m_thread.detach();
271 m_stop_state.reset();
272 }
273
274 [[nodiscard]] id get_id() const noexcept {
275 return m_thread.get_id();
276 }
277 [[nodiscard]] native_handle_type native_handle() {
278 return m_thread.native_handle();
279 }
280 [[nodiscard]] stop_source get_stop_source() noexcept {
281 return stop_source(m_stop_state);
282 }
283 [[nodiscard]] stop_token get_stop_token() const noexcept {
284 return stop_source(m_stop_state).get_token();
285 }
286 bool request_stop() noexcept {
287 return get_stop_source().request_stop();
288 }
289 [[nodiscard]] static unsigned int hardware_concurrency() noexcept {
290 return thread::hardware_concurrency();
291 }
292
293private:
294 template <typename F, typename... Args>
295 thread make_thread(F&& f, Args&&... args) {
296 if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
297 return thread(move(f), get_stop_token(), move(args)...);
298 } else {
299 return thread(move(f), move(args)...);
300 }
301 }
302
303 shared_ptr<polyfill::stop_state> m_stop_state;
304 thread m_thread;
305};
306
307} // namespace std
308
309namespace Common {
310
311template <typename Condvar, typename Lock, typename Pred>
312void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
313 if (token.stop_requested()) {
314 return;
315 }
316
317 std::stop_callback callback(token, [&] { cv.notify_all(); });
318 cv.wait(lock, [&] { return pred() || token.stop_requested(); });
319}
320
321} // namespace Common
322
323#endif
diff --git a/src/common/precompiled_headers.h b/src/common/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/common/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/common/settings.h b/src/common/settings.h
index 00e4421f7..29b730cff 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -19,6 +19,7 @@ namespace Settings {
19enum class RendererBackend : u32 { 19enum class RendererBackend : u32 {
20 OpenGL = 0, 20 OpenGL = 0,
21 Vulkan = 1, 21 Vulkan = 1,
22 Null = 2,
22}; 23};
23 24
24enum class ShaderBackend : u32 { 25enum class ShaderBackend : u32 {
@@ -399,6 +400,7 @@ struct Values {
399 Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; 400 Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
400 Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"}; 401 Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"};
401 Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"}; 402 Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"};
403 Setting<bool> cpuopt_ignore_memory_aborts{true, "cpuopt_ignore_memory_aborts"};
402 404
403 SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; 405 SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"};
404 SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; 406 SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"};
@@ -411,7 +413,7 @@ struct Values {
411 413
412 // Renderer 414 // Renderer
413 SwitchableSetting<RendererBackend, true> renderer_backend{ 415 SwitchableSetting<RendererBackend, true> renderer_backend{
414 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 416 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
415 Setting<bool> renderer_debug{false, "debug"}; 417 Setting<bool> renderer_debug{false, "debug"};
416 Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; 418 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
417 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 419 Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 485e4ad22..46f38c703 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -391,6 +391,7 @@ struct PlayerInput {
391 u32 body_color_right; 391 u32 body_color_right;
392 u32 button_color_left; 392 u32 button_color_left;
393 u32 button_color_right; 393 u32 button_color_right;
394 std::string profile_name;
394}; 395};
395 396
396struct TouchscreenInput { 397struct TouchscreenInput {
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 7a495bc79..b26db4796 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -141,7 +141,7 @@ static std::wstring CPToUTF16(u32 code_page, const std::string& input) {
141 MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); 141 MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
142 142
143 if (size == 0) { 143 if (size == 0) {
144 return L""; 144 return {};
145 } 145 }
146 146
147 std::wstring output(size, L'\0'); 147 std::wstring output(size, L'\0');
@@ -158,7 +158,7 @@ std::string UTF16ToUTF8(const std::wstring& input) {
158 const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), 158 const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
159 nullptr, 0, nullptr, nullptr); 159 nullptr, 0, nullptr, nullptr);
160 if (size == 0) { 160 if (size == 0) {
161 return ""; 161 return {};
162 } 162 }
163 163
164 std::string output(size, '\0'); 164 std::string output(size, '\0');
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
index 62c60f724..260ad44e4 100644
--- a/src/common/thread_worker.h
+++ b/src/common/thread_worker.h
@@ -7,13 +7,13 @@
7#include <condition_variable> 7#include <condition_variable>
8#include <functional> 8#include <functional>
9#include <mutex> 9#include <mutex>
10#include <stop_token>
11#include <string> 10#include <string>
12#include <thread> 11#include <thread>
13#include <type_traits> 12#include <type_traits>
14#include <vector> 13#include <vector>
15#include <queue> 14#include <queue>
16 15
16#include "common/polyfill_thread.h"
17#include "common/thread.h" 17#include "common/thread.h"
18#include "common/unique_function.h" 18#include "common/unique_function.h"
19 19
@@ -47,7 +47,8 @@ public:
47 if (requests.empty()) { 47 if (requests.empty()) {
48 wait_condition.notify_all(); 48 wait_condition.notify_all();
49 } 49 }
50 condition.wait(lock, stop_token, [this] { return !requests.empty(); }); 50 Common::CondvarWait(condition, lock, stop_token,
51 [this] { return !requests.empty(); });
51 if (stop_token.stop_requested()) { 52 if (stop_token.stop_requested()) {
52 break; 53 break;
53 } 54 }
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 053798e79..2ef1da064 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -12,6 +12,8 @@
12#include <mutex> 12#include <mutex>
13#include <utility> 13#include <utility>
14 14
15#include "common/polyfill_thread.h"
16
15namespace Common { 17namespace Common {
16template <typename T, bool with_stop_token = false> 18template <typename T, bool with_stop_token = false>
17class SPSCQueue { 19class SPSCQueue {
@@ -97,7 +99,7 @@ public:
97 T PopWait(std::stop_token stop_token) { 99 T PopWait(std::stop_token stop_token) {
98 if (Empty()) { 100 if (Empty()) {
99 std::unique_lock lock{cv_mutex}; 101 std::unique_lock lock{cv_mutex};
100 cv.wait(lock, stop_token, [this] { return !Empty(); }); 102 Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); });
101 } 103 }
102 if (stop_token.stop_requested()) { 104 if (stop_token.stop_requested()) {
103 return T{}; 105 return T{};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5629980d9..c6b5ac196 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -528,6 +528,8 @@ add_library(core STATIC
528 hle/service/mnpp/mnpp_app.h 528 hle/service/mnpp/mnpp_app.h
529 hle/service/ncm/ncm.cpp 529 hle/service/ncm/ncm.cpp
530 hle/service/ncm/ncm.h 530 hle/service/ncm/ncm.h
531 hle/service/nfc/mifare_user.cpp
532 hle/service/nfc/mifare_user.h
531 hle/service/nfc/nfc.cpp 533 hle/service/nfc/nfc.cpp
532 hle/service/nfc/nfc.h 534 hle/service/nfc/nfc.h
533 hle/service/nfc/nfc_device.cpp 535 hle/service/nfc/nfc_device.cpp
@@ -771,6 +773,7 @@ add_library(core STATIC
771 memory.h 773 memory.h
772 perf_stats.cpp 774 perf_stats.cpp
773 perf_stats.h 775 perf_stats.h
776 precompiled_headers.h
774 reporter.cpp 777 reporter.cpp
775 reporter.h 778 reporter.h
776 telemetry_session.cpp 779 telemetry_session.cpp
@@ -823,5 +826,9 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
823 hle/service/jit/jit.cpp 826 hle/service/jit/jit.cpp
824 hle/service/jit/jit.h 827 hle/service/jit/jit.h
825 ) 828 )
826 target_link_libraries(core PRIVATE dynarmic) 829 target_link_libraries(core PRIVATE dynarmic::dynarmic)
830endif()
831
832if (YUZU_USE_PRECOMPILED_HEADERS)
833 target_precompile_headers(core PRIVATE precompiled_headers.h)
827endif() 834endif()
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 29ba562dc..2df7b0ee8 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -145,11 +145,15 @@ void ARM_Interface::Run() {
145 // Notify the debugger and go to sleep if a breakpoint was hit, 145 // Notify the debugger and go to sleep if a breakpoint was hit,
146 // or if the thread is unable to continue for any reason. 146 // or if the thread is unable to continue for any reason.
147 if (Has(hr, breakpoint) || Has(hr, no_execute)) { 147 if (Has(hr, breakpoint) || Has(hr, no_execute)) {
148 RewindBreakpointInstruction(); 148 if (!Has(hr, no_execute)) {
149 RewindBreakpointInstruction();
150 }
149 if (system.DebuggerEnabled()) { 151 if (system.DebuggerEnabled()) {
150 system.GetDebugger().NotifyThreadStopped(current_thread); 152 system.GetDebugger().NotifyThreadStopped(current_thread);
153 } else {
154 LogBacktrace();
151 } 155 }
152 current_thread->RequestSuspend(Kernel::SuspendType::Debug); 156 current_thread->RequestSuspend(SuspendType::Debug);
153 break; 157 break;
154 } 158 }
155 159
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 227e06ea1..947747d36 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -29,7 +29,9 @@ class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
29public: 29public:
30 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) 30 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
31 : parent{parent_}, 31 : parent{parent_},
32 memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {} 32 memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()},
33 check_memory_access{debugger_enabled ||
34 !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {}
33 35
34 u8 MemoryRead8(u32 vaddr) override { 36 u8 MemoryRead8(u32 vaddr) override {
35 CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); 37 CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
@@ -154,6 +156,17 @@ public:
154 } 156 }
155 157
156 bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) { 158 bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
159 if (!check_memory_access) {
160 return true;
161 }
162
163 if (!memory.IsValidVirtualAddressRange(addr, size)) {
164 LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}",
165 addr);
166 parent.jit.load()->HaltExecution(ARM_Interface::no_execute);
167 return false;
168 }
169
157 if (!debugger_enabled) { 170 if (!debugger_enabled) {
158 return true; 171 return true;
159 } 172 }
@@ -181,7 +194,8 @@ public:
181 ARM_Dynarmic_32& parent; 194 ARM_Dynarmic_32& parent;
182 Core::Memory::Memory& memory; 195 Core::Memory::Memory& memory;
183 std::size_t num_interpreted_instructions{}; 196 std::size_t num_interpreted_instructions{};
184 bool debugger_enabled{}; 197 const bool debugger_enabled{};
198 const bool check_memory_access{};
185 static constexpr u64 minimum_run_cycles = 10000U; 199 static constexpr u64 minimum_run_cycles = 10000U;
186}; 200};
187 201
@@ -264,6 +278,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
264 if (!Settings::values.cpuopt_recompile_exclusives) { 278 if (!Settings::values.cpuopt_recompile_exclusives) {
265 config.recompile_on_exclusive_fastmem_failure = false; 279 config.recompile_on_exclusive_fastmem_failure = false;
266 } 280 }
281 if (!Settings::values.cpuopt_ignore_memory_aborts) {
282 config.check_halt_on_memory_access = true;
283 }
267 } else { 284 } else {
268 // Unsafe optimizations 285 // Unsafe optimizations
269 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 286 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index cb53d64ba..3df943df7 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -29,7 +29,9 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
29public: 29public:
30 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) 30 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
31 : parent{parent_}, 31 : parent{parent_},
32 memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {} 32 memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()},
33 check_memory_access{debugger_enabled ||
34 !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {}
33 35
34 u8 MemoryRead8(u64 vaddr) override { 36 u8 MemoryRead8(u64 vaddr) override {
35 CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); 37 CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
@@ -198,6 +200,17 @@ public:
198 } 200 }
199 201
200 bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) { 202 bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
203 if (!check_memory_access) {
204 return true;
205 }
206
207 if (!memory.IsValidVirtualAddressRange(addr, size)) {
208 LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}",
209 addr);
210 parent.jit.load()->HaltExecution(ARM_Interface::no_execute);
211 return false;
212 }
213
201 if (!debugger_enabled) { 214 if (!debugger_enabled) {
202 return true; 215 return true;
203 } 216 }
@@ -226,7 +239,8 @@ public:
226 Core::Memory::Memory& memory; 239 Core::Memory::Memory& memory;
227 u64 tpidrro_el0 = 0; 240 u64 tpidrro_el0 = 0;
228 u64 tpidr_el0 = 0; 241 u64 tpidr_el0 = 0;
229 bool debugger_enabled{}; 242 const bool debugger_enabled{};
243 const bool check_memory_access{};
230 static constexpr u64 minimum_run_cycles = 10000U; 244 static constexpr u64 minimum_run_cycles = 10000U;
231}; 245};
232 246
@@ -323,6 +337,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
323 if (!Settings::values.cpuopt_recompile_exclusives) { 337 if (!Settings::values.cpuopt_recompile_exclusives) {
324 config.recompile_on_exclusive_fastmem_failure = false; 338 config.recompile_on_exclusive_fastmem_failure = false;
325 } 339 }
340 if (!Settings::values.cpuopt_ignore_memory_aborts) {
341 config.check_halt_on_memory_access = true;
342 }
326 } else { 343 } else {
327 // Unsafe optimizations 344 // Unsafe optimizations
328 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 345 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 95ea3ef39..374367468 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -10,6 +10,7 @@
10#include <thread> 10#include <thread>
11 11
12#include "common/fiber.h" 12#include "common/fiber.h"
13#include "common/polyfill_thread.h"
13#include "common/thread.h" 14#include "common/thread.h"
14#include "core/hardware_properties.h" 15#include "core/hardware_properties.h"
15 16
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 1a8e02e6a..a9675df76 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -9,6 +9,7 @@
9#include <boost/process/async_pipe.hpp> 9#include <boost/process/async_pipe.hpp>
10 10
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/polyfill_thread.h"
12#include "common/thread.h" 13#include "common/thread.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/debugger/debugger.h" 15#include "core/debugger/debugger.h"
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 78e56bbbd..50303fe42 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -7,6 +7,7 @@
7#include <utility> 7#include <utility>
8 8
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/polyfill_ranges.h"
10#include "core/crypto/aes_util.h" 11#include "core/crypto/aes_util.h"
11#include "core/crypto/ctr_encryption_layer.h" 12#include "core/crypto/ctr_encryption_layer.h"
12#include "core/crypto/key_manager.h" 13#include "core/crypto/key_manager.h"
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 6c230f619..52919484e 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -16,7 +16,7 @@ DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_
16 16
17DefaultControllerApplet::~DefaultControllerApplet() = default; 17DefaultControllerApplet::~DefaultControllerApplet() = default;
18 18
19void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback, 19void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callback,
20 const ControllerParameters& parameters) const { 20 const ControllerParameters& parameters) const {
21 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); 21 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
22 22
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
index 1d2850ad5..adb2feefd 100644
--- a/src/core/frontend/applets/controller.h
+++ b/src/core/frontend/applets/controller.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <functional> 6#include <functional>
7#include <vector>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
@@ -35,9 +36,11 @@ struct ControllerParameters {
35 36
36class ControllerApplet { 37class ControllerApplet {
37public: 38public:
39 using ReconfigureCallback = std::function<void()>;
40
38 virtual ~ControllerApplet(); 41 virtual ~ControllerApplet();
39 42
40 virtual void ReconfigureControllers(std::function<void()> callback, 43 virtual void ReconfigureControllers(ReconfigureCallback callback,
41 const ControllerParameters& parameters) const = 0; 44 const ControllerParameters& parameters) const = 0;
42}; 45};
43 46
@@ -46,7 +49,7 @@ public:
46 explicit DefaultControllerApplet(HID::HIDCore& hid_core_); 49 explicit DefaultControllerApplet(HID::HIDCore& hid_core_);
47 ~DefaultControllerApplet() override; 50 ~DefaultControllerApplet() override;
48 51
49 void ReconfigureControllers(std::function<void()> callback, 52 void ReconfigureControllers(ReconfigureCallback callback,
50 const ControllerParameters& parameters) const override; 53 const ControllerParameters& parameters) const override;
51 54
52private: 55private:
diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp
index f8b961098..69c2b2b4d 100644
--- a/src/core/frontend/applets/error.cpp
+++ b/src/core/frontend/applets/error.cpp
@@ -8,13 +8,13 @@ namespace Core::Frontend {
8 8
9ErrorApplet::~ErrorApplet() = default; 9ErrorApplet::~ErrorApplet() = default;
10 10
11void DefaultErrorApplet::ShowError(Result error, std::function<void()> finished) const { 11void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const {
12 LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", 12 LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
13 error.module.Value(), error.description.Value(), error.raw); 13 error.module.Value(), error.description.Value(), error.raw);
14} 14}
15 15
16void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, 16void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
17 std::function<void()> finished) const { 17 FinishedCallback finished) const {
18 LOG_CRITICAL( 18 LOG_CRITICAL(
19 Service_Fatal, 19 Service_Fatal,
20 "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}", 20 "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
@@ -23,7 +23,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::secon
23 23
24void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text, 24void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text,
25 std::string detail_text, 25 std::string detail_text,
26 std::function<void()> finished) const { 26 FinishedCallback finished) const {
27 LOG_CRITICAL(Service_Fatal, 27 LOG_CRITICAL(Service_Fatal,
28 "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})", 28 "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
29 error.module.Value(), error.description.Value(), error.raw); 29 error.module.Value(), error.description.Value(), error.raw);
diff --git a/src/core/frontend/applets/error.h b/src/core/frontend/applets/error.h
index f378f8805..884f2f653 100644
--- a/src/core/frontend/applets/error.h
+++ b/src/core/frontend/applets/error.h
@@ -12,25 +12,27 @@ namespace Core::Frontend {
12 12
13class ErrorApplet { 13class ErrorApplet {
14public: 14public:
15 using FinishedCallback = std::function<void()>;
16
15 virtual ~ErrorApplet(); 17 virtual ~ErrorApplet();
16 18
17 virtual void ShowError(Result error, std::function<void()> finished) const = 0; 19 virtual void ShowError(Result error, FinishedCallback finished) const = 0;
18 20
19 virtual void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, 21 virtual void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
20 std::function<void()> finished) const = 0; 22 FinishedCallback finished) const = 0;
21 23
22 virtual void ShowCustomErrorText(Result error, std::string dialog_text, 24 virtual void ShowCustomErrorText(Result error, std::string dialog_text,
23 std::string fullscreen_text, 25 std::string fullscreen_text,
24 std::function<void()> finished) const = 0; 26 FinishedCallback finished) const = 0;
25}; 27};
26 28
27class DefaultErrorApplet final : public ErrorApplet { 29class DefaultErrorApplet final : public ErrorApplet {
28public: 30public:
29 void ShowError(Result error, std::function<void()> finished) const override; 31 void ShowError(Result error, FinishedCallback finished) const override;
30 void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, 32 void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
31 std::function<void()> finished) const override; 33 FinishedCallback finished) const override;
32 void ShowCustomErrorText(Result error, std::string main_text, std::string detail_text, 34 void ShowCustomErrorText(Result error, std::string main_text, std::string detail_text,
33 std::function<void()> finished) const override; 35 FinishedCallback finished) const override;
34}; 36};
35 37
36} // namespace Core::Frontend 38} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/mii_edit.cpp b/src/core/frontend/applets/mii_edit.cpp
index d37b5368a..bc8c57067 100644
--- a/src/core/frontend/applets/mii_edit.cpp
+++ b/src/core/frontend/applets/mii_edit.cpp
@@ -8,7 +8,7 @@ namespace Core::Frontend {
8 8
9MiiEditApplet::~MiiEditApplet() = default; 9MiiEditApplet::~MiiEditApplet() = default;
10 10
11void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const { 11void DefaultMiiEditApplet::ShowMiiEdit(const MiiEditCallback& callback) const {
12 LOG_WARNING(Service_AM, "(STUBBED) called"); 12 LOG_WARNING(Service_AM, "(STUBBED) called");
13 13
14 callback(); 14 callback();
diff --git a/src/core/frontend/applets/mii_edit.h b/src/core/frontend/applets/mii_edit.h
index 58fa2039b..d828f06ec 100644
--- a/src/core/frontend/applets/mii_edit.h
+++ b/src/core/frontend/applets/mii_edit.h
@@ -9,14 +9,16 @@ namespace Core::Frontend {
9 9
10class MiiEditApplet { 10class MiiEditApplet {
11public: 11public:
12 using MiiEditCallback = std::function<void()>;
13
12 virtual ~MiiEditApplet(); 14 virtual ~MiiEditApplet();
13 15
14 virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0; 16 virtual void ShowMiiEdit(const MiiEditCallback& callback) const = 0;
15}; 17};
16 18
17class DefaultMiiEditApplet final : public MiiEditApplet { 19class DefaultMiiEditApplet final : public MiiEditApplet {
18public: 20public:
19 void ShowMiiEdit(const std::function<void()>& callback) const override; 21 void ShowMiiEdit(const MiiEditCallback& callback) const override;
20}; 22};
21 23
22} // namespace Core::Frontend 24} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp
index d11fbce0a..da4cfbf87 100644
--- a/src/core/frontend/applets/profile_select.cpp
+++ b/src/core/frontend/applets/profile_select.cpp
@@ -9,8 +9,7 @@ namespace Core::Frontend {
9 9
10ProfileSelectApplet::~ProfileSelectApplet() = default; 10ProfileSelectApplet::~ProfileSelectApplet() = default;
11 11
12void DefaultProfileSelectApplet::SelectProfile( 12void DefaultProfileSelectApplet::SelectProfile(SelectProfileCallback callback) const {
13 std::function<void(std::optional<Common::UUID>)> callback) const {
14 Service::Account::ProfileManager manager; 13 Service::Account::ProfileManager manager;
15 callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{})); 14 callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
16 LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); 15 LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h
index 8d6ee5279..138429533 100644
--- a/src/core/frontend/applets/profile_select.h
+++ b/src/core/frontend/applets/profile_select.h
@@ -11,14 +11,16 @@ namespace Core::Frontend {
11 11
12class ProfileSelectApplet { 12class ProfileSelectApplet {
13public: 13public:
14 using SelectProfileCallback = std::function<void(std::optional<Common::UUID>)>;
15
14 virtual ~ProfileSelectApplet(); 16 virtual ~ProfileSelectApplet();
15 17
16 virtual void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const = 0; 18 virtual void SelectProfile(SelectProfileCallback callback) const = 0;
17}; 19};
18 20
19class DefaultProfileSelectApplet final : public ProfileSelectApplet { 21class DefaultProfileSelectApplet final : public ProfileSelectApplet {
20public: 22public:
21 void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override; 23 void SelectProfile(SelectProfileCallback callback) const override;
22}; 24};
23 25
24} // namespace Core::Frontend 26} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp
index 020c7fa5e..a3720f4d7 100644
--- a/src/core/frontend/applets/software_keyboard.cpp
+++ b/src/core/frontend/applets/software_keyboard.cpp
@@ -15,10 +15,7 @@ DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default;
15 15
16void DefaultSoftwareKeyboardApplet::InitializeKeyboard( 16void DefaultSoftwareKeyboardApplet::InitializeKeyboard(
17 bool is_inline, KeyboardInitializeParameters initialize_parameters, 17 bool is_inline, KeyboardInitializeParameters initialize_parameters,
18 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> 18 SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) {
19 submit_normal_callback_,
20 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
21 submit_inline_callback_) {
22 if (is_inline) { 19 if (is_inline) {
23 LOG_WARNING( 20 LOG_WARNING(
24 Service_AM, 21 Service_AM,
diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h
index 094d1e713..8aef103d3 100644
--- a/src/core/frontend/applets/software_keyboard.h
+++ b/src/core/frontend/applets/software_keyboard.h
@@ -54,14 +54,17 @@ struct InlineTextParameters {
54 54
55class SoftwareKeyboardApplet { 55class SoftwareKeyboardApplet {
56public: 56public:
57 using SubmitInlineCallback =
58 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>;
59 using SubmitNormalCallback =
60 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>;
61
57 virtual ~SoftwareKeyboardApplet(); 62 virtual ~SoftwareKeyboardApplet();
58 63
59 virtual void InitializeKeyboard( 64 virtual void InitializeKeyboard(bool is_inline,
60 bool is_inline, KeyboardInitializeParameters initialize_parameters, 65 KeyboardInitializeParameters initialize_parameters,
61 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> 66 SubmitNormalCallback submit_normal_callback_,
62 submit_normal_callback_, 67 SubmitInlineCallback submit_inline_callback_) = 0;
63 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
64 submit_inline_callback_) = 0;
65 68
66 virtual void ShowNormalKeyboard() const = 0; 69 virtual void ShowNormalKeyboard() const = 0;
67 70
@@ -81,12 +84,9 @@ class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
81public: 84public:
82 ~DefaultSoftwareKeyboardApplet() override; 85 ~DefaultSoftwareKeyboardApplet() override;
83 86
84 void InitializeKeyboard( 87 void InitializeKeyboard(bool is_inline, KeyboardInitializeParameters initialize_parameters,
85 bool is_inline, KeyboardInitializeParameters initialize_parameters, 88 SubmitNormalCallback submit_normal_callback_,
86 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> 89 SubmitInlineCallback submit_inline_callback_) override;
87 submit_normal_callback_,
88 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
89 submit_inline_callback_) override;
90 90
91 void ShowNormalKeyboard() const override; 91 void ShowNormalKeyboard() const override;
92 92
@@ -105,12 +105,10 @@ private:
105 void SubmitNormalText(std::u16string text) const; 105 void SubmitNormalText(std::u16string text) const;
106 void SubmitInlineText(std::u16string_view text) const; 106 void SubmitInlineText(std::u16string_view text) const;
107 107
108 KeyboardInitializeParameters parameters; 108 KeyboardInitializeParameters parameters{};
109 109
110 mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> 110 mutable SubmitNormalCallback submit_normal_callback;
111 submit_normal_callback; 111 mutable SubmitInlineCallback submit_inline_callback;
112 mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
113 submit_inline_callback;
114}; 112};
115 113
116} // namespace Core::Frontend 114} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp
index 27c7086be..b09cb7102 100644
--- a/src/core/frontend/applets/web_browser.cpp
+++ b/src/core/frontend/applets/web_browser.cpp
@@ -10,18 +10,17 @@ WebBrowserApplet::~WebBrowserApplet() = default;
10 10
11DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; 11DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
12 12
13void DefaultWebBrowserApplet::OpenLocalWebPage( 13void DefaultWebBrowserApplet::OpenLocalWebPage(const std::string& local_url,
14 const std::string& local_url, std::function<void()> extract_romfs_callback, 14 ExtractROMFSCallback extract_romfs_callback,
15 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const { 15 OpenWebPageCallback callback) const {
16 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}", 16 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}",
17 local_url); 17 local_url);
18 18
19 callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); 19 callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/");
20} 20}
21 21
22void DefaultWebBrowserApplet::OpenExternalWebPage( 22void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_url,
23 const std::string& external_url, 23 OpenWebPageCallback callback) const {
24 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const {
25 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}", 24 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}",
26 external_url); 25 external_url);
27 26
diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h
index 1411274f8..4f72284ad 100644
--- a/src/core/frontend/applets/web_browser.h
+++ b/src/core/frontend/applets/web_browser.h
@@ -11,29 +11,29 @@ namespace Core::Frontend {
11 11
12class WebBrowserApplet { 12class WebBrowserApplet {
13public: 13public:
14 using ExtractROMFSCallback = std::function<void()>;
15 using OpenWebPageCallback =
16 std::function<void(Service::AM::Applets::WebExitReason, std::string)>;
17
14 virtual ~WebBrowserApplet(); 18 virtual ~WebBrowserApplet();
15 19
16 virtual void OpenLocalWebPage( 20 virtual void OpenLocalWebPage(const std::string& local_url,
17 const std::string& local_url, std::function<void()> extract_romfs_callback, 21 ExtractROMFSCallback extract_romfs_callback,
18 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0; 22 OpenWebPageCallback callback) const = 0;
19 23
20 virtual void OpenExternalWebPage( 24 virtual void OpenExternalWebPage(const std::string& external_url,
21 const std::string& external_url, 25 OpenWebPageCallback callback) const = 0;
22 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0;
23}; 26};
24 27
25class DefaultWebBrowserApplet final : public WebBrowserApplet { 28class DefaultWebBrowserApplet final : public WebBrowserApplet {
26public: 29public:
27 ~DefaultWebBrowserApplet() override; 30 ~DefaultWebBrowserApplet() override;
28 31
29 void OpenLocalWebPage(const std::string& local_url, 32 void OpenLocalWebPage(const std::string& local_url, ExtractROMFSCallback extract_romfs_callback,
30 std::function<void()> extract_romfs_callback, 33 OpenWebPageCallback callback) const override;
31 std::function<void(Service::AM::Applets::WebExitReason, std::string)>
32 callback) const override;
33 34
34 void OpenExternalWebPage(const std::string& external_url, 35 void OpenExternalWebPage(const std::string& external_url,
35 std::function<void(Service::AM::Applets::WebExitReason, std::string)> 36 OpenWebPageCallback callback) const override;
36 callback) const override;
37}; 37};
38 38
39} // namespace Core::Frontend 39} // namespace Core::Frontend
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index ac1906d5e..95363b645 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -17,6 +17,8 @@ enum class WindowSystemType {
17 Windows, 17 Windows,
18 X11, 18 X11,
19 Wayland, 19 Wayland,
20 Cocoa,
21 Android,
20}; 22};
21 23
22/** 24/**
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index b6c8cc58d..30c2e9d17 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -37,7 +37,7 @@ void EmulatedConsole::SetTouchParams() {
37 touchscreen_param.Set("axis_x", i * 2); 37 touchscreen_param.Set("axis_x", i * 2);
38 touchscreen_param.Set("axis_y", (i * 2) + 1); 38 touchscreen_param.Set("axis_y", (i * 2) + 1);
39 touchscreen_param.Set("button", i); 39 touchscreen_param.Set("button", i);
40 touch_params[index++] = touchscreen_param; 40 touch_params[index++] = std::move(touchscreen_param);
41 } 41 }
42 42
43 const auto button_index = 43 const auto button_index =
@@ -59,7 +59,7 @@ void EmulatedConsole::SetTouchParams() {
59 touch_button_params.Set("button", params.Serialize()); 59 touch_button_params.Set("button", params.Serialize());
60 touch_button_params.Set("x", x); 60 touch_button_params.Set("x", x);
61 touch_button_params.Set("y", y); 61 touch_button_params.Set("y", y);
62 touch_params[index] = touch_button_params; 62 touch_params[index] = std::move(touch_button_params);
63 index++; 63 index++;
64 } 64 }
65} 65}
@@ -131,7 +131,7 @@ Common::ParamPackage EmulatedConsole::GetMotionParam() const {
131} 131}
132 132
133void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { 133void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
134 motion_params = param; 134 motion_params = std::move(param);
135 ReloadInput(); 135 ReloadInput();
136} 136}
137 137
@@ -199,7 +199,7 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
199 199
200 if (is_new_input) { 200 if (is_new_input) {
201 touch_value.pressed.value = true; 201 touch_value.pressed.value = true;
202 touch_value.id = static_cast<u32>(index); 202 touch_value.id = static_cast<int>(index);
203 } 203 }
204 204
205 touch_value.x = touch_input.x; 205 touch_value.x = touch_input.x;
@@ -284,7 +284,7 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
284 284
285int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { 285int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
286 std::scoped_lock lock{callback_mutex}; 286 std::scoped_lock lock{callback_mutex};
287 callback_list.insert_or_assign(last_callback_key, update_callback); 287 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
288 return last_callback_key++; 288 return last_callback_key++;
289} 289}
290 290
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index c96d9eef3..67969e938 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -3,6 +3,7 @@
3 3
4#include <algorithm> 4#include <algorithm>
5 5
6#include "common/polyfill_ranges.h"
6#include "common/thread.h" 7#include "common/thread.h"
7#include "core/hid/emulated_controller.h" 8#include "core/hid/emulated_controller.h"
8#include "core/hid/input_converter.h" 9#include "core/hid/input_converter.h"
@@ -109,10 +110,9 @@ void EmulatedController::ReloadFromSettings() {
109 original_npad_type = npad_type; 110 original_npad_type = npad_type;
110 } 111 }
111 112
113 Disconnect();
112 if (player.connected) { 114 if (player.connected) {
113 Connect(); 115 Connect();
114 } else {
115 Disconnect();
116 } 116 }
117 117
118 ReloadInput(); 118 ReloadInput();
@@ -424,15 +424,14 @@ void EmulatedController::RestoreConfig() {
424 ReloadFromSettings(); 424 ReloadFromSettings();
425} 425}
426 426
427std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices( 427std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices() const {
428 EmulatedDeviceIndex device_index) const {
429 std::vector<Common::ParamPackage> devices; 428 std::vector<Common::ParamPackage> devices;
430 for (const auto& param : button_params) { 429 for (const auto& param : button_params) {
431 if (!param.Has("engine")) { 430 if (!param.Has("engine")) {
432 continue; 431 continue;
433 } 432 }
434 const auto devices_it = std::find_if( 433 const auto devices_it = std::find_if(
435 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { 434 devices.begin(), devices.end(), [&param](const Common::ParamPackage& param_) {
436 return param.Get("engine", "") == param_.Get("engine", "") && 435 return param.Get("engine", "") == param_.Get("engine", "") &&
437 param.Get("guid", "") == param_.Get("guid", "") && 436 param.Get("guid", "") == param_.Get("guid", "") &&
438 param.Get("port", 0) == param_.Get("port", 0) && 437 param.Get("port", 0) == param_.Get("port", 0) &&
@@ -441,12 +440,12 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
441 if (devices_it != devices.end()) { 440 if (devices_it != devices.end()) {
442 continue; 441 continue;
443 } 442 }
444 Common::ParamPackage device{}; 443
444 auto& device = devices.emplace_back();
445 device.Set("engine", param.Get("engine", "")); 445 device.Set("engine", param.Get("engine", ""));
446 device.Set("guid", param.Get("guid", "")); 446 device.Set("guid", param.Get("guid", ""));
447 device.Set("port", param.Get("port", 0)); 447 device.Set("port", param.Get("port", 0));
448 device.Set("pad", param.Get("pad", 0)); 448 device.Set("pad", param.Get("pad", 0));
449 devices.push_back(device);
450 } 449 }
451 450
452 for (const auto& param : stick_params) { 451 for (const auto& param : stick_params) {
@@ -457,7 +456,7 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
457 continue; 456 continue;
458 } 457 }
459 const auto devices_it = std::find_if( 458 const auto devices_it = std::find_if(
460 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { 459 devices.begin(), devices.end(), [&param](const Common::ParamPackage& param_) {
461 return param.Get("engine", "") == param_.Get("engine", "") && 460 return param.Get("engine", "") == param_.Get("engine", "") &&
462 param.Get("guid", "") == param_.Get("guid", "") && 461 param.Get("guid", "") == param_.Get("guid", "") &&
463 param.Get("port", 0) == param_.Get("port", 0) && 462 param.Get("port", 0) == param_.Get("port", 0) &&
@@ -466,12 +465,12 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
466 if (devices_it != devices.end()) { 465 if (devices_it != devices.end()) {
467 continue; 466 continue;
468 } 467 }
469 Common::ParamPackage device{}; 468
469 auto& device = devices.emplace_back();
470 device.Set("engine", param.Get("engine", "")); 470 device.Set("engine", param.Get("engine", ""));
471 device.Set("guid", param.Get("guid", "")); 471 device.Set("guid", param.Get("guid", ""));
472 device.Set("port", param.Get("port", 0)); 472 device.Set("port", param.Get("port", 0));
473 device.Set("pad", param.Get("pad", 0)); 473 device.Set("pad", param.Get("pad", 0));
474 devices.push_back(device);
475 } 474 }
476 return devices; 475 return devices;
477} 476}
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index d004ca56a..fa7a34278 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <mutex> 9#include <mutex>
10#include <unordered_map> 10#include <unordered_map>
11#include <vector>
11 12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/input.h" 14#include "common/input.h"
@@ -243,7 +244,7 @@ public:
243 void RestoreConfig(); 244 void RestoreConfig();
244 245
245 /// Returns a vector of mapped devices from the mapped button and stick parameters 246 /// Returns a vector of mapped devices from the mapped button and stick parameters
246 std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const; 247 std::vector<Common::ParamPackage> GetMappedDevices() const;
247 248
248 // Returns the current mapped button device 249 // Returns the current mapped button device
249 Common::ParamPackage GetButtonParam(std::size_t index) const; 250 Common::ParamPackage GetButtonParam(std::size_t index) const;
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 4149eeced..4cdbf9dc6 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <mutex> 9#include <mutex>
10#include <unordered_map> 10#include <unordered_map>
11#include <vector>
11 12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/input.h" 14#include "common/input.h"
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index 3b6e7baff..87ca65592 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -280,18 +280,19 @@ struct KMemoryInfo {
280 280
281class KMemoryBlock : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> { 281class KMemoryBlock : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> {
282private: 282private:
283 u16 m_device_disable_merge_left_count; 283 u16 m_device_disable_merge_left_count{};
284 u16 m_device_disable_merge_right_count; 284 u16 m_device_disable_merge_right_count{};
285 VAddr m_address; 285 VAddr m_address{};
286 size_t m_num_pages; 286 size_t m_num_pages{};
287 KMemoryState m_memory_state; 287 KMemoryState m_memory_state{KMemoryState::None};
288 u16 m_ipc_lock_count; 288 u16 m_ipc_lock_count{};
289 u16 m_device_use_count; 289 u16 m_device_use_count{};
290 u16 m_ipc_disable_merge_count; 290 u16 m_ipc_disable_merge_count{};
291 KMemoryPermission m_permission; 291 KMemoryPermission m_permission{KMemoryPermission::None};
292 KMemoryPermission m_original_permission; 292 KMemoryPermission m_original_permission{KMemoryPermission::None};
293 KMemoryAttribute m_attribute; 293 KMemoryAttribute m_attribute{KMemoryAttribute::None};
294 KMemoryBlockDisableMergeAttribute m_disable_merge_attribute; 294 KMemoryBlockDisableMergeAttribute m_disable_merge_attribute{
295 KMemoryBlockDisableMergeAttribute::None};
295 296
296public: 297public:
297 static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) { 298 static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) {
@@ -367,12 +368,8 @@ public:
367 368
368 constexpr KMemoryBlock(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p, 369 constexpr KMemoryBlock(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p,
369 KMemoryAttribute attr) 370 KMemoryAttribute attr)
370 : Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock>(), 371 : Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock>(), m_address(addr), m_num_pages(np),
371 m_device_disable_merge_left_count(), m_device_disable_merge_right_count(), 372 m_memory_state(ms), m_permission(p), m_attribute(attr) {}
372 m_address(addr), m_num_pages(np), m_memory_state(ms), m_ipc_lock_count(0),
373 m_device_use_count(0), m_ipc_disable_merge_count(), m_permission(p),
374 m_original_permission(KMemoryPermission::None), m_attribute(attr),
375 m_disable_merge_attribute() {}
376 373
377 constexpr void Initialize(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p, 374 constexpr void Initialize(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p,
378 KMemoryAttribute attr) { 375 KMemoryAttribute attr) {
diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h
index 9b5873883..d382722a6 100644
--- a/src/core/hle/kernel/k_memory_block_manager.h
+++ b/src/core/hle/kernel/k_memory_block_manager.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <array>
6#include <functional> 7#include <functional>
7 8
8#include "common/common_funcs.h" 9#include "common/common_funcs.h"
@@ -17,9 +18,9 @@ public:
17 static constexpr size_t MaxBlocks = 2; 18 static constexpr size_t MaxBlocks = 2;
18 19
19private: 20private:
20 KMemoryBlock* m_blocks[MaxBlocks]; 21 std::array<KMemoryBlock*, MaxBlocks> m_blocks{};
21 size_t m_index; 22 size_t m_index{MaxBlocks};
22 KMemoryBlockSlabManager* m_slab_manager; 23 KMemoryBlockSlabManager* m_slab_manager{};
23 24
24private: 25private:
25 Result Initialize(size_t num_blocks) { 26 Result Initialize(size_t num_blocks) {
@@ -41,7 +42,7 @@ private:
41public: 42public:
42 KMemoryBlockManagerUpdateAllocator(Result* out_result, KMemoryBlockSlabManager* sm, 43 KMemoryBlockManagerUpdateAllocator(Result* out_result, KMemoryBlockSlabManager* sm,
43 size_t num_blocks = MaxBlocks) 44 size_t num_blocks = MaxBlocks)
44 : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) { 45 : m_slab_manager(sm) {
45 *out_result = this->Initialize(num_blocks); 46 *out_result = this->Initialize(num_blocks);
46 } 47 }
47 48
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index c4bf306e8..bd33571da 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -225,8 +225,8 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
225 ON_RESULT_FAILURE { 225 ON_RESULT_FAILURE {
226 for (const auto& it : out->Nodes()) { 226 for (const auto& it : out->Nodes()) {
227 auto& manager = this->GetManager(it.GetAddress()); 227 auto& manager = this->GetManager(it.GetAddress());
228 const size_t node_num_pages = 228 const size_t node_num_pages = std::min<u64>(
229 std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); 229 it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
230 manager.Free(it.GetAddress(), node_num_pages); 230 manager.Free(it.GetAddress(), node_num_pages);
231 } 231 }
232 out->Finalize(); 232 out->Finalize();
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h
index 5620c3660..a96c55a3e 100644
--- a/src/core/hle/kernel/k_shared_memory.h
+++ b/src/core/hle/kernel/k_shared_memory.h
@@ -74,7 +74,7 @@ public:
74 static void PostDestroy([[maybe_unused]] uintptr_t arg) {} 74 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
75 75
76private: 76private:
77 Core::DeviceMemory* device_memory; 77 Core::DeviceMemory* device_memory{};
78 KProcess* owner_process{}; 78 KProcess* owner_process{};
79 KPageGroup page_list; 79 KPageGroup page_list;
80 Svc::MemoryPermission owner_permission{}; 80 Svc::MemoryPermission owner_permission{};
diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h
index a8c77a7d4..68469b041 100644
--- a/src/core/hle/kernel/k_slab_heap.h
+++ b/src/core/hle/kernel/k_slab_heap.h
@@ -6,6 +6,7 @@
6#include <atomic> 6#include <atomic>
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/atomic_ops.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"
11#include "common/spin_lock.h" 12#include "common/spin_lock.h"
@@ -82,16 +83,13 @@ private:
82 83
83private: 84private:
84 void UpdatePeakImpl(uintptr_t obj) { 85 void UpdatePeakImpl(uintptr_t obj) {
85 static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
86 std::atomic_ref<uintptr_t> peak_ref(m_peak);
87
88 const uintptr_t alloc_peak = obj + this->GetObjectSize(); 86 const uintptr_t alloc_peak = obj + this->GetObjectSize();
89 uintptr_t cur_peak = m_peak; 87 uintptr_t cur_peak = m_peak;
90 do { 88 do {
91 if (alloc_peak <= cur_peak) { 89 if (alloc_peak <= cur_peak) {
92 break; 90 break;
93 } 91 }
94 } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak)); 92 } while (!Common::AtomicCompareAndSwap(&m_peak, alloc_peak, cur_peak, cur_peak));
95 } 93 }
96 94
97public: 95public:
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index f38c92bff..dc52b4ed3 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -784,8 +784,8 @@ private:
784 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 784 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
785 VAddr mutex_wait_address_for_debugging{}; 785 VAddr mutex_wait_address_for_debugging{};
786 ThreadWaitReasonForDebugging wait_reason_for_debugging{}; 786 ThreadWaitReasonForDebugging wait_reason_for_debugging{};
787 uintptr_t argument; 787 uintptr_t argument{};
788 VAddr stack_top; 788 VAddr stack_top{};
789 789
790public: 790public:
791 using ConditionVariableThreadTreeType = ConditionVariableThreadTree; 791 using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h
index 5d466ace7..fe0cff084 100644
--- a/src/core/hle/kernel/k_thread_local_page.h
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/intrusive_red_black_tree.h" 12#include "common/intrusive_red_black_tree.h"
13#include "common/polyfill_ranges.h"
13#include "core/hle/kernel/memory_types.h" 14#include "core/hle/kernel/memory_types.h"
14#include "core/hle/kernel/slab_helpers.h" 15#include "core/hle/kernel/slab_helpers.h"
15#include "core/hle/result.h" 16#include "core/hle/result.h"
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index b77723503..288f97df5 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -891,7 +891,7 @@ struct KernelCore::Impl {
891 Common::ThreadWorker service_threads_manager; 891 Common::ThreadWorker service_threads_manager;
892 Common::Barrier service_thread_barrier; 892 Common::Barrier service_thread_barrier;
893 893
894 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; 894 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads{};
895 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; 895 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
896 896
897 bool is_multicore{}; 897 bool is_multicore{};
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 2fc8d4be2..fb2ba4c6b 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -85,7 +85,7 @@ private:
85 std::mutex guard; 85 std::mutex guard;
86 std::condition_variable on_interrupt; 86 std::condition_variable on_interrupt;
87 std::unique_ptr<Core::ARM_Interface> arm_interface; 87 std::unique_ptr<Core::ARM_Interface> arm_interface;
88 bool is_interrupted; 88 bool is_interrupted{};
89}; 89};
90 90
91} // namespace Kernel 91} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index e6e41ac34..e72c3d35d 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -7,6 +7,7 @@
7#include <thread> 7#include <thread>
8#include <vector> 8#include <vector>
9 9
10#include "common/polyfill_thread.h"
10#include "common/scope_exit.h" 11#include "common/scope_exit.h"
11#include "common/thread.h" 12#include "common/thread.h"
12#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
@@ -35,14 +36,14 @@ public:
35 36
36private: 37private:
37 KernelCore& kernel; 38 KernelCore& kernel;
38
39 std::jthread m_host_thread;
40 std::mutex m_session_mutex;
41 std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
42 KEvent* m_wakeup_event;
43 KThread* m_thread;
44 std::atomic<bool> m_shutdown_requested;
45 const std::string m_service_name; 39 const std::string m_service_name;
40
41 std::jthread m_host_thread{};
42 std::mutex m_session_mutex{};
43 std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{};
44 KEvent* m_wakeup_event{};
45 KThread* m_thread{};
46 std::atomic<bool> m_shutdown_requested{};
46}; 47};
47 48
48void ServiceThread::Impl::WaitAndProcessImpl() { 49void ServiceThread::Impl::WaitAndProcessImpl() {
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 3730937fe..1ea8c7fbc 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -82,7 +82,7 @@ void SvcWrap64(Core::System& system) {
82} 82}
83 83
84// Used by ControlCodeMemory 84// Used by ControlCodeMemory
85template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> 85template <Result func(Core::System&, Handle, u32, VAddr, size_t, Svc::MemoryPermission)>
86void SvcWrap64(Core::System& system) { 86void SvcWrap64(Core::System& system) {
87 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), 87 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
88 static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3), 88 static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
@@ -327,7 +327,7 @@ void SvcWrap64(Core::System& system) {
327} 327}
328 328
329// Used by CreateCodeMemory 329// Used by CreateCodeMemory
330template <Result func(Core::System&, Handle*, u64, u64)> 330template <Result func(Core::System&, Handle*, VAddr, size_t)>
331void SvcWrap64(Core::System& system) { 331void SvcWrap64(Core::System& system) {
332 u32 param_1 = 0; 332 u32 param_1 = 0;
333 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw; 333 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 85a3f0802..6d1084fd1 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -7,6 +7,7 @@
7#include "common/fs/file.h" 7#include "common/fs/file.h"
8#include "common/fs/path_util.h" 8#include "common/fs/path_util.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/polyfill_ranges.h"
10#include "common/string_util.h" 11#include "common/string_util.h"
11#include "common/swap.h" 12#include "common/swap.h"
12#include "core/constants.h" 13#include "core/constants.h"
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 481e0d141..97f7c6688 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -9,6 +9,7 @@
9#include "common/fs/file.h" 9#include "common/fs/file.h"
10#include "common/fs/fs.h" 10#include "common/fs/fs.h"
11#include "common/fs/path_util.h" 11#include "common/fs/path_util.h"
12#include "common/polyfill_ranges.h"
12#include "common/settings.h" 13#include "common/settings.h"
13#include "core/hle/service/acc/profile_manager.h" 14#include "core/hle/service/acc/profile_manager.h"
14 15
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 8ea7fd760..22999c942 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1125,7 +1125,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
1125 1125
1126 const u64 offset{rp.Pop<u64>()}; 1126 const u64 offset{rp.Pop<u64>()};
1127 const std::vector<u8> data{ctx.ReadBuffer()}; 1127 const std::vector<u8> data{ctx.ReadBuffer()};
1128 const std::size_t size{std::min(data.size(), backing.GetSize() - offset)}; 1128 const std::size_t size{std::min<u64>(data.size(), backing.GetSize() - offset)};
1129 1129
1130 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); 1130 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
1131 1131
@@ -1149,7 +1149,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
1149 IPC::RequestParser rp{ctx}; 1149 IPC::RequestParser rp{ctx};
1150 1150
1151 const u64 offset{rp.Pop<u64>()}; 1151 const u64 offset{rp.Pop<u64>()};
1152 const std::size_t size{std::min(ctx.GetWriteBufferSize(), backing.GetSize() - offset)}; 1152 const std::size_t size{std::min<u64>(ctx.GetWriteBufferSize(), backing.GetSize() - offset)};
1153 1153
1154 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); 1154 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
1155 1155
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 053e8f9dd..26dec7147 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -203,9 +203,8 @@ private:
203}; 203};
204 204
205AudInU::AudInU(Core::System& system_) 205AudInU::AudInU(Core::System& system_)
206 : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew}, 206 : ServiceFramework{system_, "audin:u"}, service_context{system_, "AudInU"},
207 service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( 207 impl{std::make_unique<AudioCore::AudioIn::Manager>(system_)} {
208 system_)} {
209 // clang-format off 208 // clang-format off
210 static const FunctionInfo functions[] = { 209 static const FunctionInfo functions[] = {
211 {0, &AudInU::ListAudioIns, "ListAudioIns"}, 210 {0, &AudInU::ListAudioIns, "ListAudioIns"},
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 29751f075..991e30ba1 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -26,9 +26,8 @@ public:
26 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, 26 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
27 size_t session_id, const std::string& device_name, 27 size_t session_id, const std::string& device_name,
28 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) 28 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
29 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, 29 : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
30 service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( 30 event{service_context.CreateEvent("AudioOutEvent")},
31 "AudioOutEvent")},
32 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { 31 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
33 32
34 // clang-format off 33 // clang-format off
@@ -221,9 +220,8 @@ private:
221}; 220};
222 221
223AudOutU::AudOutU(Core::System& system_) 222AudOutU::AudOutU(Core::System& system_)
224 : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew}, 223 : ServiceFramework{system_, "audout:u"}, service_context{system_, "AudOutU"},
225 service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>( 224 impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} {
226 system_)} {
227 // clang-format off 225 // clang-format off
228 static const FunctionInfo functions[] = { 226 static const FunctionInfo functions[] = {
229 {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, 227 {0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 034ee273f..ead16c321 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -14,6 +14,7 @@
14#include "common/bit_util.h" 14#include "common/bit_util.h"
15#include "common/common_funcs.h" 15#include "common/common_funcs.h"
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/polyfill_ranges.h"
17#include "common/string_util.h" 18#include "common/string_util.h"
18#include "core/core.h" 19#include "core/core.h"
19#include "core/hle/ipc_helpers.h" 20#include "core/hle/ipc_helpers.h"
@@ -34,10 +35,9 @@ public:
34 AudioCore::AudioRendererParameterInternal& params, 35 AudioCore::AudioRendererParameterInternal& params,
35 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, 36 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
36 u32 process_handle, u64 applet_resource_user_id, s32 session_id) 37 u32 process_handle, u64 applet_resource_user_id, s32 session_id)
37 : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, 38 : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"},
38 service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent( 39 rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_},
39 "IAudioRendererEvent")}, 40 impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
40 manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
41 // clang-format off 41 // clang-format off
42 static const FunctionInfo functions[] = { 42 static const FunctionInfo functions[] = {
43 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, 43 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
@@ -242,10 +242,8 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> {
242public: 242public:
243 explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, 243 explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
244 u32 device_num) 244 u32 device_num)
245 : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew}, 245 : ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"},
246 service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>( 246 impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)},
247 system_, applet_resource_user_id,
248 revision)},
249 event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { 247 event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
250 static const FunctionInfo functions[] = { 248 static const FunctionInfo functions[] = {
251 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, 249 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
@@ -420,7 +418,7 @@ private:
420}; 418};
421 419
422AudRenU::AudRenU(Core::System& system_) 420AudRenU::AudRenU(Core::System& system_)
423 : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew}, 421 : ServiceFramework{system_, "audren:u"},
424 service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { 422 service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
425 // clang-format off 423 // clang-format off
426 static const FunctionInfo functions[] = { 424 static const FunctionInfo functions[] = {
diff --git a/src/core/hle/service/nfc/mifare_user.cpp b/src/core/hle/service/nfc/mifare_user.cpp
new file mode 100644
index 000000000..51523a3ae
--- /dev/null
+++ b/src/core/hle/service/nfc/mifare_user.cpp
@@ -0,0 +1,400 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/hid/hid_types.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/k_event.h"
9#include "core/hle/service/nfc/mifare_user.h"
10#include "core/hle/service/nfc/nfc_device.h"
11#include "core/hle/service/nfc/nfc_result.h"
12
13namespace Service::NFC {
14
15MFIUser::MFIUser(Core::System& system_)
16 : ServiceFramework{system_, "NFC::MFIUser"}, service_context{system_, service_name} {
17 static const FunctionInfo functions[] = {
18 {0, &MFIUser::Initialize, "Initialize"},
19 {1, &MFIUser::Finalize, "Finalize"},
20 {2, &MFIUser::ListDevices, "ListDevices"},
21 {3, &MFIUser::StartDetection, "StartDetection"},
22 {4, &MFIUser::StopDetection, "StopDetection"},
23 {5, &MFIUser::Read, "Read"},
24 {6, &MFIUser::Write, "Write"},
25 {7, &MFIUser::GetTagInfo, "GetTagInfo"},
26 {8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"},
27 {9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"},
28 {10, &MFIUser::GetState, "GetState"},
29 {11, &MFIUser::GetDeviceState, "GetDeviceState"},
30 {12, &MFIUser::GetNpadId, "GetNpadId"},
31 {13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"},
32 };
33 RegisterHandlers(functions);
34
35 availability_change_event = service_context.CreateEvent("MFIUser:AvailabilityChangeEvent");
36
37 for (u32 device_index = 0; device_index < 10; device_index++) {
38 devices[device_index] =
39 std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
40 service_context, availability_change_event);
41 }
42}
43
44MFIUser ::~MFIUser() {
45 availability_change_event->Close();
46}
47
48void MFIUser::Initialize(Kernel::HLERequestContext& ctx) {
49 LOG_INFO(Service_NFC, "called");
50
51 state = State::Initialized;
52
53 for (auto& device : devices) {
54 device->Initialize();
55 }
56
57 IPC::ResponseBuilder rb{ctx, 2, 0};
58 rb.Push(ResultSuccess);
59}
60
61void MFIUser::Finalize(Kernel::HLERequestContext& ctx) {
62 LOG_INFO(Service_NFC, "called");
63
64 state = State::NonInitialized;
65
66 for (auto& device : devices) {
67 device->Finalize();
68 }
69
70 IPC::ResponseBuilder rb{ctx, 2};
71 rb.Push(ResultSuccess);
72}
73
74void MFIUser::ListDevices(Kernel::HLERequestContext& ctx) {
75 LOG_DEBUG(Service_NFC, "called");
76
77 if (state == State::NonInitialized) {
78 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(MifareNfcDisabled);
80 return;
81 }
82
83 if (!ctx.CanWriteBuffer()) {
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(MifareInvalidArgument);
86 return;
87 }
88
89 if (ctx.GetWriteBufferSize() == 0) {
90 IPC::ResponseBuilder rb{ctx, 2};
91 rb.Push(MifareInvalidArgument);
92 return;
93 }
94
95 std::vector<u64> nfp_devices;
96 const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
97
98 for (const auto& device : devices) {
99 if (nfp_devices.size() >= max_allowed_devices) {
100 continue;
101 }
102 if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
103 nfp_devices.push_back(device->GetHandle());
104 }
105 }
106
107 if (nfp_devices.empty()) {
108 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(MifareDeviceNotFound);
110 return;
111 }
112
113 ctx.WriteBuffer(nfp_devices);
114
115 IPC::ResponseBuilder rb{ctx, 3};
116 rb.Push(ResultSuccess);
117 rb.Push(static_cast<s32>(nfp_devices.size()));
118}
119
120void MFIUser::StartDetection(Kernel::HLERequestContext& ctx) {
121 IPC::RequestParser rp{ctx};
122 const auto device_handle{rp.Pop<u64>()};
123 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
124
125 if (state == State::NonInitialized) {
126 IPC::ResponseBuilder rb{ctx, 2};
127 rb.Push(MifareNfcDisabled);
128 return;
129 }
130
131 auto device = GetNfcDevice(device_handle);
132
133 if (!device.has_value()) {
134 IPC::ResponseBuilder rb{ctx, 2};
135 rb.Push(MifareDeviceNotFound);
136 return;
137 }
138
139 const auto result = device.value()->StartDetection(NFP::TagProtocol::All);
140 IPC::ResponseBuilder rb{ctx, 2};
141 rb.Push(result);
142}
143
144void MFIUser::StopDetection(Kernel::HLERequestContext& ctx) {
145 IPC::RequestParser rp{ctx};
146 const auto device_handle{rp.Pop<u64>()};
147 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
148
149 if (state == State::NonInitialized) {
150 IPC::ResponseBuilder rb{ctx, 2};
151 rb.Push(MifareNfcDisabled);
152 return;
153 }
154
155 auto device = GetNfcDevice(device_handle);
156
157 if (!device.has_value()) {
158 IPC::ResponseBuilder rb{ctx, 2};
159 rb.Push(MifareDeviceNotFound);
160 return;
161 }
162
163 const auto result = device.value()->StopDetection();
164 IPC::ResponseBuilder rb{ctx, 2};
165 rb.Push(result);
166}
167
168void MFIUser::Read(Kernel::HLERequestContext& ctx) {
169 IPC::RequestParser rp{ctx};
170 const auto device_handle{rp.Pop<u64>()};
171 const auto buffer{ctx.ReadBuffer()};
172 const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareReadBlockParameter>()};
173 std::vector<NFP::MifareReadBlockParameter> read_commands(number_of_commands);
174
175 memcpy(read_commands.data(), buffer.data(),
176 number_of_commands * sizeof(NFP::MifareReadBlockParameter));
177
178 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
179 device_handle, number_of_commands);
180
181 if (state == State::NonInitialized) {
182 IPC::ResponseBuilder rb{ctx, 2};
183 rb.Push(MifareNfcDisabled);
184 return;
185 }
186
187 auto device = GetNfcDevice(device_handle);
188
189 if (!device.has_value()) {
190 IPC::ResponseBuilder rb{ctx, 2};
191 rb.Push(MifareDeviceNotFound);
192 return;
193 }
194
195 Result result = ResultSuccess;
196 std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
197 for (std::size_t i = 0; i < number_of_commands; i++) {
198 result = device.value()->MifareRead(read_commands[i], out_data[i]);
199 if (result.IsError()) {
200 break;
201 }
202 }
203
204 ctx.WriteBuffer(out_data);
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(result);
207}
208
209void MFIUser::Write(Kernel::HLERequestContext& ctx) {
210 IPC::RequestParser rp{ctx};
211 const auto device_handle{rp.Pop<u64>()};
212 const auto buffer{ctx.ReadBuffer()};
213 const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareWriteBlockParameter>()};
214 std::vector<NFP::MifareWriteBlockParameter> write_commands(number_of_commands);
215
216 memcpy(write_commands.data(), buffer.data(),
217 number_of_commands * sizeof(NFP::MifareWriteBlockParameter));
218
219 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
220 device_handle, number_of_commands);
221
222 if (state == State::NonInitialized) {
223 IPC::ResponseBuilder rb{ctx, 2};
224 rb.Push(MifareNfcDisabled);
225 return;
226 }
227
228 auto device = GetNfcDevice(device_handle);
229
230 if (!device.has_value()) {
231 IPC::ResponseBuilder rb{ctx, 2};
232 rb.Push(MifareDeviceNotFound);
233 return;
234 }
235
236 Result result = ResultSuccess;
237 std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
238 for (std::size_t i = 0; i < number_of_commands; i++) {
239 result = device.value()->MifareWrite(write_commands[i]);
240 if (result.IsError()) {
241 break;
242 }
243 }
244
245 if (result.IsSuccess()) {
246 result = device.value()->Flush();
247 }
248
249 IPC::ResponseBuilder rb{ctx, 2};
250 rb.Push(result);
251}
252
253void MFIUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
254 IPC::RequestParser rp{ctx};
255 const auto device_handle{rp.Pop<u64>()};
256 LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
257
258 if (state == State::NonInitialized) {
259 IPC::ResponseBuilder rb{ctx, 2};
260 rb.Push(MifareNfcDisabled);
261 return;
262 }
263
264 auto device = GetNfcDevice(device_handle);
265
266 if (!device.has_value()) {
267 IPC::ResponseBuilder rb{ctx, 2};
268 rb.Push(MifareDeviceNotFound);
269 return;
270 }
271
272 NFP::TagInfo tag_info{};
273 const auto result = device.value()->GetTagInfo(tag_info, true);
274 ctx.WriteBuffer(tag_info);
275 IPC::ResponseBuilder rb{ctx, 2};
276 rb.Push(result);
277}
278
279void MFIUser::GetActivateEventHandle(Kernel::HLERequestContext& ctx) {
280 IPC::RequestParser rp{ctx};
281 const auto device_handle{rp.Pop<u64>()};
282 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
283
284 if (state == State::NonInitialized) {
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(MifareNfcDisabled);
287 return;
288 }
289
290 auto device = GetNfcDevice(device_handle);
291
292 if (!device.has_value()) {
293 IPC::ResponseBuilder rb{ctx, 2};
294 rb.Push(MifareDeviceNotFound);
295 return;
296 }
297
298 IPC::ResponseBuilder rb{ctx, 2, 1};
299 rb.Push(ResultSuccess);
300 rb.PushCopyObjects(device.value()->GetActivateEvent());
301}
302
303void MFIUser::GetDeactivateEventHandle(Kernel::HLERequestContext& ctx) {
304 IPC::RequestParser rp{ctx};
305 const auto device_handle{rp.Pop<u64>()};
306 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
307
308 if (state == State::NonInitialized) {
309 IPC::ResponseBuilder rb{ctx, 2};
310 rb.Push(MifareNfcDisabled);
311 return;
312 }
313
314 auto device = GetNfcDevice(device_handle);
315
316 if (!device.has_value()) {
317 IPC::ResponseBuilder rb{ctx, 2};
318 rb.Push(MifareDeviceNotFound);
319 return;
320 }
321
322 IPC::ResponseBuilder rb{ctx, 2, 1};
323 rb.Push(ResultSuccess);
324 rb.PushCopyObjects(device.value()->GetDeactivateEvent());
325}
326
327void MFIUser::GetState(Kernel::HLERequestContext& ctx) {
328 LOG_DEBUG(Service_NFC, "called");
329
330 IPC::ResponseBuilder rb{ctx, 3};
331 rb.Push(ResultSuccess);
332 rb.PushEnum(state);
333}
334
335void MFIUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
336 IPC::RequestParser rp{ctx};
337 const auto device_handle{rp.Pop<u64>()};
338 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
339
340 auto device = GetNfcDevice(device_handle);
341
342 if (!device.has_value()) {
343 IPC::ResponseBuilder rb{ctx, 2};
344 rb.Push(MifareDeviceNotFound);
345 return;
346 }
347
348 IPC::ResponseBuilder rb{ctx, 3};
349 rb.Push(ResultSuccess);
350 rb.PushEnum(device.value()->GetCurrentState());
351}
352
353void MFIUser::GetNpadId(Kernel::HLERequestContext& ctx) {
354 IPC::RequestParser rp{ctx};
355 const auto device_handle{rp.Pop<u64>()};
356 LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
357
358 if (state == State::NonInitialized) {
359 IPC::ResponseBuilder rb{ctx, 2};
360 rb.Push(MifareNfcDisabled);
361 return;
362 }
363
364 auto device = GetNfcDevice(device_handle);
365
366 if (!device.has_value()) {
367 IPC::ResponseBuilder rb{ctx, 2};
368 rb.Push(MifareDeviceNotFound);
369 return;
370 }
371
372 IPC::ResponseBuilder rb{ctx, 3};
373 rb.Push(ResultSuccess);
374 rb.PushEnum(device.value()->GetNpadId());
375}
376
377void MFIUser::GetAvailabilityChangeEventHandle(Kernel::HLERequestContext& ctx) {
378 LOG_INFO(Service_NFC, "called");
379
380 if (state == State::NonInitialized) {
381 IPC::ResponseBuilder rb{ctx, 2};
382 rb.Push(MifareNfcDisabled);
383 return;
384 }
385
386 IPC::ResponseBuilder rb{ctx, 2, 1};
387 rb.Push(ResultSuccess);
388 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
389}
390
391std::optional<std::shared_ptr<NfcDevice>> MFIUser::GetNfcDevice(u64 handle) {
392 for (auto& device : devices) {
393 if (device->GetHandle() == handle) {
394 return device;
395 }
396 }
397 return std::nullopt;
398}
399
400} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/mifare_user.h b/src/core/hle/service/nfc/mifare_user.h
new file mode 100644
index 000000000..0e0638cb6
--- /dev/null
+++ b/src/core/hle/service/nfc/mifare_user.h
@@ -0,0 +1,52 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <optional>
9
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/service.h"
12
13namespace Service::NFC {
14class NfcDevice;
15
16class MFIUser final : public ServiceFramework<MFIUser> {
17public:
18 explicit MFIUser(Core::System& system_);
19 ~MFIUser();
20
21private:
22 enum class State : u32 {
23 NonInitialized,
24 Initialized,
25 };
26
27 void Initialize(Kernel::HLERequestContext& ctx);
28 void Finalize(Kernel::HLERequestContext& ctx);
29 void ListDevices(Kernel::HLERequestContext& ctx);
30 void StartDetection(Kernel::HLERequestContext& ctx);
31 void StopDetection(Kernel::HLERequestContext& ctx);
32 void Read(Kernel::HLERequestContext& ctx);
33 void Write(Kernel::HLERequestContext& ctx);
34 void GetTagInfo(Kernel::HLERequestContext& ctx);
35 void GetActivateEventHandle(Kernel::HLERequestContext& ctx);
36 void GetDeactivateEventHandle(Kernel::HLERequestContext& ctx);
37 void GetState(Kernel::HLERequestContext& ctx);
38 void GetDeviceState(Kernel::HLERequestContext& ctx);
39 void GetNpadId(Kernel::HLERequestContext& ctx);
40 void GetAvailabilityChangeEventHandle(Kernel::HLERequestContext& ctx);
41
42 std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
43
44 KernelHelpers::ServiceContext service_context;
45
46 std::array<std::shared_ptr<NfcDevice>, 10> devices{};
47
48 State state{State::NonInitialized};
49 Kernel::KEvent* availability_change_event;
50};
51
52} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 2f4bacb3b..b17b18ab9 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -6,6 +6,7 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/service/nfc/mifare_user.h"
9#include "core/hle/service/nfc/nfc.h" 10#include "core/hle/service/nfc/nfc.h"
10#include "core/hle/service/nfc/nfc_user.h" 11#include "core/hle/service/nfc/nfc_user.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
@@ -50,32 +51,6 @@ private:
50 } 51 }
51}; 52};
52 53
53class MFIUser final : public ServiceFramework<MFIUser> {
54public:
55 explicit MFIUser(Core::System& system_) : ServiceFramework{system_, "NFC::MFIUser"} {
56 // clang-format off
57 static const FunctionInfo functions[] = {
58 {0, nullptr, "Initialize"},
59 {1, nullptr, "Finalize"},
60 {2, nullptr, "ListDevices"},
61 {3, nullptr, "StartDetection"},
62 {4, nullptr, "StopDetection"},
63 {5, nullptr, "Read"},
64 {6, nullptr, "Write"},
65 {7, nullptr, "GetTagInfo"},
66 {8, nullptr, "GetActivateEventHandle"},
67 {9, nullptr, "GetDeactivateEventHandle"},
68 {10, nullptr, "GetState"},
69 {11, nullptr, "GetDeviceState"},
70 {12, nullptr, "GetNpadId"},
71 {13, nullptr, "GetAvailabilityChangeEventHandle"},
72 };
73 // clang-format on
74
75 RegisterHandlers(functions);
76 }
77};
78
79class NFC_MF_U final : public ServiceFramework<NFC_MF_U> { 54class NFC_MF_U final : public ServiceFramework<NFC_MF_U> {
80public: 55public:
81 explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} { 56 explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} {
diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp
index 4d514cf5f..78578f723 100644
--- a/src/core/hle/service/nfc/nfc_device.cpp
+++ b/src/core/hle/service/nfc/nfc_device.cpp
@@ -77,11 +77,13 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
77 return false; 77 return false;
78 } 78 }
79 79
80 if (data.size() != sizeof(NFP::EncryptedNTAG215File)) { 80 if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
81 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size()); 81 LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
82 return false; 82 return false;
83 } 83 }
84 84
85 tag_data.resize(data.size());
86 memcpy(tag_data.data(), data.data(), data.size());
85 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); 87 memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
86 88
87 device_state = NFP::DeviceState::TagFound; 89 device_state = NFP::DeviceState::TagFound;
@@ -121,7 +123,7 @@ void NfcDevice::Finalize() {
121 device_state = NFP::DeviceState::Unavailable; 123 device_state = NFP::DeviceState::Unavailable;
122} 124}
123 125
124Result NfcDevice::StartDetection(s32 protocol_) { 126Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
125 if (device_state != NFP::DeviceState::Initialized && 127 if (device_state != NFP::DeviceState::Initialized &&
126 device_state != NFP::DeviceState::TagRemoved) { 128 device_state != NFP::DeviceState::TagRemoved) {
127 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 129 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
@@ -134,7 +136,7 @@ Result NfcDevice::StartDetection(s32 protocol_) {
134 } 136 }
135 137
136 device_state = NFP::DeviceState::SearchingForTag; 138 device_state = NFP::DeviceState::SearchingForTag;
137 protocol = protocol_; 139 allowed_protocols = allowed_protocol;
138 return ResultSuccess; 140 return ResultSuccess;
139} 141}
140 142
@@ -160,7 +162,7 @@ Result NfcDevice::StopDetection() {
160 return WrongDeviceState; 162 return WrongDeviceState;
161} 163}
162 164
163Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const { 165Result NfcDevice::Flush() {
164 if (device_state != NFP::DeviceState::TagFound && 166 if (device_state != NFP::DeviceState::TagFound &&
165 device_state != NFP::DeviceState::TagMounted) { 167 device_state != NFP::DeviceState::TagMounted) {
166 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); 168 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
@@ -170,6 +172,34 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
170 return WrongDeviceState; 172 return WrongDeviceState;
171 } 173 }
172 174
175 if (!npad_device->WriteNfc(tag_data)) {
176 LOG_ERROR(Service_NFP, "Error writing to file");
177 return MifareReadError;
178 }
179
180 return ResultSuccess;
181}
182
183Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
184 if (device_state != NFP::DeviceState::TagFound &&
185 device_state != NFP::DeviceState::TagMounted) {
186 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
187 if (device_state == NFP::DeviceState::TagRemoved) {
188 return TagRemoved;
189 }
190 return WrongDeviceState;
191 }
192
193 if (is_mifare) {
194 tag_info = {
195 .uuid = encrypted_tag_data.uuid.uid,
196 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
197 .protocol = NFP::TagProtocol::TypeA,
198 .tag_type = NFP::TagType::Type4,
199 };
200 return ResultSuccess;
201 }
202
173 // Protocol and tag type may change here 203 // Protocol and tag type may change here
174 tag_info = { 204 tag_info = {
175 .uuid = encrypted_tag_data.uuid.uid, 205 .uuid = encrypted_tag_data.uuid.uid,
@@ -181,6 +211,52 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
181 return ResultSuccess; 211 return ResultSuccess;
182} 212}
183 213
214Result NfcDevice::MifareRead(const NFP::MifareReadBlockParameter& parameter,
215 NFP::MifareReadBlockData& read_block_data) {
216 const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
217 read_block_data.sector_number = parameter.sector_number;
218
219 if (device_state != NFP::DeviceState::TagFound &&
220 device_state != NFP::DeviceState::TagMounted) {
221 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
222 if (device_state == NFP::DeviceState::TagRemoved) {
223 return TagRemoved;
224 }
225 return WrongDeviceState;
226 }
227
228 if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
229 return MifareReadError;
230 }
231
232 // TODO: Use parameter.sector_key to read encrypted data
233 memcpy(read_block_data.data.data(), tag_data.data() + sector_index, sizeof(NFP::DataBlock));
234
235 return ResultSuccess;
236}
237
238Result NfcDevice::MifareWrite(const NFP::MifareWriteBlockParameter& parameter) {
239 const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
240
241 if (device_state != NFP::DeviceState::TagFound &&
242 device_state != NFP::DeviceState::TagMounted) {
243 LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
244 if (device_state == NFP::DeviceState::TagRemoved) {
245 return TagRemoved;
246 }
247 return WrongDeviceState;
248 }
249
250 if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
251 return MifareReadError;
252 }
253
254 // TODO: Use parameter.sector_key to encrypt the data
255 memcpy(tag_data.data() + sector_index, parameter.data.data(), sizeof(NFP::DataBlock));
256
257 return ResultSuccess;
258}
259
184u64 NfcDevice::GetHandle() const { 260u64 NfcDevice::GetHandle() const {
185 // Generate a handle based of the npad id 261 // Generate a handle based of the npad id
186 return static_cast<u64>(npad_id); 262 return static_cast<u64>(npad_id);
diff --git a/src/core/hle/service/nfc/nfc_device.h b/src/core/hle/service/nfc/nfc_device.h
index fa1348f1a..a6e114d36 100644
--- a/src/core/hle/service/nfc/nfc_device.h
+++ b/src/core/hle/service/nfc/nfc_device.h
@@ -34,10 +34,16 @@ public:
34 void Initialize(); 34 void Initialize();
35 void Finalize(); 35 void Finalize();
36 36
37 Result StartDetection(s32 protocol_); 37 Result StartDetection(NFP::TagProtocol allowed_protocol);
38 Result StopDetection(); 38 Result StopDetection();
39 Result Flush();
39 40
40 Result GetTagInfo(NFP::TagInfo& tag_info) const; 41 Result GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const;
42
43 Result MifareRead(const NFP::MifareReadBlockParameter& parameter,
44 NFP::MifareReadBlockData& read_block_data);
45
46 Result MifareWrite(const NFP::MifareWriteBlockParameter& parameter);
41 47
42 u64 GetHandle() const; 48 u64 GetHandle() const;
43 NFP::DeviceState GetCurrentState() const; 49 NFP::DeviceState GetCurrentState() const;
@@ -61,10 +67,11 @@ private:
61 Kernel::KEvent* deactivate_event = nullptr; 67 Kernel::KEvent* deactivate_event = nullptr;
62 Kernel::KEvent* availability_change_event = nullptr; 68 Kernel::KEvent* availability_change_event = nullptr;
63 69
64 s32 protocol{}; 70 NFP::TagProtocol allowed_protocols{};
65 NFP::DeviceState device_state{NFP::DeviceState::Unavailable}; 71 NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
66 72
67 NFP::EncryptedNTAG215File encrypted_tag_data{}; 73 NFP::EncryptedNTAG215File encrypted_tag_data{};
74 std::vector<u8> tag_data{};
68}; 75};
69 76
70} // namespace Service::NFC 77} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h
index 537dc15f4..146b8ba61 100644
--- a/src/core/hle/service/nfc/nfc_result.h
+++ b/src/core/hle/service/nfc/nfc_result.h
@@ -12,6 +12,12 @@ constexpr Result InvalidArgument(ErrorModule::NFC, 65);
12constexpr Result WrongDeviceState(ErrorModule::NFC, 73); 12constexpr Result WrongDeviceState(ErrorModule::NFC, 73);
13constexpr Result NfcDisabled(ErrorModule::NFC, 80); 13constexpr Result NfcDisabled(ErrorModule::NFC, 80);
14constexpr Result TagRemoved(ErrorModule::NFC, 97); 14constexpr Result TagRemoved(ErrorModule::NFC, 97);
15constexpr Result CorruptedData(ErrorModule::NFC, 144); 15
16constexpr Result MifareDeviceNotFound(ErrorModule::NFCMifare, 64);
17constexpr Result MifareInvalidArgument(ErrorModule::NFCMifare, 65);
18constexpr Result MifareWrongDeviceState(ErrorModule::NFCMifare, 73);
19constexpr Result MifareNfcDisabled(ErrorModule::NFCMifare, 80);
20constexpr Result MifareTagRemoved(ErrorModule::NFCMifare, 97);
21constexpr Result MifareReadError(ErrorModule::NFCMifare, 288);
16 22
17} // namespace Service::NFC 23} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp
index ced2d560b..4615697e2 100644
--- a/src/core/hle/service/nfc/nfc_user.cpp
+++ b/src/core/hle/service/nfc/nfc_user.cpp
@@ -201,7 +201,7 @@ void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
201void IUser::StartDetection(Kernel::HLERequestContext& ctx) { 201void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
202 IPC::RequestParser rp{ctx}; 202 IPC::RequestParser rp{ctx};
203 const auto device_handle{rp.Pop<u64>()}; 203 const auto device_handle{rp.Pop<u64>()};
204 const auto nfp_protocol{rp.Pop<s32>()}; 204 const auto nfp_protocol{rp.PopEnum<NFP::TagProtocol>()};
205 LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); 205 LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
206 206
207 if (state == State::NonInitialized) { 207 if (state == State::NonInitialized) {
@@ -267,7 +267,7 @@ void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
267 } 267 }
268 268
269 NFP::TagInfo tag_info{}; 269 NFP::TagInfo tag_info{};
270 const auto result = device.value()->GetTagInfo(tag_info); 270 const auto result = device.value()->GetTagInfo(tag_info, false);
271 ctx.WriteBuffer(tag_info); 271 ctx.WriteBuffer(tag_info);
272 IPC::ResponseBuilder rb{ctx, 2}; 272 IPC::ResponseBuilder rb{ctx, 2};
273 rb.Push(result); 273 rb.Push(result);
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index 69858096a..fc228c2b2 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -106,11 +106,24 @@ enum class CabinetMode : u8 {
106 StartFormatter, 106 StartFormatter,
107}; 107};
108 108
109enum class MifareCmd : u8 {
110 AuthA = 0x60,
111 AuthB = 0x61,
112 Read = 0x30,
113 Write = 0xA0,
114 Transfer = 0xB0,
115 Decrement = 0xC0,
116 Increment = 0xC1,
117 Store = 0xC2
118};
119
109using UniqueSerialNumber = std::array<u8, 7>; 120using UniqueSerialNumber = std::array<u8, 7>;
110using LockBytes = std::array<u8, 2>; 121using LockBytes = std::array<u8, 2>;
111using HashData = std::array<u8, 0x20>; 122using HashData = std::array<u8, 0x20>;
112using ApplicationArea = std::array<u8, 0xD8>; 123using ApplicationArea = std::array<u8, 0xD8>;
113using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; 124using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
125using DataBlock = std::array<u8, 0x10>;
126using KeyData = std::array<u8, 0x6>;
114 127
115struct TagUuid { 128struct TagUuid {
116 UniqueSerialNumber uid; 129 UniqueSerialNumber uid;
@@ -323,4 +336,37 @@ struct RegisterInfo {
323}; 336};
324static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); 337static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
325 338
339struct SectorKey {
340 MifareCmd command;
341 u8 unknown; // Usually 1
342 INSERT_PADDING_BYTES(0x6);
343 KeyData sector_key;
344 INSERT_PADDING_BYTES(0x2);
345};
346static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
347
348struct MifareReadBlockParameter {
349 u8 sector_number;
350 INSERT_PADDING_BYTES(0x7);
351 SectorKey sector_key;
352};
353static_assert(sizeof(MifareReadBlockParameter) == 0x18,
354 "MifareReadBlockParameter is an invalid size");
355
356struct MifareReadBlockData {
357 DataBlock data;
358 u8 sector_number;
359 INSERT_PADDING_BYTES(0x7);
360};
361static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
362
363struct MifareWriteBlockParameter {
364 DataBlock data;
365 u8 sector_number;
366 INSERT_PADDING_BYTES(0x7);
367 SectorKey sector_key;
368};
369static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
370 "MifareWriteBlockParameter is an invalid size");
371
326} // namespace Service::NFP 372} // namespace Service::NFP
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/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 460bef976..9b22397db 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -11,6 +11,7 @@
11#include <vector> 11#include <vector>
12 12
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/polyfill_thread.h"
14#include "core/hle/result.h" 15#include "core/hle/result.h"
15#include "core/hle/service/kernel_helpers.h" 16#include "core/hle/service/kernel_helpers.h"
16 17
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 057fd3661..7b8e510a2 100644
--- a/src/core/internal_network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -9,6 +9,7 @@
9#include "common/bit_cast.h" 9#include "common/bit_cast.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/polyfill_ranges.h"
12#include "common/settings.h" 13#include "common/settings.h"
13#include "common/string_util.h" 14#include "common/string_util.h"
14#include "core/internal_network/network_interface.h" 15#include "core/internal_network/network_interface.h"
diff --git a/src/core/precompiled_headers.h b/src/core/precompiled_headers.h
new file mode 100644
index 000000000..30a31001d
--- /dev/null
+++ b/src/core/precompiled_headers.h
@@ -0,0 +1,11 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <boost/container/flat_map.hpp> // used by service.h which is heavily included
7#include <boost/intrusive/rbtree.hpp> // used by k_auto_object.h which is heavily included
8
9#include "common/common_precompiled_headers.h"
10
11#include "core/hle/kernel/k_process.h"
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 6e21296f6..77821e047 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -38,7 +38,7 @@ std::string GetTimestamp() {
38 38
39using namespace nlohmann; 39using namespace nlohmann;
40 40
41void SaveToFile(json json, const std::filesystem::path& filename) { 41void SaveToFile(const json& json, const std::filesystem::path& filename) {
42 if (!Common::FS::CreateParentDirs(filename)) { 42 if (!Common::FS::CreateParentDirs(filename)) {
43 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", 43 LOG_ERROR(Core, "Failed to create path for '{}' to save report!",
44 Common::FS::PathToUTF8String(filename)); 44 Common::FS::PathToUTF8String(filename));
@@ -81,8 +81,8 @@ json GetReportCommonData(u64 title_id, Result result, const std::string& timesta
81} 81}
82 82
83json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc, 83json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc,
84 u64 pstate, std::array<u64, 31> registers, 84 u64 pstate, const std::array<u64, 31>& registers,
85 std::optional<std::array<u64, 32>> backtrace = {}) { 85 const std::optional<std::array<u64, 32>>& backtrace = {}) {
86 auto out = json{ 86 auto out = json{
87 {"entry_point", fmt::format("{:016X}", entry_point)}, 87 {"entry_point", fmt::format("{:016X}", entry_point)},
88 {"sp", fmt::format("{:016X}", sp)}, 88 {"sp", fmt::format("{:016X}", sp)},
@@ -224,11 +224,11 @@ void Reporter::SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 e
224 224
225 out["processor_state"] = std::move(proc_out); 225 out["processor_state"] = std::move(proc_out);
226 226
227 SaveToFile(std::move(out), GetPath("crash_report", title_id, timestamp)); 227 SaveToFile(out, GetPath("crash_report", title_id, timestamp));
228} 228}
229 229
230void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, 230void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
231 std::optional<std::vector<u8>> resolved_buffer) const { 231 const std::optional<std::vector<u8>>& resolved_buffer) const {
232 if (!IsReportingEnabled()) { 232 if (!IsReportingEnabled()) {
233 return; 233 return;
234 } 234 }
@@ -250,7 +250,7 @@ void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64
250 250
251 out["svc_break"] = std::move(break_out); 251 out["svc_break"] = std::move(break_out);
252 252
253 SaveToFile(std::move(out), GetPath("svc_break_report", title_id, timestamp)); 253 SaveToFile(out, GetPath("svc_break_report", title_id, timestamp));
254} 254}
255 255
256void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, 256void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
@@ -271,13 +271,13 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u
271 271
272 out["function"] = std::move(function_out); 272 out["function"] = std::move(function_out);
273 273
274 SaveToFile(std::move(out), GetPath("unimpl_func_report", title_id, timestamp)); 274 SaveToFile(out, GetPath("unimpl_func_report", title_id, timestamp));
275} 275}
276 276
277void Reporter::SaveUnimplementedAppletReport( 277void Reporter::SaveUnimplementedAppletReport(
278 u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color, 278 u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color,
279 bool startup_sound, u64 system_tick, std::vector<std::vector<u8>> normal_channel, 279 bool startup_sound, u64 system_tick, const std::vector<std::vector<u8>>& normal_channel,
280 std::vector<std::vector<u8>> interactive_channel) const { 280 const std::vector<std::vector<u8>>& interactive_channel) const {
281 if (!IsReportingEnabled()) { 281 if (!IsReportingEnabled()) {
282 return; 282 return;
283 } 283 }
@@ -308,10 +308,11 @@ void Reporter::SaveUnimplementedAppletReport(
308 out["applet_normal_data"] = std::move(normal_out); 308 out["applet_normal_data"] = std::move(normal_out);
309 out["applet_interactive_data"] = std::move(interactive_out); 309 out["applet_interactive_data"] = std::move(interactive_out);
310 310
311 SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp)); 311 SaveToFile(out, GetPath("unimpl_applet_report", title_id, timestamp));
312} 312}
313 313
314void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, 314void Reporter::SavePlayReport(PlayReportType type, u64 title_id,
315 const std::vector<std::vector<u8>>& data,
315 std::optional<u64> process_id, std::optional<u128> user_id) const { 316 std::optional<u64> process_id, std::optional<u128> user_id) const {
316 if (!IsReportingEnabled()) { 317 if (!IsReportingEnabled()) {
317 return; 318 return;
@@ -335,12 +336,12 @@ void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std
335 out["play_report_type"] = fmt::format("{:02}", static_cast<u8>(type)); 336 out["play_report_type"] = fmt::format("{:02}", static_cast<u8>(type));
336 out["play_report_data"] = std::move(data_out); 337 out["play_report_data"] = std::move(data_out);
337 338
338 SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp)); 339 SaveToFile(out, GetPath("play_report", title_id, timestamp));
339} 340}
340 341
341void Reporter::SaveErrorReport(u64 title_id, Result result, 342void Reporter::SaveErrorReport(u64 title_id, Result result,
342 std::optional<std::string> custom_text_main, 343 const std::optional<std::string>& custom_text_main,
343 std::optional<std::string> custom_text_detail) const { 344 const std::optional<std::string>& custom_text_detail) const {
344 if (!IsReportingEnabled()) { 345 if (!IsReportingEnabled()) {
345 return; 346 return;
346 } 347 }
@@ -354,11 +355,11 @@ void Reporter::SaveErrorReport(u64 title_id, Result result,
354 out["backtrace"] = GetBacktraceData(system); 355 out["backtrace"] = GetBacktraceData(system);
355 356
356 out["error_custom_text"] = { 357 out["error_custom_text"] = {
357 {"main", *custom_text_main}, 358 {"main", custom_text_main.value_or("")},
358 {"detail", *custom_text_detail}, 359 {"detail", custom_text_detail.value_or("")},
359 }; 360 };
360 361
361 SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); 362 SaveToFile(out, GetPath("error_report", title_id, timestamp));
362} 363}
363 364
364void Reporter::SaveFSAccessLog(std::string_view log_message) const { 365void Reporter::SaveFSAccessLog(std::string_view log_message) const {
diff --git a/src/core/reporter.h b/src/core/reporter.h
index 68755cbde..9fdb9d6c1 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -36,7 +36,7 @@ public:
36 36
37 // Used by syscall svcBreak 37 // Used by syscall svcBreak
38 void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, 38 void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
39 std::optional<std::vector<u8>> resolved_buffer = {}) const; 39 const std::optional<std::vector<u8>>& resolved_buffer = {}) const;
40 40
41 // Used by HLE service handler 41 // Used by HLE service handler
42 void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, 42 void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
@@ -44,10 +44,10 @@ public:
44 const std::string& service_name) const; 44 const std::string& service_name) const;
45 45
46 // Used by stub applet implementation 46 // Used by stub applet implementation
47 void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, 47 void SaveUnimplementedAppletReport(
48 u32 theme_color, bool startup_sound, u64 system_tick, 48 u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color,
49 std::vector<std::vector<u8>> normal_channel, 49 bool startup_sound, u64 system_tick, const std::vector<std::vector<u8>>& normal_channel,
50 std::vector<std::vector<u8>> interactive_channel) const; 50 const std::vector<std::vector<u8>>& interactive_channel) const;
51 51
52 enum class PlayReportType { 52 enum class PlayReportType {
53 Old, 53 Old,
@@ -56,13 +56,13 @@ public:
56 System, 56 System,
57 }; 57 };
58 58
59 void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, 59 void SavePlayReport(PlayReportType type, u64 title_id, const std::vector<std::vector<u8>>& data,
60 std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; 60 std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
61 61
62 // Used by error applet 62 // Used by error applet
63 void SaveErrorReport(u64 title_id, Result result, 63 void SaveErrorReport(u64 title_id, Result result,
64 std::optional<std::string> custom_text_main = {}, 64 const std::optional<std::string>& custom_text_main = {},
65 std::optional<std::string> custom_text_detail = {}) const; 65 const std::optional<std::string>& custom_text_detail = {}) const;
66 66
67 void SaveFSAccessLog(std::string_view log_message) const; 67 void SaveFSAccessLog(std::string_view log_message) const;
68 68
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index abcf6eb11..8d5f2be2f 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -55,6 +55,8 @@ static const char* TranslateRenderer(Settings::RendererBackend backend) {
55 return "OpenGL"; 55 return "OpenGL";
56 case Settings::RendererBackend::Vulkan: 56 case Settings::RendererBackend::Vulkan:
57 return "Vulkan"; 57 return "Vulkan";
58 case Settings::RendererBackend::Null:
59 return "Null";
58 } 60 }
59 return "Unknown"; 61 return "Unknown";
60} 62}
diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt
index 2d9731f19..5bbe1d4b5 100644
--- a/src/dedicated_room/CMakeLists.txt
+++ b/src/dedicated_room/CMakeLists.txt
@@ -4,6 +4,7 @@
4set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) 4set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
5 5
6add_executable(yuzu-room 6add_executable(yuzu-room
7 precompiled_headers.h
7 yuzu_room.cpp 8 yuzu_room.cpp
8 yuzu_room.rc 9 yuzu_room.rc
9) 10)
@@ -25,3 +26,7 @@ target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
25if(UNIX AND NOT APPLE) 26if(UNIX AND NOT APPLE)
26 install(TARGETS yuzu-room) 27 install(TARGETS yuzu-room)
27endif() 28endif()
29
30if (YUZU_USE_PRECOMPILED_HEADERS)
31 target_precompile_headers(yuzu-room PRIVATE precompiled_headers.h)
32endif()
diff --git a/src/dedicated_room/precompiled_headers.h b/src/dedicated_room/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/dedicated_room/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 193127d0a..7932aaab0 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -34,6 +34,7 @@ add_library(input_common STATIC
34 input_poller.h 34 input_poller.h
35 main.cpp 35 main.cpp
36 main.h 36 main.h
37 precompiled_headers.h
37) 38)
38 39
39if (MSVC) 40if (MSVC)
@@ -55,15 +56,13 @@ if (ENABLE_SDL2)
55 drivers/sdl_driver.cpp 56 drivers/sdl_driver.cpp
56 drivers/sdl_driver.h 57 drivers/sdl_driver.h
57 ) 58 )
58 if (YUZU_USE_EXTERNAL_SDL2) 59 target_link_libraries(input_common PRIVATE SDL2::SDL2)
59 target_link_libraries(input_common PRIVATE SDL2-static)
60 else()
61 target_link_libraries(input_common PRIVATE SDL2)
62 endif()
63 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 60 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
64endif() 61endif()
65 62
66target_link_libraries(input_common PRIVATE usb)
67
68create_target_directory_groups(input_common) 63create_target_directory_groups(input_common)
69target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) 64target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost libusb::usb)
65
66if (YUZU_USE_PRECOMPILED_HEADERS)
67 target_precompile_headers(input_common PRIVATE precompiled_headers.h)
68endif()
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index 7f81767f7..b5270fd0b 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -5,10 +5,10 @@
5 5
6#include <array> 6#include <array>
7#include <memory> 7#include <memory>
8#include <stop_token>
9#include <string> 8#include <string>
10#include <thread> 9#include <thread>
11 10
11#include "common/polyfill_thread.h"
12#include "input_common/input_engine.h" 12#include "input_common/input_engine.h"
13 13
14struct libusb_context; 14struct libusb_context;
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index 98c3157a8..faf9cbdc3 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -1,7 +1,6 @@
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 <stop_token>
5#include <thread> 4#include <thread>
6#include <fmt/format.h> 5#include <fmt/format.h>
7 6
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h
index 286ce1cf6..72073cc23 100644
--- a/src/input_common/drivers/mouse.h
+++ b/src/input_common/drivers/mouse.h
@@ -3,9 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <stop_token>
7#include <thread> 6#include <thread>
8 7
8#include "common/polyfill_thread.h"
9#include "common/vector_math.h" 9#include "common/vector_math.h"
10#include "input_common/input_engine.h" 10#include "input_common/input_engine.h"
11 11
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp
index 21c6ed405..f3ade90da 100644
--- a/src/input_common/drivers/tas_input.cpp
+++ b/src/input_common/drivers/tas_input.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <cstring> 4#include <cstring>
5#include <sstream>
5#include <fmt/format.h> 6#include <fmt/format.h>
6 7
7#include "common/fs/file.h" 8#include "common/fs/file.h"
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 564a188e5..63ffaca67 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -47,20 +47,20 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc(
47 47
48Common::Input::NfcState VirtualAmiibo::WriteNfcData( 48Common::Input::NfcState VirtualAmiibo::WriteNfcData(
49 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { 49 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
50 const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite, 50 const Common::FS::IOFile nfc_file{file_path, Common::FS::FileAccessMode::ReadWrite,
51 Common::FS::FileType::BinaryFile}; 51 Common::FS::FileType::BinaryFile};
52 52
53 if (!amiibo_file.IsOpen()) { 53 if (!nfc_file.IsOpen()) {
54 LOG_ERROR(Core, "Amiibo is already on use"); 54 LOG_ERROR(Core, "Amiibo is already on use");
55 return Common::Input::NfcState::WriteFailed; 55 return Common::Input::NfcState::WriteFailed;
56 } 56 }
57 57
58 if (!amiibo_file.Write(data)) { 58 if (!nfc_file.Write(data)) {
59 LOG_ERROR(Service_NFP, "Error writting to file"); 59 LOG_ERROR(Service_NFP, "Error writting to file");
60 return Common::Input::NfcState::WriteFailed; 60 return Common::Input::NfcState::WriteFailed;
61 } 61 }
62 62
63 amiibo_data = data; 63 nfc_data = data;
64 64
65 return Common::Input::NfcState::Success; 65 return Common::Input::NfcState::Success;
66} 66}
@@ -70,32 +70,44 @@ VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
70} 70}
71 71
72VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { 72VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
73 const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read, 73 const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read,
74 Common::FS::FileType::BinaryFile}; 74 Common::FS::FileType::BinaryFile};
75 75
76 if (state != State::WaitingForAmiibo) { 76 if (state != State::WaitingForAmiibo) {
77 return Info::WrongDeviceState; 77 return Info::WrongDeviceState;
78 } 78 }
79 79
80 if (!amiibo_file.IsOpen()) { 80 if (!nfc_file.IsOpen()) {
81 return Info::UnableToLoad; 81 return Info::UnableToLoad;
82 } 82 }
83 83
84 amiibo_data.resize(amiibo_size); 84 switch (nfc_file.GetSize()) {
85 85 case AmiiboSize:
86 if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) { 86 case AmiiboSizeWithoutPassword:
87 nfc_data.resize(AmiiboSize);
88 if (nfc_file.Read(nfc_data) < AmiiboSizeWithoutPassword) {
89 return Info::NotAnAmiibo;
90 }
91 break;
92 case MifareSize:
93 nfc_data.resize(MifareSize);
94 if (nfc_file.Read(nfc_data) < MifareSize) {
95 return Info::NotAnAmiibo;
96 }
97 break;
98 default:
87 return Info::NotAnAmiibo; 99 return Info::NotAnAmiibo;
88 } 100 }
89 101
90 file_path = filename; 102 file_path = filename;
91 state = State::AmiiboIsOpen; 103 state = State::AmiiboIsOpen;
92 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); 104 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
93 return Info::Success; 105 return Info::Success;
94} 106}
95 107
96VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { 108VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
97 if (state == State::AmiiboIsOpen) { 109 if (state == State::AmiiboIsOpen) {
98 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); 110 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
99 return Info::Success; 111 return Info::Success;
100 } 112 }
101 113
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 9baeb3997..0f9dad333 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -53,12 +53,13 @@ public:
53 std::string GetLastFilePath() const; 53 std::string GetLastFilePath() const;
54 54
55private: 55private:
56 static constexpr std::size_t amiibo_size = 0x21C; 56 static constexpr std::size_t AmiiboSize = 0x21C;
57 static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8; 57 static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8;
58 static constexpr std::size_t MifareSize = 0x400;
58 59
59 std::string file_path{}; 60 std::string file_path{};
60 State state{State::Initialized}; 61 State state{State::Initialized};
61 std::vector<u8> amiibo_data; 62 std::vector<u8> nfc_data;
62 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive}; 63 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive};
63}; 64};
64} // namespace InputCommon 65} // namespace InputCommon
diff --git a/src/input_common/precompiled_headers.h b/src/input_common/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/input_common/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 6f8ca4b90..1ab52da59 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -8,6 +8,7 @@ add_library(network STATIC
8 network.h 8 network.h
9 packet.cpp 9 packet.cpp
10 packet.h 10 packet.h
11 precompiled_headers.h
11 room.cpp 12 room.cpp
12 room.h 13 room.h
13 room_member.cpp 14 room_member.cpp
@@ -18,8 +19,12 @@ add_library(network STATIC
18 19
19create_target_directory_groups(network) 20create_target_directory_groups(network)
20 21
21target_link_libraries(network PRIVATE common enet Boost::boost) 22target_link_libraries(network PRIVATE common enet::enet Boost::boost)
22if (ENABLE_WEB_SERVICE) 23if (ENABLE_WEB_SERVICE)
23 target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE) 24 target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE)
24 target_link_libraries(network PRIVATE web_service) 25 target_link_libraries(network PRIVATE web_service)
25endif() 26endif()
27
28if (YUZU_USE_PRECOMPILED_HEADERS)
29 target_precompile_headers(network PRIVATE precompiled_headers.h)
30endif()
diff --git a/src/network/precompiled_headers.h b/src/network/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/network/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 545d69c7e..525b2363c 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
@@ -230,6 +231,7 @@ add_library(shader_recompiler STATIC
230 ir_opt/texture_pass.cpp 231 ir_opt/texture_pass.cpp
231 ir_opt/verification_pass.cpp 232 ir_opt/verification_pass.cpp
232 object_pool.h 233 object_pool.h
234 precompiled_headers.h
233 profile.h 235 profile.h
234 program_header.h 236 program_header.h
235 runtime_info.h 237 runtime_info.h
@@ -254,7 +256,12 @@ else()
254 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. 256 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
255 # And this in turns limits the size of a std::array. 257 # And this in turns limits the size of a std::array.
256 $<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024> 258 $<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
259 $<$<CXX_COMPILER_ID:AppleClang>:-fbracket-depth=1024>
257 ) 260 )
258endif() 261endif()
259 262
260create_target_directory_groups(shader_recompiler) 263create_target_directory_groups(shader_recompiler)
264
265if (YUZU_USE_PRECOMPILED_HEADERS)
266 target_precompile_headers(shader_recompiler PRIVATE precompiled_headers.h)
267endif()
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 265ac9c85..0f86a8004 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -402,8 +402,10 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
402 ctx.AddCapability(spv::Capability::SparseResidency); 402 ctx.AddCapability(spv::Capability::SparseResidency);
403 } 403 }
404 if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) { 404 if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) {
405 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); 405 if (profile.supported_spirv < 0x00010600) {
406 ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); 406 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
407 }
408 ctx.AddCapability(spv::Capability::DemoteToHelperInvocation);
407 } 409 }
408 if (info.stores[IR::Attribute::ViewportIndex]) { 410 if (info.stores[IR::Attribute::ViewportIndex]) {
409 ctx.AddCapability(spv::Capability::MultiViewport); 411 ctx.AddCapability(spv::Capability::MultiViewport);
@@ -426,12 +428,11 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
426 if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || 428 if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
427 info.uses_subgroup_shuffles) && 429 info.uses_subgroup_shuffles) &&
428 profile.support_vote) { 430 profile.support_vote) {
429 ctx.AddExtension("SPV_KHR_shader_ballot"); 431 ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
430 ctx.AddCapability(spv::Capability::SubgroupBallotKHR); 432 ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
431 if (!profile.warp_size_potentially_larger_than_guest) { 433 if (!profile.warp_size_potentially_larger_than_guest) {
432 // vote ops are only used when not taking the long path 434 // vote ops are only used when not taking the long path
433 ctx.AddExtension("SPV_KHR_subgroup_vote"); 435 ctx.AddCapability(spv::Capability::GroupNonUniformVote);
434 ctx.AddCapability(spv::Capability::SubgroupVoteKHR);
435 } 436 }
436 } 437 }
437 if (info.uses_int64_bit_atomics && profile.support_int64_atomics) { 438 if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
index 7ad0b08ac..fb2c792c1 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -12,7 +12,7 @@ void EmitJoin(EmitContext&) {
12 12
13void EmitDemoteToHelperInvocation(EmitContext& ctx) { 13void EmitDemoteToHelperInvocation(EmitContext& ctx) {
14 if (ctx.profile.support_demote_to_helper_invocation) { 14 if (ctx.profile.support_demote_to_helper_invocation) {
15 ctx.OpDemoteToHelperInvocationEXT(); 15 ctx.OpDemoteToHelperInvocation();
16 } else { 16 } else {
17 const Id kill_label{ctx.OpLabel()}; 17 const Id kill_label{ctx.OpLabel()};
18 const Id impossible_label{ctx.OpLabel()}; 18 const Id impossible_label{ctx.OpLabel()};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index 7cbbbfaa6..2c90f2368 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -6,6 +6,10 @@
6 6
7namespace Shader::Backend::SPIRV { 7namespace Shader::Backend::SPIRV {
8namespace { 8namespace {
9Id SubgroupScope(EmitContext& ctx) {
10 return ctx.Const(static_cast<u32>(spv::Scope::Subgroup));
11}
12
9Id GetThreadId(EmitContext& ctx) { 13Id GetThreadId(EmitContext& ctx) {
10 return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); 14 return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
11} 15}
@@ -49,8 +53,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
49} 53}
50 54
51Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { 55Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
52 return ctx.OpSelect(ctx.U32[1], in_range, 56 return ctx.OpSelect(
53 ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); 57 ctx.U32[1], in_range,
58 ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
54} 59}
55 60
56Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { 61Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) {
@@ -71,40 +76,46 @@ Id EmitLaneId(EmitContext& ctx) {
71 76
72Id EmitVoteAll(EmitContext& ctx, Id pred) { 77Id EmitVoteAll(EmitContext& ctx, Id pred) {
73 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 78 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
74 return ctx.OpSubgroupAllKHR(ctx.U1, pred); 79 return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
75 } 80 }
76 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 81 const Id mask_ballot{
82 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
77 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 83 const Id active_mask{WarpExtract(ctx, mask_ballot)};
78 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 84 const Id ballot{
85 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
79 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; 86 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
80 return ctx.OpIEqual(ctx.U1, lhs, active_mask); 87 return ctx.OpIEqual(ctx.U1, lhs, active_mask);
81} 88}
82 89
83Id EmitVoteAny(EmitContext& ctx, Id pred) { 90Id EmitVoteAny(EmitContext& ctx, Id pred) {
84 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 91 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
85 return ctx.OpSubgroupAnyKHR(ctx.U1, pred); 92 return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
86 } 93 }
87 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 94 const Id mask_ballot{
95 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
88 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 96 const Id active_mask{WarpExtract(ctx, mask_ballot)};
89 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 97 const Id ballot{
98 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
90 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; 99 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
91 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value); 100 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value);
92} 101}
93 102
94Id EmitVoteEqual(EmitContext& ctx, Id pred) { 103Id EmitVoteEqual(EmitContext& ctx, Id pred) {
95 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 104 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
96 return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred); 105 return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
97 } 106 }
98 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 107 const Id mask_ballot{
108 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
99 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 109 const Id active_mask{WarpExtract(ctx, mask_ballot)};
100 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 110 const Id ballot{
111 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
101 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)}; 112 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)};
102 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value), 113 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value),
103 ctx.OpIEqual(ctx.U1, lhs, active_mask)); 114 ctx.OpIEqual(ctx.U1, lhs, active_mask));
104} 115}
105 116
106Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { 117Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
107 const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)}; 118 const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
108 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 119 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
109 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); 120 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
110 } 121 }
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h
index e70d7745c..d155afd0f 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.h
+++ b/src/shader_recompiler/frontend/ir/opcodes.h
@@ -8,6 +8,7 @@
8 8
9#include <fmt/format.h> 9#include <fmt/format.h>
10 10
11#include "common/polyfill_ranges.h"
11#include "shader_recompiler/frontend/ir/type.h" 12#include "shader_recompiler/frontend/ir/type.h"
12 13
13namespace Shader::IR { 14namespace Shader::IR {
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index e8bbb93a5..8b34356fd 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -23,7 +23,6 @@
23#include "shader_recompiler/frontend/ir/pred.h" 23#include "shader_recompiler/frontend/ir/pred.h"
24#include "shader_recompiler/frontend/ir/reg.h" 24#include "shader_recompiler/frontend/ir/reg.h"
25#include "shader_recompiler/frontend/ir/type.h" 25#include "shader_recompiler/frontend/ir/type.h"
26#include "shader_recompiler/frontend/ir/value.h"
27 26
28namespace Shader::IR { 27namespace Shader::IR {
29 28
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
index 6939692cd..dce414cb4 100644
--- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
@@ -9,6 +9,7 @@
9 9
10#include <fmt/format.h> 10#include <fmt/format.h>
11 11
12#include "common/polyfill_ranges.h"
12#include "shader_recompiler/exception.h" 13#include "shader_recompiler/exception.h"
13#include "shader_recompiler/frontend/maxwell/control_flow.h" 14#include "shader_recompiler/frontend/maxwell/control_flow.h"
14#include "shader_recompiler/frontend/maxwell/decode.h" 15#include "shader_recompiler/frontend/maxwell/decode.h"
diff --git a/src/shader_recompiler/frontend/maxwell/decode.cpp b/src/shader_recompiler/frontend/maxwell/decode.cpp
index 455c91470..774f65bc5 100644
--- a/src/shader_recompiler/frontend/maxwell/decode.cpp
+++ b/src/shader_recompiler/frontend/maxwell/decode.cpp
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_ranges.h"
10#include "shader_recompiler/exception.h" 11#include "shader_recompiler/exception.h"
11#include "shader_recompiler/frontend/maxwell/decode.h" 12#include "shader_recompiler/frontend/maxwell/decode.h"
12#include "shader_recompiler/frontend/maxwell/opcodes.h" 13#include "shader_recompiler/frontend/maxwell/opcodes.h"
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index ce42475d4..80c90fe6a 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -12,6 +12,7 @@
12 12
13#include <boost/intrusive/list.hpp> 13#include <boost/intrusive/list.hpp>
14 14
15#include "common/polyfill_ranges.h"
15#include "shader_recompiler/environment.h" 16#include "shader_recompiler/environment.h"
16#include "shader_recompiler/frontend/ir/basic_block.h" 17#include "shader_recompiler/frontend/ir/basic_block.h"
17#include "shader_recompiler/frontend/ir/ir_emitter.h" 18#include "shader_recompiler/frontend/ir/ir_emitter.h"
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
index 4942878b9..85c18d942 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
@@ -176,12 +176,13 @@ void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::F16F32F64& src_a) {
176 (f2i.src_format == SrcFormat::F64) != (f2i.dest_format == DestFormat::I64); 176 (f2i.src_format == SrcFormat::F64) != (f2i.dest_format == DestFormat::I64);
177 if (special_nan_cases) { 177 if (special_nan_cases) {
178 if (f2i.dest_format == DestFormat::I32) { 178 if (f2i.dest_format == DestFormat::I32) {
179 constexpr u32 nan_value = 0x8000'0000U;
179 handled_special_case = true; 180 handled_special_case = true;
180 result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(0x8000'0000U), result)}; 181 result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(nan_value), result)};
181 } else if (f2i.dest_format == DestFormat::I64) { 182 } else if (f2i.dest_format == DestFormat::I64) {
183 constexpr u64 nan_value = 0x8000'0000'0000'0000ULL;
182 handled_special_case = true; 184 handled_special_case = true;
183 result = IR::U64{ 185 result = IR::U64{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(nan_value), result)};
184 v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(0x8000'0000'0000'0000UL), result)};
185 } 186 }
186 } 187 }
187 if (!handled_special_case && is_signed) { 188 if (!handled_special_case && is_signed) {
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/precompiled_headers.h b/src/shader_recompiler/precompiled_headers.h
new file mode 100644
index 000000000..5dd6b7eca
--- /dev/null
+++ b/src/shader_recompiler/precompiled_headers.h
@@ -0,0 +1,7 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
7#include "frontend/maxwell/translate/impl/impl.h"
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/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 43ad2c7ff..348d1edf4 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -11,6 +11,7 @@ add_executable(tests
11 common/unique_function.cpp 11 common/unique_function.cpp
12 core/core_timing.cpp 12 core/core_timing.cpp
13 core/internal_network/network.cpp 13 core/internal_network/network.cpp
14 precompiled_headers.h
14 tests.cpp 15 tests.cpp
15 video_core/buffer_base.cpp 16 video_core/buffer_base.cpp
16 input_common/calibration_configuration_job.cpp 17 input_common/calibration_configuration_job.cpp
@@ -22,3 +23,7 @@ target_link_libraries(tests PRIVATE common core input_common)
22target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads) 23target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads)
23 24
24add_test(NAME tests COMMAND tests) 25add_test(NAME tests COMMAND tests)
26
27if (YUZU_USE_PRECOMPILED_HEADERS)
28 target_precompile_headers(tests PRIVATE precompiled_headers.h)
29endif()
diff --git a/src/tests/precompiled_headers.h b/src/tests/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/tests/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index b03a30992..5096d935e 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -84,6 +84,7 @@ add_library(video_core STATIC
84 gpu_thread.h 84 gpu_thread.h
85 memory_manager.cpp 85 memory_manager.cpp
86 memory_manager.h 86 memory_manager.h
87 precompiled_headers.h
87 pte_kind.h 88 pte_kind.h
88 query_cache.h 89 query_cache.h
89 rasterizer_accelerated.cpp 90 rasterizer_accelerated.cpp
@@ -91,6 +92,10 @@ add_library(video_core STATIC
91 rasterizer_interface.h 92 rasterizer_interface.h
92 renderer_base.cpp 93 renderer_base.cpp
93 renderer_base.h 94 renderer_base.h
95 renderer_null/null_rasterizer.cpp
96 renderer_null/null_rasterizer.h
97 renderer_null/renderer_null.cpp
98 renderer_null/renderer_null.h
94 renderer_opengl/gl_buffer_cache.cpp 99 renderer_opengl/gl_buffer_cache.cpp
95 renderer_opengl/gl_buffer_cache.h 100 renderer_opengl/gl_buffer_cache.h
96 renderer_opengl/gl_compute_pipeline.cpp 101 renderer_opengl/gl_compute_pipeline.cpp
@@ -259,8 +264,7 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
259 264
260add_dependencies(video_core host_shaders) 265add_dependencies(video_core host_shaders)
261target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) 266target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
262target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) 267target_link_libraries(video_core PRIVATE sirit Vulkan::Headers)
263target_link_libraries(video_core PRIVATE sirit)
264 268
265if (ENABLE_NSIGHT_AFTERMATH) 269if (ENABLE_NSIGHT_AFTERMATH)
266 if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) 270 if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
@@ -279,9 +283,15 @@ if (MSVC)
279 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data 283 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
280 ) 284 )
281else() 285else()
282 target_compile_options(video_core PRIVATE 286 if (APPLE)
283 -Werror=conversion 287 # error: declaration shadows a typedef in 'interval_base_set<SubType, DomainT, Compare, Interval, Alloc>'
288 # error: implicit conversion loses integer precision: 'int' to 'boost::icl::bound_type' (aka 'unsigned char')
289 target_compile_options(video_core PRIVATE -Wno-shadow -Wno-unused-local-typedef)
290 else()
291 target_compile_options(video_core PRIVATE -Werror=conversion)
292 endif()
284 293
294 target_compile_options(video_core PRIVATE
285 -Wno-sign-conversion 295 -Wno-sign-conversion
286 ) 296 )
287 297
@@ -294,9 +304,13 @@ if (ARCHITECTURE_x86_64)
294 macro/macro_jit_x64.cpp 304 macro/macro_jit_x64.cpp
295 macro/macro_jit_x64.h 305 macro/macro_jit_x64.h
296 ) 306 )
297 target_link_libraries(video_core PUBLIC xbyak) 307 target_link_libraries(video_core PUBLIC xbyak::xbyak)
298endif() 308endif()
299 309
300if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 310if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
301 target_link_libraries(video_core PRIVATE dynarmic) 311 target_link_libraries(video_core PRIVATE dynarmic::dynarmic)
312endif()
313
314if (YUZU_USE_PRECOMPILED_HEADERS)
315 target_precompile_headers(video_core PRIVATE precompiled_headers.h)
302endif() 316endif()
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index f9a6472cf..92d77eef2 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -535,7 +535,7 @@ private:
535 const u64* const state_words = Array<type>(); 535 const u64* const state_words = Array<type>();
536 const u64 num_query_words = size / BYTES_PER_WORD + 1; 536 const u64 num_query_words = size / BYTES_PER_WORD + 1;
537 const u64 word_begin = offset / BYTES_PER_WORD; 537 const u64 word_begin = offset / BYTES_PER_WORD;
538 const u64 word_end = std::min(word_begin + num_query_words, NumWords()); 538 const u64 word_end = std::min<u64>(word_begin + num_query_words, NumWords());
539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); 539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; 540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD;
541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { 541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) {
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 5d3a8293b..6881b34c4 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -19,6 +19,7 @@
19#include "common/literals.h" 19#include "common/literals.h"
20#include "common/lru_cache.h" 20#include "common/lru_cache.h"
21#include "common/microprofile.h" 21#include "common/microprofile.h"
22#include "common/polyfill_ranges.h"
22#include "common/settings.h" 23#include "common/settings.h"
23#include "core/memory.h" 24#include "core/memory.h"
24#include "video_core/buffer_cache/buffer_base.h" 25#include "video_core/buffer_cache/buffer_base.h"
diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h
index 584a0c26c..cdaf4f8d5 100644
--- a/src/video_core/control/channel_state_cache.h
+++ b/src/video_core/control/channel_state_cache.h
@@ -35,8 +35,6 @@ public:
35 explicit ChannelInfo(Tegra::Control::ChannelState& state); 35 explicit ChannelInfo(Tegra::Control::ChannelState& state);
36 ChannelInfo(const ChannelInfo& state) = delete; 36 ChannelInfo(const ChannelInfo& state) = delete;
37 ChannelInfo& operator=(const ChannelInfo&) = delete; 37 ChannelInfo& operator=(const ChannelInfo&) = delete;
38 ChannelInfo(ChannelInfo&& other) = default;
39 ChannelInfo& operator=(ChannelInfo&& other) = default;
40 38
41 Tegra::Engines::Maxwell3D& maxwell3d; 39 Tegra::Engines::Maxwell3D& maxwell3d;
42 Tegra::Engines::KeplerCompute& kepler_compute; 40 Tegra::Engines::KeplerCompute& kepler_compute;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 55462752c..fb9b9b94e 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -126,7 +126,6 @@ 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;
130} 129}
131 130
132void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { 131void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
@@ -218,16 +217,19 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
218 regs.index_buffer.count = regs.index_buffer32_first.count; 217 regs.index_buffer.count = regs.index_buffer32_first.count;
219 regs.index_buffer.first = regs.index_buffer32_first.first; 218 regs.index_buffer.first = regs.index_buffer32_first.first;
220 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 219 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
220 draw_indexed = true;
221 return ProcessDraw(); 221 return ProcessDraw();
222 case MAXWELL3D_REG_INDEX(index_buffer16_first): 222 case MAXWELL3D_REG_INDEX(index_buffer16_first):
223 regs.index_buffer.count = regs.index_buffer16_first.count; 223 regs.index_buffer.count = regs.index_buffer16_first.count;
224 regs.index_buffer.first = regs.index_buffer16_first.first; 224 regs.index_buffer.first = regs.index_buffer16_first.first;
225 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 225 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
226 draw_indexed = true;
226 return ProcessDraw(); 227 return ProcessDraw();
227 case MAXWELL3D_REG_INDEX(index_buffer8_first): 228 case MAXWELL3D_REG_INDEX(index_buffer8_first):
228 regs.index_buffer.count = regs.index_buffer8_first.count; 229 regs.index_buffer.count = regs.index_buffer8_first.count;
229 regs.index_buffer.first = regs.index_buffer8_first.first; 230 regs.index_buffer.first = regs.index_buffer8_first.first;
230 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 231 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
232 draw_indexed = true;
231 return ProcessDraw(); 233 return ProcessDraw();
232 case MAXWELL3D_REG_INDEX(topology_override): 234 case MAXWELL3D_REG_INDEX(topology_override):
233 use_topology_override = true; 235 use_topology_override = true;
@@ -300,21 +302,33 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
300 draw_mode = DrawMode::InlineIndex; 302 draw_mode = DrawMode::InlineIndex;
301 }; 303 };
302 switch (method) { 304 switch (method) {
305 case MAXWELL3D_REG_INDEX(draw.begin): {
306 draw_mode =
307 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
308 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
309 ? DrawMode::Instance
310 : DrawMode::General;
311 break;
312 }
303 case MAXWELL3D_REG_INDEX(draw.end): 313 case MAXWELL3D_REG_INDEX(draw.end):
304 switch (draw_mode) { 314 switch (draw_mode) {
305 case DrawMode::General: 315 case DrawMode::General:
306 ProcessDraw(1); 316 ProcessDraw();
307 break; 317 break;
308 case DrawMode::InlineIndex: 318 case DrawMode::InlineIndex:
309 regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4); 319 regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
310 regs.index_buffer.format = Regs::IndexFormat::UnsignedInt; 320 regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
311 ProcessDraw(1); 321 draw_indexed = true;
322 ProcessDraw();
312 inline_index_draw_indexes.clear(); 323 inline_index_draw_indexes.clear();
313 break; 324 break;
314 case DrawMode::Instance: 325 case DrawMode::Instance:
315 break; 326 break;
316 } 327 }
317 break; 328 break;
329 case MAXWELL3D_REG_INDEX(index_buffer.count):
330 draw_indexed = true;
331 break;
318 case MAXWELL3D_REG_INDEX(draw_inline_index): 332 case MAXWELL3D_REG_INDEX(draw_inline_index):
319 update_inline_index(method_argument); 333 update_inline_index(method_argument);
320 break; 334 break;
@@ -328,13 +342,6 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
328 update_inline_index(regs.inline_index_4x8.index2); 342 update_inline_index(regs.inline_index_4x8.index2);
329 update_inline_index(regs.inline_index_4x8.index3); 343 update_inline_index(regs.inline_index_4x8.index3);
330 break; 344 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;
338 } 345 }
339 } else { 346 } else {
340 ProcessDeferredDraw(); 347 ProcessDeferredDraw();
@@ -486,41 +493,51 @@ void Maxwell3D::ProcessQueryGet() {
486 493
487void Maxwell3D::ProcessQueryCondition() { 494void Maxwell3D::ProcessQueryCondition() {
488 const GPUVAddr condition_address{regs.render_enable.Address()}; 495 const GPUVAddr condition_address{regs.render_enable.Address()};
489 switch (regs.render_enable.mode) { 496 switch (regs.render_enable_override) {
490 case Regs::RenderEnable::Mode::True: { 497 case Regs::RenderEnable::Override::AlwaysRender:
491 execute_on = true; 498 execute_on = true;
492 break; 499 break;
493 } 500 case Regs::RenderEnable::Override::NeverRender:
494 case Regs::RenderEnable::Mode::False: {
495 execute_on = false; 501 execute_on = false;
496 break; 502 break;
497 } 503 case Regs::RenderEnable::Override::UseRenderEnable:
498 case Regs::RenderEnable::Mode::Conditional: { 504 switch (regs.render_enable.mode) {
499 Regs::ReportSemaphore::Compare cmp; 505 case Regs::RenderEnable::Mode::True: {
500 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); 506 execute_on = true;
501 execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U; 507 break;
502 break; 508 }
503 } 509 case Regs::RenderEnable::Mode::False: {
504 case Regs::RenderEnable::Mode::IfEqual: { 510 execute_on = false;
505 Regs::ReportSemaphore::Compare cmp; 511 break;
506 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); 512 }
507 execute_on = 513 case Regs::RenderEnable::Mode::Conditional: {
508 cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode; 514 Regs::ReportSemaphore::Compare cmp;
509 break; 515 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
510 } 516 execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U;
511 case Regs::RenderEnable::Mode::IfNotEqual: { 517 break;
512 Regs::ReportSemaphore::Compare cmp; 518 }
513 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); 519 case Regs::RenderEnable::Mode::IfEqual: {
514 execute_on = 520 Regs::ReportSemaphore::Compare cmp;
515 cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode; 521 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
516 break; 522 execute_on = cmp.initial_sequence == cmp.current_sequence &&
517 } 523 cmp.initial_mode == cmp.current_mode;
518 default: { 524 break;
519 UNIMPLEMENTED_MSG("Uninplemented Condition Mode!"); 525 }
520 execute_on = true; 526 case Regs::RenderEnable::Mode::IfNotEqual: {
527 Regs::ReportSemaphore::Compare cmp;
528 memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
529 execute_on = cmp.initial_sequence != cmp.current_sequence ||
530 cmp.initial_mode != cmp.current_mode;
531 break;
532 }
533 default: {
534 UNIMPLEMENTED_MSG("Uninplemented Condition Mode!");
535 execute_on = true;
536 break;
537 }
538 }
521 break; 539 break;
522 } 540 }
523 }
524} 541}
525 542
526void Maxwell3D::ProcessCounterReset() { 543void Maxwell3D::ProcessCounterReset() {
@@ -624,27 +641,16 @@ void Maxwell3D::ProcessClearBuffers(u32 layer_count) {
624 641
625void Maxwell3D::ProcessDraw(u32 instance_count) { 642void Maxwell3D::ProcessDraw(u32 instance_count) {
626 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), 643 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
627 regs.vertex_buffer.count); 644 draw_indexed ? regs.index_buffer.count : regs.vertex_buffer.count);
628
629 ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
630
631 // Both instance configuration registers can not be set at the same time.
632 ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
633 regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
634 "Illegal combination of instancing parameters");
635 645
636 ProcessTopologyOverride(); 646 ProcessTopologyOverride();
637 647
638 const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count;
639 if (ShouldExecute()) { 648 if (ShouldExecute()) {
640 rasterizer->Draw(is_indexed, instance_count); 649 rasterizer->Draw(draw_indexed, instance_count);
641 } 650 }
642 651
643 if (is_indexed) { 652 draw_indexed = false;
644 regs.index_buffer.count = 0; 653 deferred_draw_method.clear();
645 } else {
646 regs.vertex_buffer.count = 0;
647 }
648} 654}
649 655
650void Maxwell3D::ProcessDeferredDraw() { 656void Maxwell3D::ProcessDeferredDraw() {
@@ -667,8 +673,6 @@ void Maxwell3D::ProcessDeferredDraw() {
667 ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?"); 673 ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?");
668 674
669 ProcessDraw(instance_count); 675 ProcessDraw(instance_count);
670
671 deferred_draw_method.clear();
672} 676}
673 677
674} // namespace Tegra::Engines 678} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index deba292a5..a541cd95f 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -3159,6 +3159,7 @@ private:
3159 std::vector<u32> deferred_draw_method; 3159 std::vector<u32> deferred_draw_method;
3160 enum class DrawMode : u32 { General = 0, Instance, InlineIndex }; 3160 enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
3161 DrawMode draw_mode{DrawMode::General}; 3161 DrawMode draw_mode{DrawMode::General};
3162 bool draw_indexed{};
3162}; 3163};
3163 3164
3164#define ASSERT_REG_POSITION(field_name, position) \ 3165#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/sw_blitter/converter.cpp b/src/video_core/engines/sw_blitter/converter.cpp
index cd46dfd4f..2419b5632 100644
--- a/src/video_core/engines/sw_blitter/converter.cpp
+++ b/src/video_core/engines/sw_blitter/converter.cpp
@@ -2,12 +2,12 @@
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <array> 4#include <array>
5#include <bit>
6#include <cmath> 5#include <cmath>
7#include <span> 6#include <span>
8#include <unordered_map> 7#include <unordered_map>
9 8
10#include "common/assert.h" 9#include "common/assert.h"
10#include "common/bit_cast.h"
11#include "video_core/engines/sw_blitter/converter.h" 11#include "video_core/engines/sw_blitter/converter.h"
12#include "video_core/surface.h" 12#include "video_core/surface.h"
13#include "video_core/textures/decoders.h" 13#include "video_core/textures/decoders.h"
@@ -693,21 +693,21 @@ private:
693 return shifted_value >> shift_amount; 693 return shifted_value >> shift_amount;
694 }; 694 };
695 const auto force_to_fp16 = [](f32 base_value) { 695 const auto force_to_fp16 = [](f32 base_value) {
696 u32 tmp = std::bit_cast<u32>(base_value); 696 u32 tmp = Common::BitCast<u32>(base_value);
697 constexpr size_t fp32_mantissa_bits = 23; 697 constexpr size_t fp32_mantissa_bits = 23;
698 constexpr size_t fp16_mantissa_bits = 10; 698 constexpr size_t fp16_mantissa_bits = 10;
699 constexpr size_t mantissa_mask = 699 constexpr size_t mantissa_mask =
700 ~((1ULL << (fp32_mantissa_bits - fp16_mantissa_bits)) - 1ULL); 700 ~((1ULL << (fp32_mantissa_bits - fp16_mantissa_bits)) - 1ULL);
701 tmp = tmp & static_cast<u32>(mantissa_mask); 701 tmp = tmp & static_cast<u32>(mantissa_mask);
702 // TODO: force the exponent within the range of half float. Not needed in UNORM / SNORM 702 // TODO: force the exponent within the range of half float. Not needed in UNORM / SNORM
703 return std::bit_cast<f32>(tmp); 703 return Common::BitCast<f32>(tmp);
704 }; 704 };
705 const auto from_fp_n = [&sign_extend](u32 base_value, size_t bits, size_t mantissa) { 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; 706 constexpr size_t fp32_mantissa_bits = 23;
707 size_t shift_towards = fp32_mantissa_bits - mantissa; 707 size_t shift_towards = fp32_mantissa_bits - mantissa;
708 const u32 new_value = 708 const u32 new_value =
709 static_cast<u32>(sign_extend(base_value, bits) << shift_towards) & (~(1U << 31)); 709 static_cast<u32>(sign_extend(base_value, bits) << shift_towards) & (~(1U << 31));
710 return std::bit_cast<f32>(new_value); 710 return Common::BitCast<f32>(new_value);
711 }; 711 };
712 const auto calculate_snorm = [&]() { 712 const auto calculate_snorm = [&]() {
713 return static_cast<f32>( 713 return static_cast<f32>(
@@ -737,13 +737,13 @@ private:
737 out_component = force_to_fp16(out_component); 737 out_component = force_to_fp16(out_component);
738 } else if constexpr (component_types[which_component] == ComponentType::FLOAT) { 738 } else if constexpr (component_types[which_component] == ComponentType::FLOAT) {
739 if constexpr (component_sizes[which_component] == 32) { 739 if constexpr (component_sizes[which_component] == 32) {
740 out_component = std::bit_cast<f32>(value); 740 out_component = Common::BitCast<f32>(value);
741 } else if constexpr (component_sizes[which_component] == 16) { 741 } else if constexpr (component_sizes[which_component] == 16) {
742 static constexpr u32 sign_mask = 0x8000; 742 static constexpr u32 sign_mask = 0x8000;
743 static constexpr u32 mantissa_mask = 0x8000; 743 static constexpr u32 mantissa_mask = 0x8000;
744 out_component = std::bit_cast<f32>(((value & sign_mask) << 16) | 744 out_component = Common::BitCast<f32>(((value & sign_mask) << 16) |
745 (((value & 0x7c00) + 0x1C000) << 13) | 745 (((value & 0x7c00) + 0x1C000) << 13) |
746 ((value & mantissa_mask) << 13)); 746 ((value & mantissa_mask) << 13));
747 } else { 747 } else {
748 out_component = from_fp_n(value, component_sizes[which_component], 748 out_component = from_fp_n(value, component_sizes[which_component],
749 component_sizes[which_component] - 5); 749 component_sizes[which_component] - 5);
@@ -771,7 +771,7 @@ private:
771 }; 771 };
772 const auto to_fp_n = [](f32 base_value, size_t bits, size_t mantissa) { 772 const auto to_fp_n = [](f32 base_value, size_t bits, size_t mantissa) {
773 constexpr size_t fp32_mantissa_bits = 23; 773 constexpr size_t fp32_mantissa_bits = 23;
774 u32 tmp_value = std::bit_cast<u32>(std::max(base_value, 0.0f)); 774 u32 tmp_value = Common::BitCast<u32>(std::max(base_value, 0.0f));
775 size_t shift_towards = fp32_mantissa_bits - mantissa; 775 size_t shift_towards = fp32_mantissa_bits - mantissa;
776 return tmp_value >> shift_towards; 776 return tmp_value >> shift_towards;
777 }; 777 };
@@ -799,13 +799,13 @@ private:
799 insert_to_word(tmp_word); 799 insert_to_word(tmp_word);
800 } else if constexpr (component_types[which_component] == ComponentType::FLOAT) { 800 } else if constexpr (component_types[which_component] == ComponentType::FLOAT) {
801 if constexpr (component_sizes[which_component] == 32) { 801 if constexpr (component_sizes[which_component] == 32) {
802 u32 tmp_word = std::bit_cast<u32>(in_component); 802 u32 tmp_word = Common::BitCast<u32>(in_component);
803 insert_to_word(tmp_word); 803 insert_to_word(tmp_word);
804 } else if constexpr (component_sizes[which_component] == 16) { 804 } else if constexpr (component_sizes[which_component] == 16) {
805 static constexpr u32 sign_mask = 0x8000; 805 static constexpr u32 sign_mask = 0x8000;
806 static constexpr u32 mantissa_mask = 0x03ff; 806 static constexpr u32 mantissa_mask = 0x03ff;
807 static constexpr u32 exponent_mask = 0x7c00; 807 static constexpr u32 exponent_mask = 0x7c00;
808 const u32 tmp_word = std::bit_cast<u32>(in_component); 808 const u32 tmp_word = Common::BitCast<u32>(in_component);
809 const u32 half = ((tmp_word >> 16) & sign_mask) | 809 const u32 half = ((tmp_word >> 16) & sign_mask) |
810 ((((tmp_word & 0x7f800000) - 0x38000000) >> 13) & exponent_mask) | 810 ((((tmp_word & 0x7f800000) - 0x38000000) >> 13) & exponent_mask) |
811 ((tmp_word >> 13) & mantissa_mask); 811 ((tmp_word >> 13) & mantissa_mask);
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 1bd477011..164a5252a 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -125,7 +125,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
125 state.queue.Push(CommandDataContainer(std::move(command_data), fence, block)); 125 state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
126 126
127 if (block) { 127 if (block) {
128 state.cv.wait(lk, thread.get_stop_token(), [this, fence] { 128 Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] {
129 return fence <= state.signaled_fence.load(std::memory_order_relaxed); 129 return fence <= state.signaled_fence.load(std::memory_order_relaxed);
130 }); 130 });
131 } 131 }
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 64628d3e3..c71a419c7 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -10,6 +10,7 @@
10#include <thread> 10#include <thread>
11#include <variant> 11#include <variant>
12 12
13#include "common/polyfill_thread.h"
13#include "common/threadsafe_queue.h" 14#include "common/threadsafe_queue.h"
14#include "video_core/framebuffer_config.h" 15#include "video_core/framebuffer_config.h"
15 16
diff --git a/src/video_core/precompiled_headers.h b/src/video_core/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/video_core/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index cfd872a40..b6907463c 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -6,8 +6,8 @@
6#include <functional> 6#include <functional>
7#include <optional> 7#include <optional>
8#include <span> 8#include <span>
9#include <stop_token>
10#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_thread.h"
11#include "video_core/engines/fermi_2d.h" 11#include "video_core/engines/fermi_2d.h"
12#include "video_core/gpu.h" 12#include "video_core/gpu.h"
13 13
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp
new file mode 100644
index 000000000..9734d84bc
--- /dev/null
+++ b/src/video_core/renderer_null/null_rasterizer.cpp
@@ -0,0 +1,90 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/host1x/host1x.h"
5#include "video_core/memory_manager.h"
6#include "video_core/renderer_null/null_rasterizer.h"
7
8namespace Null {
9
10AccelerateDMA::AccelerateDMA() = default;
11
12bool AccelerateDMA::BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) {
13 return true;
14}
15bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) {
16 return true;
17}
18
19RasterizerNull::RasterizerNull(Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu)
20 : RasterizerAccelerated(cpu_memory_), m_gpu{gpu} {}
21RasterizerNull::~RasterizerNull() = default;
22
23void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {}
24void RasterizerNull::Clear(u32 layer_count) {}
25void RasterizerNull::DispatchCompute() {}
26void RasterizerNull::ResetCounter(VideoCore::QueryType type) {}
27void RasterizerNull::Query(GPUVAddr gpu_addr, VideoCore::QueryType type,
28 std::optional<u64> timestamp) {
29 if (!gpu_memory) {
30 return;
31 }
32
33 gpu_memory->Write(gpu_addr, u64{0});
34 if (timestamp) {
35 gpu_memory->Write(gpu_addr + 8, *timestamp);
36 }
37}
38void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
39 u32 size) {}
40void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {}
41void RasterizerNull::FlushAll() {}
42void RasterizerNull::FlushRegion(VAddr addr, u64 size) {}
43bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size) {
44 return false;
45}
46void RasterizerNull::InvalidateRegion(VAddr addr, u64 size) {}
47void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {}
48void RasterizerNull::InvalidateGPUCache() {}
49void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {}
50void RasterizerNull::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {}
51void RasterizerNull::SignalFence(std::function<void()>&& func) {
52 func();
53}
54void RasterizerNull::SyncOperation(std::function<void()>&& func) {
55 func();
56}
57void RasterizerNull::SignalSyncPoint(u32 value) {
58 auto& syncpoint_manager = m_gpu.Host1x().GetSyncpointManager();
59 syncpoint_manager.IncrementGuest(value);
60 syncpoint_manager.IncrementHost(value);
61}
62void RasterizerNull::SignalReference() {}
63void RasterizerNull::ReleaseFences() {}
64void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size) {}
65void RasterizerNull::WaitForIdle() {}
66void RasterizerNull::FragmentBarrier() {}
67void RasterizerNull::TiledCacheBarrier() {}
68void RasterizerNull::FlushCommands() {}
69void RasterizerNull::TickFrame() {}
70Tegra::Engines::AccelerateDMAInterface& RasterizerNull::AccessAccelerateDMA() {
71 return m_accelerate_dma;
72}
73bool RasterizerNull::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
74 const Tegra::Engines::Fermi2D::Surface& dst,
75 const Tegra::Engines::Fermi2D::Config& copy_config) {
76 return true;
77}
78void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
79 std::span<const u8> memory) {}
80bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config,
81 VAddr framebuffer_addr, u32 pixel_stride) {
82 return true;
83}
84void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
85 const VideoCore::DiskResourceLoadCallback& callback) {}
86void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {}
87void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {}
88void RasterizerNull::ReleaseChannel(s32 channel_id) {}
89
90} // namespace Null
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h
new file mode 100644
index 000000000..ecf77ba42
--- /dev/null
+++ b/src/video_core/renderer_null/null_rasterizer.h
@@ -0,0 +1,78 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "video_core/control/channel_state_cache.h"
8#include "video_core/engines/maxwell_dma.h"
9#include "video_core/rasterizer_accelerated.h"
10#include "video_core/rasterizer_interface.h"
11
12namespace Core {
13class System;
14}
15
16namespace Null {
17
18class RasterizerNull;
19
20class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface {
21public:
22 explicit AccelerateDMA();
23 bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override;
24 bool BufferClear(GPUVAddr src_address, u64 amount, u32 value) override;
25};
26
27class RasterizerNull final : public VideoCore::RasterizerAccelerated,
28 protected VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
29public:
30 explicit RasterizerNull(Core::Memory::Memory& cpu_memory, Tegra::GPU& gpu);
31 ~RasterizerNull() override;
32
33 void Draw(bool is_indexed, u32 instance_count) override;
34 void Clear(u32 layer_count) override;
35 void DispatchCompute() override;
36 void ResetCounter(VideoCore::QueryType type) override;
37 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
38 void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
39 void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
40 void FlushAll() override;
41 void FlushRegion(VAddr addr, u64 size) override;
42 bool MustFlushRegion(VAddr addr, u64 size) override;
43 void InvalidateRegion(VAddr addr, u64 size) override;
44 void OnCPUWrite(VAddr addr, u64 size) override;
45 void InvalidateGPUCache() override;
46 void UnmapMemory(VAddr addr, u64 size) override;
47 void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override;
48 void SignalFence(std::function<void()>&& func) override;
49 void SyncOperation(std::function<void()>&& func) override;
50 void SignalSyncPoint(u32 value) override;
51 void SignalReference() override;
52 void ReleaseFences() override;
53 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
54 void WaitForIdle() override;
55 void FragmentBarrier() override;
56 void TiledCacheBarrier() override;
57 void FlushCommands() override;
58 void TickFrame() override;
59 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
60 const Tegra::Engines::Fermi2D::Surface& dst,
61 const Tegra::Engines::Fermi2D::Config& copy_config) override;
62 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
63 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
64 std::span<const u8> memory) override;
65 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
66 u32 pixel_stride) override;
67 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
68 const VideoCore::DiskResourceLoadCallback& callback) override;
69 void InitializeChannel(Tegra::Control::ChannelState& channel) override;
70 void BindChannel(Tegra::Control::ChannelState& channel) override;
71 void ReleaseChannel(s32 channel_id) override;
72
73private:
74 Tegra::GPU& m_gpu;
75 AccelerateDMA m_accelerate_dma;
76};
77
78} // namespace Null
diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp
new file mode 100644
index 000000000..e2a189b63
--- /dev/null
+++ b/src/video_core/renderer_null/renderer_null.cpp
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/renderer_null/renderer_null.h"
5
6namespace Null {
7
8RendererNull::RendererNull(Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory,
9 Tegra::GPU& gpu,
10 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
11 : RendererBase(emu_window, std::move(context_)), m_gpu(gpu), m_rasterizer(cpu_memory, gpu) {}
12
13RendererNull::~RendererNull() = default;
14
15void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
16 if (!framebuffer) {
17 return;
18 }
19
20 m_gpu.RendererFrameEndNotify();
21 render_window.OnFrameDisplayed();
22}
23
24} // namespace Null
diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h
new file mode 100644
index 000000000..967ff5645
--- /dev/null
+++ b/src/video_core/renderer_null/renderer_null.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <string>
8
9#include "video_core/renderer_base.h"
10#include "video_core/renderer_null/null_rasterizer.h"
11
12namespace Null {
13
14class RendererNull final : public VideoCore::RendererBase {
15public:
16 explicit RendererNull(Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory,
17 Tegra::GPU& gpu,
18 std::unique_ptr<Core::Frontend::GraphicsContext> context);
19 ~RendererNull() override;
20
21 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
22
23 VideoCore::RasterizerInterface* ReadRasterizer() override {
24 return &m_rasterizer;
25 }
26
27 [[nodiscard]] std::string GetDeviceVendor() const override {
28 return "NULL";
29 }
30
31private:
32 Tegra::GPU& m_gpu;
33 RasterizerNull m_rasterizer;
34};
35
36} // namespace Null
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 1663e277d..e2e3dac34 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -14,6 +14,7 @@
14 14
15#include "common/literals.h" 15#include "common/literals.h"
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/polyfill_ranges.h"
17#include "common/settings.h" 18#include "common/settings.h"
18#include "shader_recompiler/stage.h" 19#include "shader_recompiler/stage.h"
19#include "video_core/renderer_opengl/gl_device.h" 20#include "video_core/renderer_opengl/gl_device.h"
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/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 89f181fe3..53ffea904 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -4,7 +4,6 @@
4#pragma once 4#pragma once
5 5
6#include <filesystem> 6#include <filesystem>
7#include <stop_token>
8#include <unordered_map> 7#include <unordered_map>
9 8
10#include "common/common_types.h" 9#include "common/common_types.h"
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 98cc26679..5864e772b 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -7,6 +7,7 @@
7#include "common/bit_cast.h" 7#include "common/bit_cast.h"
8#include "common/cityhash.h" 8#include "common/cityhash.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_ranges.h"
10#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 11#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
11#include "video_core/renderer_vulkan/vk_state_tracker.h" 12#include "video_core/renderer_vulkan/vk_state_tracker.h"
12 13
@@ -92,6 +93,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
92 provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0); 93 provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0);
93 conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); 94 conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0);
94 smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); 95 smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);
96 alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0);
97 alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0);
95 98
96 for (size_t i = 0; i < regs.rt.size(); ++i) { 99 for (size_t i = 0; i < regs.rt.size(); ++i) {
97 color_formats[i] = static_cast<u8>(regs.rt[i].format); 100 color_formats[i] = static_cast<u8>(regs.rt[i].format);
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 1afdef329..ab79fb8f3 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -195,6 +195,8 @@ struct FixedPipelineState {
195 BitField<12, 1, u32> provoking_vertex_last; 195 BitField<12, 1, u32> provoking_vertex_last;
196 BitField<13, 1, u32> conservative_raster_enable; 196 BitField<13, 1, u32> conservative_raster_enable;
197 BitField<14, 1, u32> smooth_lines; 197 BitField<14, 1, u32> smooth_lines;
198 BitField<15, 1, u32> alpha_to_coverage_enabled;
199 BitField<16, 1, u32> alpha_to_one_enabled;
198 }; 200 };
199 std::array<u8, Maxwell::NumRenderTargets> color_formats; 201 std::array<u8, Maxwell::NumRenderTargets> color_formats;
200 202
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 430a84272..3e03c5cd6 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -58,7 +58,7 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra
58 case Tegra::Texture::WrapMode::Border: 58 case Tegra::Texture::WrapMode::Border:
59 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; 59 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
60 case Tegra::Texture::WrapMode::Clamp: 60 case Tegra::Texture::WrapMode::Clamp:
61 if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) { 61 if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) {
62 // Nvidia's Vulkan driver defaults to GL_CLAMP on invalid enumerations, we can hack this 62 // Nvidia's Vulkan driver defaults to GL_CLAMP on invalid enumerations, we can hack this
63 // by sending an invalid enumeration. 63 // by sending an invalid enumeration.
64 return static_cast<VkSamplerAddressMode>(0xcafe); 64 return static_cast<VkSamplerAddressMode>(0xcafe);
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index b7843e995..28b893e25 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -44,17 +44,17 @@ public:
44 }); 44 });
45 } 45 }
46 46
47 vk::DescriptorUpdateTemplateKHR CreateTemplate(VkDescriptorSetLayout descriptor_set_layout, 47 vk::DescriptorUpdateTemplate CreateTemplate(VkDescriptorSetLayout descriptor_set_layout,
48 VkPipelineLayout pipeline_layout, 48 VkPipelineLayout pipeline_layout,
49 bool use_push_descriptor) const { 49 bool use_push_descriptor) const {
50 if (entries.empty()) { 50 if (entries.empty()) {
51 return nullptr; 51 return nullptr;
52 } 52 }
53 const VkDescriptorUpdateTemplateType type = 53 const VkDescriptorUpdateTemplateType type =
54 use_push_descriptor ? VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR 54 use_push_descriptor ? VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR
55 : VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR; 55 : VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
56 return device->GetLogical().CreateDescriptorUpdateTemplateKHR({ 56 return device->GetLogical().CreateDescriptorUpdateTemplate({
57 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR, 57 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO,
58 .pNext = nullptr, 58 .pNext = nullptr,
59 .flags = 0, 59 .flags = 0,
60 .descriptorUpdateEntryCount = static_cast<u32>(entries.size()), 60 .descriptorUpdateEntryCount = static_cast<u32>(entries.size()),
@@ -129,7 +129,7 @@ private:
129 const Device* device{}; 129 const Device* device{};
130 bool is_compute{}; 130 bool is_compute{};
131 boost::container::small_vector<VkDescriptorSetLayoutBinding, 32> bindings; 131 boost::container::small_vector<VkDescriptorSetLayoutBinding, 32> bindings;
132 boost::container::small_vector<VkDescriptorUpdateTemplateEntryKHR, 32> entries; 132 boost::container::small_vector<VkDescriptorUpdateTemplateEntry, 32> entries;
133 u32 binding{}; 133 u32 binding{};
134 u32 num_descriptors{}; 134 u32 num_descriptors{};
135 size_t offset{}; 135 size_t offset{};
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index c2a95200b..18be54729 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -45,14 +45,14 @@ std::string GetDriverVersion(const Device& device) {
45 // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 45 // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314
46 const u32 version = device.GetDriverVersion(); 46 const u32 version = device.GetDriverVersion();
47 47
48 if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) { 48 if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) {
49 const u32 major = (version >> 22) & 0x3ff; 49 const u32 major = (version >> 22) & 0x3ff;
50 const u32 minor = (version >> 14) & 0x0ff; 50 const u32 minor = (version >> 14) & 0x0ff;
51 const u32 secondary = (version >> 6) & 0x0ff; 51 const u32 secondary = (version >> 6) & 0x0ff;
52 const u32 tertiary = version & 0x003f; 52 const u32 tertiary = version & 0x003f;
53 return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary); 53 return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary);
54 } 54 }
55 if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR) { 55 if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
56 const u32 major = version >> 14; 56 const u32 major = version >> 14;
57 const u32 minor = version & 0x3fff; 57 const u32 minor = version & 0x3fff;
58 return fmt::format("{}.{}", major, minor); 58 return fmt::format("{}.{}", major, minor);
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 89426121f..6e5abade4 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/math_util.h" 12#include "common/math_util.h"
13#include "common/polyfill_ranges.h"
13#include "common/settings.h" 14#include "common/settings.h"
14#include "core/core.h" 15#include "core/core.h"
15#include "core/frontend/emu_window.h" 16#include "core/frontend/emu_window.h"
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 241d7573e..2c00979d7 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -93,7 +93,7 @@ constexpr DescriptorBankInfo ASTC_BANK_INFO{
93 .score = 2, 93 .score = 2,
94}; 94};
95 95
96constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{ 96constexpr VkDescriptorUpdateTemplateEntry INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{
97 .dstBinding = 0, 97 .dstBinding = 0,
98 .dstArrayElement = 0, 98 .dstArrayElement = 0,
99 .descriptorCount = 2, 99 .descriptorCount = 2,
@@ -102,7 +102,7 @@ constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMP
102 .stride = sizeof(DescriptorUpdateEntry), 102 .stride = sizeof(DescriptorUpdateEntry),
103}; 103};
104 104
105constexpr std::array<VkDescriptorUpdateTemplateEntryKHR, ASTC_NUM_BINDINGS> 105constexpr std::array<VkDescriptorUpdateTemplateEntry, ASTC_NUM_BINDINGS>
106 ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY{{ 106 ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY{{
107 { 107 {
108 .dstBinding = ASTC_BINDING_INPUT_BUFFER, 108 .dstBinding = ASTC_BINDING_INPUT_BUFFER,
@@ -134,7 +134,7 @@ struct AstcPushConstants {
134 134
135ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, 135ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool,
136 vk::Span<VkDescriptorSetLayoutBinding> bindings, 136 vk::Span<VkDescriptorSetLayoutBinding> bindings,
137 vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, 137 vk::Span<VkDescriptorUpdateTemplateEntry> templates,
138 const DescriptorBankInfo& bank_info, 138 const DescriptorBankInfo& bank_info,
139 vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code) 139 vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code)
140 : device{device_} { 140 : device{device_} {
@@ -155,13 +155,13 @@ ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool,
155 .pPushConstantRanges = push_constants.data(), 155 .pPushConstantRanges = push_constants.data(),
156 }); 156 });
157 if (!templates.empty()) { 157 if (!templates.empty()) {
158 descriptor_template = device.GetLogical().CreateDescriptorUpdateTemplateKHR({ 158 descriptor_template = device.GetLogical().CreateDescriptorUpdateTemplate({
159 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR, 159 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO,
160 .pNext = nullptr, 160 .pNext = nullptr,
161 .flags = 0, 161 .flags = 0,
162 .descriptorUpdateEntryCount = templates.size(), 162 .descriptorUpdateEntryCount = templates.size(),
163 .pDescriptorUpdateEntries = templates.data(), 163 .pDescriptorUpdateEntries = templates.data(),
164 .templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR, 164 .templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET,
165 .descriptorSetLayout = *descriptor_set_layout, 165 .descriptorSetLayout = *descriptor_set_layout,
166 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, 166 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
167 .pipelineLayout = *layout, 167 .pipelineLayout = *layout,
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index dcc691a8e..5d32e3caf 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -29,14 +29,14 @@ class ComputePass {
29public: 29public:
30 explicit ComputePass(const Device& device, DescriptorPool& descriptor_pool, 30 explicit ComputePass(const Device& device, DescriptorPool& descriptor_pool,
31 vk::Span<VkDescriptorSetLayoutBinding> bindings, 31 vk::Span<VkDescriptorSetLayoutBinding> bindings,
32 vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, 32 vk::Span<VkDescriptorUpdateTemplateEntry> templates,
33 const DescriptorBankInfo& bank_info, 33 const DescriptorBankInfo& bank_info,
34 vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code); 34 vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code);
35 ~ComputePass(); 35 ~ComputePass();
36 36
37protected: 37protected:
38 const Device& device; 38 const Device& device;
39 vk::DescriptorUpdateTemplateKHR descriptor_template; 39 vk::DescriptorUpdateTemplate descriptor_template;
40 vk::PipelineLayout layout; 40 vk::PipelineLayout layout;
41 vk::Pipeline pipeline; 41 vk::Pipeline pipeline;
42 vk::DescriptorSetLayout descriptor_set_layout; 42 vk::DescriptorSetLayout descriptor_set_layout;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 7906e11a8..04a3a861e 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -53,7 +53,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
53 .requiredSubgroupSize = GuestWarpSize, 53 .requiredSubgroupSize = GuestWarpSize,
54 }; 54 };
55 VkPipelineCreateFlags flags{}; 55 VkPipelineCreateFlags flags{};
56 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { 56 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
57 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; 57 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
58 } 58 }
59 pipeline = device.GetLogical().CreateComputePipeline({ 59 pipeline = device.GetLogical().CreateComputePipeline({
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index 9879735fe..d70837fc5 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -55,7 +55,7 @@ private:
55 vk::DescriptorSetLayout descriptor_set_layout; 55 vk::DescriptorSetLayout descriptor_set_layout;
56 DescriptorAllocator descriptor_allocator; 56 DescriptorAllocator descriptor_allocator;
57 vk::PipelineLayout pipeline_layout; 57 vk::PipelineLayout pipeline_layout;
58 vk::DescriptorUpdateTemplateKHR descriptor_update_template; 58 vk::DescriptorUpdateTemplate descriptor_update_template;
59 vk::Pipeline pipeline; 59 vk::Pipeline pipeline;
60 60
61 std::condition_variable build_condvar; 61 std::condition_variable build_condvar;
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
index c7196b64e..b5ae6443c 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
@@ -7,6 +7,7 @@
7#include <vector> 7#include <vector>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_ranges.h"
10#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 11#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
11#include "video_core/renderer_vulkan/vk_resource_pool.h" 12#include "video_core/renderer_vulkan/vk_resource_pool.h"
12#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index ef75c126c..006128638 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -714,8 +714,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
714 .sampleShadingEnable = VK_FALSE, 714 .sampleShadingEnable = VK_FALSE,
715 .minSampleShading = 0.0f, 715 .minSampleShading = 0.0f,
716 .pSampleMask = nullptr, 716 .pSampleMask = nullptr,
717 .alphaToCoverageEnable = VK_FALSE, 717 .alphaToCoverageEnable = key.state.alpha_to_coverage_enabled != 0 ? VK_TRUE : VK_FALSE,
718 .alphaToOneEnable = VK_FALSE, 718 .alphaToOneEnable = key.state.alpha_to_one_enabled != 0 ? VK_TRUE : VK_FALSE,
719 }; 719 };
720 const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{ 720 const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
721 .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, 721 .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
@@ -830,7 +830,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
830 */ 830 */
831 } 831 }
832 VkPipelineCreateFlags flags{}; 832 VkPipelineCreateFlags flags{};
833 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { 833 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
834 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; 834 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
835 } 835 }
836 pipeline = device.GetLogical().CreateGraphicsPipeline({ 836 pipeline = device.GetLogical().CreateGraphicsPipeline({
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 6bf577d25..1ed2967be 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -151,7 +151,7 @@ private:
151 vk::DescriptorSetLayout descriptor_set_layout; 151 vk::DescriptorSetLayout descriptor_set_layout;
152 DescriptorAllocator descriptor_allocator; 152 DescriptorAllocator descriptor_allocator;
153 vk::PipelineLayout pipeline_layout; 153 vk::PipelineLayout pipeline_layout;
154 vk::DescriptorUpdateTemplateKHR descriptor_update_template; 154 vk::DescriptorUpdateTemplate descriptor_update_template;
155 vk::Pipeline pipeline; 155 vk::Pipeline pipeline;
156 156
157 std::condition_variable build_condvar; 157 std::condition_variable build_condvar;
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index 4e81d3d28..8aa07ef9d 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -11,10 +11,10 @@
11namespace Vulkan { 11namespace Vulkan {
12 12
13MasterSemaphore::MasterSemaphore(const Device& device) { 13MasterSemaphore::MasterSemaphore(const Device& device) {
14 static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{ 14 static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{
15 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, 15 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
16 .pNext = nullptr, 16 .pNext = nullptr,
17 .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR, 17 .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
18 .initialValue = 0, 18 .initialValue = 0,
19 }; 19 };
20 static constexpr VkSemaphoreCreateInfo semaphore_ci{ 20 static constexpr VkSemaphoreCreateInfo semaphore_ci{
@@ -28,7 +28,7 @@ MasterSemaphore::MasterSemaphore(const Device& device) {
28 return; 28 return;
29 } 29 }
30 // Validation layers have a bug where they fail to track resource usage when using timeline 30 // Validation layers have a bug where they fail to track resource usage when using timeline
31 // semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have 31 // semaphores and synchronizing with GetSemaphoreCounterValue. To workaround this issue, have
32 // a separate thread waiting for each timeline semaphore value. 32 // a separate thread waiting for each timeline semaphore value.
33 debug_thread = std::jthread([this](std::stop_token stop_token) { 33 debug_thread = std::jthread([this](std::stop_token stop_token) {
34 u64 counter = 0; 34 u64 counter = 0;
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 362ed579a..689f02ea5 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -7,6 +7,7 @@
7#include <thread> 7#include <thread>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_thread.h"
10#include "video_core/vulkan_common/vulkan_wrapper.h" 11#include "video_core/vulkan_common/vulkan_wrapper.h"
11 12
12namespace Vulkan { 13namespace Vulkan {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index d4b0a542a..38a6b7488 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -46,6 +46,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache);
46namespace { 46namespace {
47using Shader::Backend::SPIRV::EmitSPIRV; 47using Shader::Backend::SPIRV::EmitSPIRV;
48using Shader::Maxwell::ConvertLegacyToGeneric; 48using Shader::Maxwell::ConvertLegacyToGeneric;
49using Shader::Maxwell::GenerateGeometryPassthrough;
49using Shader::Maxwell::MergeDualVertexPrograms; 50using Shader::Maxwell::MergeDualVertexPrograms;
50using Shader::Maxwell::TranslateProgram; 51using Shader::Maxwell::TranslateProgram;
51using VideoCommon::ComputeEnvironment; 52using VideoCommon::ComputeEnvironment;
@@ -53,13 +54,24 @@ using VideoCommon::FileEnvironment;
53using VideoCommon::GenericEnvironment; 54using VideoCommon::GenericEnvironment;
54using VideoCommon::GraphicsEnvironment; 55using VideoCommon::GraphicsEnvironment;
55 56
56constexpr u32 CACHE_VERSION = 7; 57constexpr u32 CACHE_VERSION = 8;
57 58
58template <typename Container> 59template <typename Container>
59auto MakeSpan(Container& container) { 60auto MakeSpan(Container& container) {
60 return std::span(container.data(), container.size()); 61 return std::span(container.data(), container.size());
61} 62}
62 63
64Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) {
65 switch (topology) {
66 case Maxwell::PrimitiveTopology::Points:
67 return Shader::OutputTopology::PointList;
68 case Maxwell::PrimitiveTopology::LineStrip:
69 return Shader::OutputTopology::LineStrip;
70 default:
71 return Shader::OutputTopology::TriangleStrip;
72 }
73}
74
63Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { 75Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) {
64 switch (comparison) { 76 switch (comparison) {
65 case Maxwell::ComparisonOp::Never_D3D: 77 case Maxwell::ComparisonOp::Never_D3D:
@@ -275,9 +287,9 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
275 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), 287 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"),
276 serialization_thread(1, "VkPipelineSerialization") { 288 serialization_thread(1, "VkPipelineSerialization") {
277 const auto& float_control{device.FloatControlProperties()}; 289 const auto& float_control{device.FloatControlProperties()};
278 const VkDriverIdKHR driver_id{device.GetDriverID()}; 290 const VkDriverId driver_id{device.GetDriverID()};
279 profile = Shader::Profile{ 291 profile = Shader::Profile{
280 .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, 292 .supported_spirv = device.SupportedSpirvVersion(),
281 .unified_descriptor_binding = true, 293 .unified_descriptor_binding = true,
282 .support_descriptor_aliasing = true, 294 .support_descriptor_aliasing = true,
283 .support_int8 = device.IsInt8Supported(), 295 .support_int8 = device.IsInt8Supported(),
@@ -285,10 +297,10 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
285 .support_int64 = device.IsShaderInt64Supported(), 297 .support_int64 = device.IsShaderInt64Supported(),
286 .support_vertex_instance_id = false, 298 .support_vertex_instance_id = false,
287 .support_float_controls = true, 299 .support_float_controls = true,
288 .support_separate_denorm_behavior = float_control.denormBehaviorIndependence == 300 .support_separate_denorm_behavior =
289 VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR, 301 float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
290 .support_separate_rounding_mode = 302 .support_separate_rounding_mode =
291 float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR, 303 float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
292 .support_fp16_denorm_preserve = float_control.shaderDenormPreserveFloat16 != VK_FALSE, 304 .support_fp16_denorm_preserve = float_control.shaderDenormPreserveFloat16 != VK_FALSE,
293 .support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE, 305 .support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE,
294 .support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE, 306 .support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE,
@@ -315,18 +327,19 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
315 .lower_left_origin_mode = false, 327 .lower_left_origin_mode = false,
316 .need_declared_frag_colors = false, 328 .need_declared_frag_colors = false,
317 329
318 .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR, 330 .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS,
319 .has_broken_unsigned_image_offsets = false, 331 .has_broken_unsigned_image_offsets = false,
320 .has_broken_signed_operations = false, 332 .has_broken_signed_operations = false,
321 .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR, 333 .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
322 .ignore_nan_fp_comparisons = false, 334 .ignore_nan_fp_comparisons = false,
323 }; 335 };
324 host_info = Shader::HostTranslateInfo{ 336 host_info = Shader::HostTranslateInfo{
325 .support_float16 = device.IsFloat16Supported(), 337 .support_float16 = device.IsFloat16Supported(),
326 .support_int64 = device.IsShaderInt64Supported(), 338 .support_int64 = device.IsShaderInt64Supported(),
327 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || 339 .needs_demote_reorder =
328 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 340 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE,
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
@@ -395,7 +408,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
395 std::unique_ptr<PipelineStatistics> statistics; 408 std::unique_ptr<PipelineStatistics> statistics;
396 } state; 409 } state;
397 410
398 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { 411 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
399 state.statistics = std::make_unique<PipelineStatistics>(device); 412 state.statistics = std::make_unique<PipelineStatistics>(device);
400 } 413 }
401 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { 414 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
@@ -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_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 4b15c0f85..929c8ece6 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -98,7 +98,7 @@ HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr<HostCounter> depend
98 query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} { 98 query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} {
99 const vk::Device* logical = &cache.GetDevice().GetLogical(); 99 const vk::Device* logical = &cache.GetDevice().GetLogical();
100 cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { 100 cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
101 logical->ResetQueryPoolEXT(query.first, query.second, 1); 101 logical->ResetQueryPool(query.first, query.second, 1);
102 cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT); 102 cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT);
103 }); 103 });
104} 104}
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
index dc21b7e69..91ad4bf57 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
@@ -12,7 +12,7 @@
12namespace Vulkan { 12namespace Vulkan {
13 13
14struct RenderPassKey { 14struct RenderPassKey {
15 auto operator<=>(const RenderPassKey&) const noexcept = default; 15 bool operator==(const RenderPassKey&) const noexcept = default;
16 16
17 std::array<VideoCore::Surface::PixelFormat, 8> color_formats; 17 std::array<VideoCore::Surface::PixelFormat, 8> color_formats;
18 VideoCore::Surface::PixelFormat depth_format; 18 VideoCore::Surface::PixelFormat depth_format;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 4a7b633b7..c2e53a5d5 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -145,7 +145,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) {
145 if (work_queue.empty()) { 145 if (work_queue.empty()) {
146 wait_cv.notify_all(); 146 wait_cv.notify_all();
147 } 147 }
148 work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); }); 148 Common::CondvarWait(work_cv, lock, stop_token, [&] { return !work_queue.empty(); });
149 if (stop_token.stop_requested()) { 149 if (stop_token.stop_requested()) {
150 continue; 150 continue;
151 } 151 }
@@ -194,8 +194,8 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
194 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 194 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
195 }; 195 };
196 196
197 const VkTimelineSemaphoreSubmitInfoKHR timeline_si{ 197 const VkTimelineSemaphoreSubmitInfo timeline_si{
198 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, 198 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
199 .pNext = nullptr, 199 .pNext = nullptr,
200 .waitSemaphoreValueCount = num_wait_semaphores, 200 .waitSemaphoreValueCount = num_wait_semaphores,
201 .pWaitSemaphoreValues = wait_values.data(), 201 .pWaitSemaphoreValues = wait_values.data(),
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 929216749..3858c506c 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -12,6 +12,7 @@
12 12
13#include "common/alignment.h" 13#include "common/alignment.h"
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/polyfill_thread.h"
15#include "video_core/renderer_vulkan/vk_master_semaphore.h" 16#include "video_core/renderer_vulkan/vk_master_semaphore.h"
16#include "video_core/vulkan_common/vulkan_wrapper.h" 17#include "video_core/vulkan_common/vulkan_wrapper.h"
17 18
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 706d9ba74..d7be417f5 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -7,6 +7,7 @@
7#include <vector> 7#include <vector>
8 8
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/polyfill_ranges.h"
10#include "common/settings.h" 11#include "common/settings.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h
index a4391202d..f3cc4c70b 100644
--- a/src/video_core/shader_cache.h
+++ b/src/video_core/shader_cache.h
@@ -12,6 +12,7 @@
12#include <vector> 12#include <vector>
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/polyfill_ranges.h"
15#include "video_core/control/channel_state_cache.h" 16#include "video_core/control/channel_state_cache.h"
16#include "video_core/rasterizer_interface.h" 17#include "video_core/rasterizer_interface.h"
17#include "video_core/shader_environment.h" 18#include "video_core/shader_environment.h"
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index f24f320b6..958810747 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -15,6 +15,7 @@
15#include "common/fs/fs.h" 15#include "common/fs/fs.h"
16#include "common/fs/path_util.h" 16#include "common/fs/path_util.h"
17#include "common/logging/log.h" 17#include "common/logging/log.h"
18#include "common/polyfill_ranges.h"
18#include "shader_recompiler/environment.h" 19#include "shader_recompiler/environment.h"
19#include "video_core/engines/kepler_compute.h" 20#include "video_core/engines/kepler_compute.h"
20#include "video_core/memory_manager.h" 21#include "video_core/memory_manager.h"
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index bb55b029f..1342fab1e 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -10,12 +10,12 @@
10#include <memory> 10#include <memory>
11#include <optional> 11#include <optional>
12#include <span> 12#include <span>
13#include <stop_token>
14#include <type_traits> 13#include <type_traits>
15#include <unordered_map> 14#include <unordered_map>
16#include <vector> 15#include <vector>
17 16
18#include "common/common_types.h" 17#include "common/common_types.h"
18#include "common/polyfill_thread.h"
19#include "common/unique_function.h" 19#include "common/unique_function.h"
20#include "shader_recompiler/environment.h" 20#include "shader_recompiler/environment.h"
21#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp
index ee4f2d406..418890126 100644
--- a/src/video_core/texture_cache/formatter.cpp
+++ b/src/video_core/texture_cache/formatter.cpp
@@ -4,6 +4,7 @@
4#include <algorithm> 4#include <algorithm>
5#include <string> 5#include <string>
6 6
7#include "common/polyfill_ranges.h"
7#include "video_core/texture_cache/formatter.h" 8#include "video_core/texture_cache/formatter.h"
8#include "video_core/texture_cache/image_base.h" 9#include "video_core/texture_cache/image_base.h"
9#include "video_core/texture_cache/image_info.h" 10#include "video_core/texture_cache/image_info.h"
diff --git a/src/video_core/texture_cache/render_targets.h b/src/video_core/texture_cache/render_targets.h
index 1efbd6507..0829d773a 100644
--- a/src/video_core/texture_cache/render_targets.h
+++ b/src/video_core/texture_cache/render_targets.h
@@ -13,7 +13,7 @@ namespace VideoCommon {
13 13
14/// Framebuffer properties used to lookup a framebuffer 14/// Framebuffer properties used to lookup a framebuffer
15struct RenderTargets { 15struct RenderTargets {
16 constexpr auto operator<=>(const RenderTargets&) const noexcept = default; 16 constexpr bool operator==(const RenderTargets&) const noexcept = default;
17 17
18 constexpr bool Contains(std::span<const ImageViewId> elements) const noexcept { 18 constexpr bool Contains(std::span<const ImageViewId> elements) const noexcept {
19 const auto contains = [elements](ImageViewId item) { 19 const auto contains = [elements](ImageViewId item) {
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index 46e8a86e6..1e2aad76a 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.h
@@ -12,6 +12,7 @@
12 12
13#include "common/assert.h" 13#include "common/assert.h"
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/polyfill_ranges.h"
15 16
16namespace VideoCommon { 17namespace VideoCommon {
17 18
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 9db7195bf..587339a31 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -16,6 +16,7 @@
16#include "common/hash.h" 16#include "common/hash.h"
17#include "common/literals.h" 17#include "common/literals.h"
18#include "common/lru_cache.h" 18#include "common/lru_cache.h"
19#include "common/polyfill_ranges.h"
19#include "video_core/compatible_formats.h" 20#include "video_core/compatible_formats.h"
20#include "video_core/control/channel_state_cache.h" 21#include "video_core/control/channel_state_cache.h"
21#include "video_core/delayed_destruction_ring.h" 22#include "video_core/delayed_destruction_ring.h"
@@ -60,8 +61,6 @@ public:
60 TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept; 61 TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept;
61 TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete; 62 TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete;
62 TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete; 63 TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete;
63 TextureCacheChannelInfo(TextureCacheChannelInfo&& other) noexcept = default;
64 TextureCacheChannelInfo& operator=(TextureCacheChannelInfo&& other) noexcept = default;
65 64
66 DescriptorTable<TICEntry> graphics_image_table{gpu_memory}; 65 DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
67 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory}; 66 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index 69a32819a..e8d7c7863 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -15,6 +15,7 @@
15 15
16#include "common/alignment.h" 16#include "common/alignment.h"
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "common/polyfill_ranges.h"
18#include "common/thread_worker.h" 19#include "common/thread_worker.h"
19#include "video_core/textures/astc.h" 20#include "video_core/textures/astc.h"
20 21
diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp
index 45071185a..155599316 100644
--- a/src/video_core/transform_feedback.cpp
+++ b/src/video_core/transform_feedback.cpp
@@ -7,6 +7,7 @@
7 7
8#include "common/alignment.h" 8#include "common/alignment.h"
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/polyfill_ranges.h"
10#include "shader_recompiler/shader_info.h" 11#include "shader_recompiler/shader_info.h"
11#include "video_core/transform_feedback.h" 12#include "video_core/transform_feedback.h"
12 13
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 04ac4af11..fedb4a7bb 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -7,6 +7,7 @@
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "video_core/renderer_base.h" 9#include "video_core/renderer_base.h"
10#include "video_core/renderer_null/renderer_null.h"
10#include "video_core/renderer_opengl/renderer_opengl.h" 11#include "video_core/renderer_opengl/renderer_opengl.h"
11#include "video_core/renderer_vulkan/renderer_vulkan.h" 12#include "video_core/renderer_vulkan/renderer_vulkan.h"
12#include "video_core/video_core.h" 13#include "video_core/video_core.h"
@@ -26,6 +27,9 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
26 case Settings::RendererBackend::Vulkan: 27 case Settings::RendererBackend::Vulkan:
27 return std::make_unique<Vulkan::RendererVulkan>(telemetry_session, emu_window, cpu_memory, 28 return std::make_unique<Vulkan::RendererVulkan>(telemetry_session, emu_window, cpu_memory,
28 gpu, std::move(context)); 29 gpu, std::move(context));
30 case Settings::RendererBackend::Null:
31 return std::make_unique<Null::RendererNull>(emu_window, cpu_memory, gpu,
32 std::move(context));
29 default: 33 default:
30 return nullptr; 34 return nullptr;
31 } 35 }
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index cdda06813..6a2ad4b1d 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -12,6 +12,7 @@
12 12
13#include "common/assert.h" 13#include "common/assert.h"
14#include "common/literals.h" 14#include "common/literals.h"
15#include "common/polyfill_ranges.h"
15#include "common/settings.h" 16#include "common/settings.h"
16#include "video_core/vulkan_common/nsight_aftermath_tracker.h" 17#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
17#include "video_core/vulkan_common/vulkan_device.h" 18#include "video_core/vulkan_common/vulkan_device.h"
@@ -74,23 +75,8 @@ enum class NvidiaArchitecture {
74}; 75};
75 76
76constexpr std::array REQUIRED_EXTENSIONS{ 77constexpr std::array REQUIRED_EXTENSIONS{
77 VK_KHR_MAINTENANCE1_EXTENSION_NAME,
78 VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME,
79 VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME,
80 VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
81 VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
82 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
83 VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
84 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
85 VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
86 VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
87 VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME,
88 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, 78 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
89 VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
90 VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
91 VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, 79 VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
92 VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
93 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME,
94#ifdef _WIN32 80#ifdef _WIN32
95 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, 81 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
96#endif 82#endif
@@ -99,6 +85,19 @@ constexpr std::array REQUIRED_EXTENSIONS{
99#endif 85#endif
100}; 86};
101 87
88constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_2{
89 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
90 VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
91 VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
92 VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
93 VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
94 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
95};
96
97constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_3{
98 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME,
99};
100
102template <typename T> 101template <typename T>
103void SetNext(void**& next, T& data) { 102void SetNext(void**& next, T& data) {
104 *next = &data; 103 *next = &data;
@@ -308,10 +307,10 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
308 VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; 307 VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{};
309 shading_rate_props.sType = 308 shading_rate_props.sType =
310 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; 309 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
311 VkPhysicalDeviceProperties2KHR physical_properties{}; 310 VkPhysicalDeviceProperties2 physical_properties{};
312 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; 311 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
313 physical_properties.pNext = &shading_rate_props; 312 physical_properties.pNext = &shading_rate_props;
314 physical.GetProperties2KHR(physical_properties); 313 physical.GetProperties2(physical_properties);
315 if (shading_rate_props.primitiveFragmentShadingRateWithMultipleViewports) { 314 if (shading_rate_props.primitiveFragmentShadingRateWithMultipleViewports) {
316 // Only Ampere and newer support this feature 315 // Only Ampere and newer support this feature
317 return NvidiaArchitecture::AmpereOrNewer; 316 return NvidiaArchitecture::AmpereOrNewer;
@@ -327,7 +326,8 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
327Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, 326Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
328 const vk::InstanceDispatch& dld_) 327 const vk::InstanceDispatch& dld_)
329 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, 328 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()},
330 supported_extensions{GetSupportedExtensions(physical)}, 329 instance_version{properties.apiVersion}, supported_extensions{GetSupportedExtensions(
330 physical)},
331 format_properties(GetFormatProperties(physical)) { 331 format_properties(GetFormatProperties(physical)) {
332 CheckSuitability(surface != nullptr); 332 CheckSuitability(surface != nullptr);
333 SetupFamilies(surface); 333 SetupFamilies(surface);
@@ -401,15 +401,15 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
401 const void* first_next = &features2; 401 const void* first_next = &features2;
402 void** next = &features2.pNext; 402 void** next = &features2.pNext;
403 403
404 VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore{ 404 VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{
405 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR, 405 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
406 .pNext = nullptr, 406 .pNext = nullptr,
407 .timelineSemaphore = true, 407 .timelineSemaphore = true,
408 }; 408 };
409 SetNext(next, timeline_semaphore); 409 SetNext(next, timeline_semaphore);
410 410
411 VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{ 411 VkPhysicalDevice16BitStorageFeatures bit16_storage{
412 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, 412 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES,
413 .pNext = nullptr, 413 .pNext = nullptr,
414 .storageBuffer16BitAccess = true, 414 .storageBuffer16BitAccess = true,
415 .uniformAndStorageBuffer16BitAccess = true, 415 .uniformAndStorageBuffer16BitAccess = true,
@@ -418,8 +418,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
418 }; 418 };
419 SetNext(next, bit16_storage); 419 SetNext(next, bit16_storage);
420 420
421 VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage{ 421 VkPhysicalDevice8BitStorageFeatures bit8_storage{
422 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR, 422 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES,
423 .pNext = nullptr, 423 .pNext = nullptr,
424 .storageBuffer8BitAccess = false, 424 .storageBuffer8BitAccess = false,
425 .uniformAndStorageBuffer8BitAccess = true, 425 .uniformAndStorageBuffer8BitAccess = true,
@@ -436,32 +436,39 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
436 }; 436 };
437 SetNext(next, robustness2); 437 SetNext(next, robustness2);
438 438
439 VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{ 439 VkPhysicalDeviceHostQueryResetFeatures host_query_reset{
440 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT, 440 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES,
441 .pNext = nullptr, 441 .pNext = nullptr,
442 .hostQueryReset = true, 442 .hostQueryReset = true,
443 }; 443 };
444 SetNext(next, host_query_reset); 444 SetNext(next, host_query_reset);
445 445
446 VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{ 446 VkPhysicalDeviceVariablePointerFeatures variable_pointers{
447 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR, 447 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES,
448 .pNext = nullptr, 448 .pNext = nullptr,
449 .variablePointersStorageBuffer = VK_TRUE, 449 .variablePointersStorageBuffer = VK_TRUE,
450 .variablePointers = VK_TRUE, 450 .variablePointers = VK_TRUE,
451 }; 451 };
452 SetNext(next, variable_pointers); 452 SetNext(next, variable_pointers);
453 453
454 VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{ 454 VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{
455 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT, 455 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES,
456 .pNext = nullptr, 456 .pNext = nullptr,
457 .shaderDemoteToHelperInvocation = true, 457 .shaderDemoteToHelperInvocation = true,
458 }; 458 };
459 SetNext(next, demote); 459 SetNext(next, demote);
460 460
461 VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8; 461 VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{
462 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES,
463 .pNext = nullptr,
464 .shaderDrawParameters = true,
465 };
466 SetNext(next, draw_parameters);
467
468 VkPhysicalDeviceShaderFloat16Int8Features float16_int8;
462 if (is_int8_supported || is_float16_supported) { 469 if (is_int8_supported || is_float16_supported) {
463 float16_int8 = { 470 float16_int8 = {
464 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR, 471 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES,
465 .pNext = nullptr, 472 .pNext = nullptr,
466 .shaderFloat16 = is_float16_supported, 473 .shaderFloat16 = is_float16_supported,
467 .shaderInt8 = is_int8_supported, 474 .shaderInt8 = is_int8_supported,
@@ -487,10 +494,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
487 LOG_INFO(Render_Vulkan, "Device doesn't support passthrough geometry shaders"); 494 LOG_INFO(Render_Vulkan, "Device doesn't support passthrough geometry shaders");
488 } 495 }
489 496
490 VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR std430_layout; 497 VkPhysicalDeviceUniformBufferStandardLayoutFeatures std430_layout;
491 if (khr_uniform_buffer_standard_layout) { 498 if (khr_uniform_buffer_standard_layout) {
492 std430_layout = { 499 std430_layout = {
493 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR, 500 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES,
494 .pNext = nullptr, 501 .pNext = nullptr,
495 .uniformBufferStandardLayout = true, 502 .uniformBufferStandardLayout = true,
496 }; 503 };
@@ -608,10 +615,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
608 LOG_INFO(Render_Vulkan, "Device doesn't support vertex input dynamic state"); 615 LOG_INFO(Render_Vulkan, "Device doesn't support vertex input dynamic state");
609 } 616 }
610 617
611 VkPhysicalDeviceShaderAtomicInt64FeaturesKHR atomic_int64; 618 VkPhysicalDeviceShaderAtomicInt64Features atomic_int64;
612 if (ext_shader_atomic_int64) { 619 if (ext_shader_atomic_int64) {
613 atomic_int64 = { 620 atomic_int64 = {
614 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR, 621 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES,
615 .pNext = nullptr, 622 .pNext = nullptr,
616 .shaderBufferInt64Atomics = VK_TRUE, 623 .shaderBufferInt64Atomics = VK_TRUE,
617 .shaderSharedInt64Atomics = VK_TRUE, 624 .shaderSharedInt64Atomics = VK_TRUE,
@@ -896,28 +903,51 @@ std::string Device::GetDriverName() const {
896 } 903 }
897} 904}
898 905
906static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) {
907 std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()};
908
909 if (available_version < VK_API_VERSION_1_2) {
910 extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_2.begin(),
911 REQUIRED_EXTENSIONS_BEFORE_1_2.end());
912 }
913
914 if (available_version < VK_API_VERSION_1_3) {
915 extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_3.begin(),
916 REQUIRED_EXTENSIONS_BEFORE_1_3.end());
917 }
918
919 return extensions;
920}
921
899void Device::CheckSuitability(bool requires_swapchain) const { 922void Device::CheckSuitability(bool requires_swapchain) const {
900 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; 923 std::vector<const char*> required_extensions =
901 bool has_swapchain = false; 924 ExtensionsRequiredForInstanceVersion(instance_version);
902 for (const VkExtensionProperties& property : physical.EnumerateDeviceExtensionProperties()) { 925 std::vector<const char*> available_extensions;
903 const std::string_view name{property.extensionName}; 926
904 for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { 927 if (requires_swapchain) {
905 if (available_extensions[i]) { 928 required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
906 continue;
907 }
908 available_extensions[i] = name == REQUIRED_EXTENSIONS[i];
909 }
910 has_swapchain = has_swapchain || name == VK_KHR_SWAPCHAIN_EXTENSION_NAME;
911 } 929 }
912 for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { 930
913 if (available_extensions[i]) { 931 auto extension_properties = physical.EnumerateDeviceExtensionProperties();
914 continue; 932
933 for (const VkExtensionProperties& property : extension_properties) {
934 available_extensions.push_back(property.extensionName);
935 }
936
937 bool has_all_required_extensions = true;
938 for (const char* requirement_name : required_extensions) {
939 const bool found =
940 std::ranges::any_of(available_extensions, [&](const char* extension_name) {
941 return std::strcmp(requirement_name, extension_name) == 0;
942 });
943
944 if (!found) {
945 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", requirement_name);
946 has_all_required_extensions = false;
915 } 947 }
916 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]);
917 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
918 } 948 }
919 if (requires_swapchain && !has_swapchain) { 949
920 LOG_ERROR(Render_Vulkan, "Missing required extension: VK_KHR_swapchain"); 950 if (!has_all_required_extensions) {
921 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); 951 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
922 } 952 }
923 953
@@ -940,27 +970,46 @@ void Device::CheckSuitability(bool requires_swapchain) const {
940 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); 970 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
941 } 971 }
942 } 972 }
943 VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{}; 973 VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{};
944 demote.sType = 974 demote.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES;
945 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT;
946 demote.pNext = nullptr; 975 demote.pNext = nullptr;
947 976
948 VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{}; 977 VkPhysicalDeviceVariablePointerFeatures variable_pointers{};
949 variable_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR; 978 variable_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES;
950 variable_pointers.pNext = &demote; 979 variable_pointers.pNext = &demote;
951 980
952 VkPhysicalDeviceRobustness2FeaturesEXT robustness2{}; 981 VkPhysicalDeviceRobustness2FeaturesEXT robustness2{};
953 robustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT; 982 robustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
954 robustness2.pNext = &variable_pointers; 983 robustness2.pNext = &variable_pointers;
955 984
956 VkPhysicalDeviceFeatures2KHR features2{}; 985 VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{};
986 timeline_semaphore.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES;
987 timeline_semaphore.pNext = &robustness2;
988
989 VkPhysicalDevice16BitStorageFeatures bit16_storage{};
990 bit16_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
991 bit16_storage.pNext = &timeline_semaphore;
992
993 VkPhysicalDevice8BitStorageFeatures bit8_storage{};
994 bit8_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES;
995 bit8_storage.pNext = &bit16_storage;
996
997 VkPhysicalDeviceHostQueryResetFeatures host_query_reset{};
998 host_query_reset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES;
999 host_query_reset.pNext = &bit8_storage;
1000
1001 VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{};
1002 draw_parameters.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
1003 draw_parameters.pNext = &host_query_reset;
1004
1005 VkPhysicalDeviceFeatures2 features2{};
957 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; 1006 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
958 features2.pNext = &robustness2; 1007 features2.pNext = &draw_parameters;
959 1008
960 physical.GetFeatures2KHR(features2); 1009 physical.GetFeatures2(features2);
961 1010
962 const VkPhysicalDeviceFeatures& features{features2.features}; 1011 const VkPhysicalDeviceFeatures& features{features2.features};
963 const std::array feature_report{ 1012 std::array feature_report{
964 std::make_pair(features.robustBufferAccess, "robustBufferAccess"), 1013 std::make_pair(features.robustBufferAccess, "robustBufferAccess"),
965 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), 1014 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
966 std::make_pair(features.imageCubeArray, "imageCubeArray"), 1015 std::make_pair(features.imageCubeArray, "imageCubeArray"),
@@ -976,6 +1025,7 @@ void Device::CheckSuitability(bool requires_swapchain) const {
976 std::make_pair(features.tessellationShader, "tessellationShader"), 1025 std::make_pair(features.tessellationShader, "tessellationShader"),
977 std::make_pair(features.sampleRateShading, "sampleRateShading"), 1026 std::make_pair(features.sampleRateShading, "sampleRateShading"),
978 std::make_pair(features.dualSrcBlend, "dualSrcBlend"), 1027 std::make_pair(features.dualSrcBlend, "dualSrcBlend"),
1028 std::make_pair(features.logicOp, "logicOp"),
979 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), 1029 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),
980 std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), 1030 std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"),
981 std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), 1031 std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"),
@@ -983,27 +1033,38 @@ void Device::CheckSuitability(bool requires_swapchain) const {
983 "shaderStorageImageWriteWithoutFormat"), 1033 "shaderStorageImageWriteWithoutFormat"),
984 std::make_pair(features.shaderClipDistance, "shaderClipDistance"), 1034 std::make_pair(features.shaderClipDistance, "shaderClipDistance"),
985 std::make_pair(features.shaderCullDistance, "shaderCullDistance"), 1035 std::make_pair(features.shaderCullDistance, "shaderCullDistance"),
986 std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"),
987 std::make_pair(variable_pointers.variablePointers, "variablePointers"), 1036 std::make_pair(variable_pointers.variablePointers, "variablePointers"),
988 std::make_pair(variable_pointers.variablePointersStorageBuffer, 1037 std::make_pair(variable_pointers.variablePointersStorageBuffer,
989 "variablePointersStorageBuffer"), 1038 "variablePointersStorageBuffer"),
990 std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"), 1039 std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"),
991 std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"), 1040 std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"),
992 std::make_pair(robustness2.nullDescriptor, "nullDescriptor"), 1041 std::make_pair(robustness2.nullDescriptor, "nullDescriptor"),
1042 std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"),
1043 std::make_pair(timeline_semaphore.timelineSemaphore, "timelineSemaphore"),
1044 std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"),
1045 std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess,
1046 "uniformAndStorageBuffer16BitAccess"),
1047 std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess,
1048 "uniformAndStorageBuffer8BitAccess"),
1049 std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"),
1050 std::make_pair(draw_parameters.shaderDrawParameters, "shaderDrawParameters"),
993 }; 1051 };
1052
1053 bool has_all_required_features = true;
994 for (const auto& [is_supported, name] : feature_report) { 1054 for (const auto& [is_supported, name] : feature_report) {
995 if (is_supported) { 1055 if (!is_supported) {
996 continue; 1056 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name);
1057 has_all_required_features = false;
997 } 1058 }
998 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); 1059 }
1060
1061 if (!has_all_required_features) {
999 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); 1062 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
1000 } 1063 }
1001} 1064}
1002 1065
1003std::vector<const char*> Device::LoadExtensions(bool requires_surface) { 1066std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1004 std::vector<const char*> extensions; 1067 std::vector<const char*> extensions = ExtensionsRequiredForInstanceVersion(instance_version);
1005 extensions.reserve(8 + REQUIRED_EXTENSIONS.size());
1006 extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end());
1007 if (requires_surface) { 1068 if (requires_surface) {
1008 extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); 1069 extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
1009 } 1070 }
@@ -1079,37 +1140,37 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1079 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false); 1140 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false);
1080 } 1141 }
1081 } 1142 }
1082 VkPhysicalDeviceFeatures2KHR features{}; 1143 VkPhysicalDeviceFeatures2 features{};
1083 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; 1144 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1084 1145
1085 VkPhysicalDeviceProperties2KHR physical_properties{}; 1146 VkPhysicalDeviceProperties2 physical_properties{};
1086 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; 1147 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
1087 1148
1088 if (has_khr_shader_float16_int8) { 1149 if (has_khr_shader_float16_int8) {
1089 VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8_features; 1150 VkPhysicalDeviceShaderFloat16Int8Features float16_int8_features;
1090 float16_int8_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR; 1151 float16_int8_features.sType =
1152 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
1091 float16_int8_features.pNext = nullptr; 1153 float16_int8_features.pNext = nullptr;
1092 features.pNext = &float16_int8_features; 1154 features.pNext = &float16_int8_features;
1093 1155
1094 physical.GetFeatures2KHR(features); 1156 physical.GetFeatures2(features);
1095 is_float16_supported = float16_int8_features.shaderFloat16; 1157 is_float16_supported = float16_int8_features.shaderFloat16;
1096 is_int8_supported = float16_int8_features.shaderInt8; 1158 is_int8_supported = float16_int8_features.shaderInt8;
1097 extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); 1159 extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
1098 } 1160 }
1099 if (has_ext_subgroup_size_control) { 1161 if (has_ext_subgroup_size_control) {
1100 VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroup_features; 1162 VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_features;
1101 subgroup_features.sType = 1163 subgroup_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES;
1102 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT;
1103 subgroup_features.pNext = nullptr; 1164 subgroup_features.pNext = nullptr;
1104 features.pNext = &subgroup_features; 1165 features.pNext = &subgroup_features;
1105 physical.GetFeatures2KHR(features); 1166 physical.GetFeatures2(features);
1106 1167
1107 VkPhysicalDeviceSubgroupSizeControlPropertiesEXT subgroup_properties; 1168 VkPhysicalDeviceSubgroupSizeControlProperties subgroup_properties;
1108 subgroup_properties.sType = 1169 subgroup_properties.sType =
1109 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT; 1170 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES;
1110 subgroup_properties.pNext = nullptr; 1171 subgroup_properties.pNext = nullptr;
1111 physical_properties.pNext = &subgroup_properties; 1172 physical_properties.pNext = &subgroup_properties;
1112 physical.GetProperties2KHR(physical_properties); 1173 physical.GetProperties2(physical_properties);
1113 1174
1114 is_warp_potentially_bigger = subgroup_properties.maxSubgroupSize > GuestWarpSize; 1175 is_warp_potentially_bigger = subgroup_properties.maxSubgroupSize > GuestWarpSize;
1115 1176
@@ -1128,7 +1189,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1128 provoking_vertex.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT; 1189 provoking_vertex.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT;
1129 provoking_vertex.pNext = nullptr; 1190 provoking_vertex.pNext = nullptr;
1130 features.pNext = &provoking_vertex; 1191 features.pNext = &provoking_vertex;
1131 physical.GetFeatures2KHR(features); 1192 physical.GetFeatures2(features);
1132 1193
1133 if (provoking_vertex.provokingVertexLast && 1194 if (provoking_vertex.provokingVertexLast &&
1134 provoking_vertex.transformFeedbackPreservesProvokingVertex) { 1195 provoking_vertex.transformFeedbackPreservesProvokingVertex) {
@@ -1142,7 +1203,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1142 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT; 1203 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT;
1143 vertex_input.pNext = nullptr; 1204 vertex_input.pNext = nullptr;
1144 features.pNext = &vertex_input; 1205 features.pNext = &vertex_input;
1145 physical.GetFeatures2KHR(features); 1206 physical.GetFeatures2(features);
1146 1207
1147 if (vertex_input.vertexInputDynamicState) { 1208 if (vertex_input.vertexInputDynamicState) {
1148 extensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 1209 extensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
@@ -1154,7 +1215,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1154 atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES; 1215 atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES;
1155 atomic_int64.pNext = nullptr; 1216 atomic_int64.pNext = nullptr;
1156 features.pNext = &atomic_int64; 1217 features.pNext = &atomic_int64;
1157 physical.GetFeatures2KHR(features); 1218 physical.GetFeatures2(features);
1158 1219
1159 if (atomic_int64.shaderBufferInt64Atomics && atomic_int64.shaderSharedInt64Atomics) { 1220 if (atomic_int64.shaderBufferInt64Atomics && atomic_int64.shaderSharedInt64Atomics) {
1160 extensions.push_back(VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); 1221 extensions.push_back(VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
@@ -1166,13 +1227,13 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1166 tfb_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT; 1227 tfb_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
1167 tfb_features.pNext = nullptr; 1228 tfb_features.pNext = nullptr;
1168 features.pNext = &tfb_features; 1229 features.pNext = &tfb_features;
1169 physical.GetFeatures2KHR(features); 1230 physical.GetFeatures2(features);
1170 1231
1171 VkPhysicalDeviceTransformFeedbackPropertiesEXT tfb_properties; 1232 VkPhysicalDeviceTransformFeedbackPropertiesEXT tfb_properties;
1172 tfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; 1233 tfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
1173 tfb_properties.pNext = nullptr; 1234 tfb_properties.pNext = nullptr;
1174 physical_properties.pNext = &tfb_properties; 1235 physical_properties.pNext = &tfb_properties;
1175 physical.GetProperties2KHR(physical_properties); 1236 physical.GetProperties2(physical_properties);
1176 1237
1177 if (tfb_features.transformFeedback && tfb_features.geometryStreams && 1238 if (tfb_features.transformFeedback && tfb_features.geometryStreams &&
1178 tfb_properties.maxTransformFeedbackStreams >= 4 && 1239 tfb_properties.maxTransformFeedbackStreams >= 4 &&
@@ -1187,7 +1248,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1187 border_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT; 1248 border_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
1188 border_features.pNext = nullptr; 1249 border_features.pNext = nullptr;
1189 features.pNext = &border_features; 1250 features.pNext = &border_features;
1190 physical.GetFeatures2KHR(features); 1251 physical.GetFeatures2(features);
1191 1252
1192 if (border_features.customBorderColors && border_features.customBorderColorWithoutFormat) { 1253 if (border_features.customBorderColors && border_features.customBorderColorWithoutFormat) {
1193 extensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); 1254 extensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
@@ -1200,7 +1261,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1200 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; 1261 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
1201 extended_dynamic_state.pNext = nullptr; 1262 extended_dynamic_state.pNext = nullptr;
1202 features.pNext = &extended_dynamic_state; 1263 features.pNext = &extended_dynamic_state;
1203 physical.GetFeatures2KHR(features); 1264 physical.GetFeatures2(features);
1204 1265
1205 if (extended_dynamic_state.extendedDynamicState) { 1266 if (extended_dynamic_state.extendedDynamicState) {
1206 extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 1267 extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
@@ -1212,7 +1273,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1212 line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT; 1273 line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT;
1213 line_raster.pNext = nullptr; 1274 line_raster.pNext = nullptr;
1214 features.pNext = &line_raster; 1275 features.pNext = &line_raster;
1215 physical.GetFeatures2KHR(features); 1276 physical.GetFeatures2(features);
1216 if (line_raster.rectangularLines && line_raster.smoothLines) { 1277 if (line_raster.rectangularLines && line_raster.smoothLines) {
1217 extensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME); 1278 extensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
1218 ext_line_rasterization = true; 1279 ext_line_rasterization = true;
@@ -1224,7 +1285,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1224 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR; 1285 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR;
1225 layout.pNext = nullptr; 1286 layout.pNext = nullptr;
1226 features.pNext = &layout; 1287 features.pNext = &layout;
1227 physical.GetFeatures2KHR(features); 1288 physical.GetFeatures2(features);
1228 1289
1229 if (layout.workgroupMemoryExplicitLayout && 1290 if (layout.workgroupMemoryExplicitLayout &&
1230 layout.workgroupMemoryExplicitLayout8BitAccess && 1291 layout.workgroupMemoryExplicitLayout8BitAccess &&
@@ -1240,7 +1301,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1240 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR; 1301 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR;
1241 executable_properties.pNext = nullptr; 1302 executable_properties.pNext = nullptr;
1242 features.pNext = &executable_properties; 1303 features.pNext = &executable_properties;
1243 physical.GetFeatures2KHR(features); 1304 physical.GetFeatures2(features);
1244 1305
1245 if (executable_properties.pipelineExecutableInfo) { 1306 if (executable_properties.pipelineExecutableInfo) {
1246 extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); 1307 extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
@@ -1253,7 +1314,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1253 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT; 1314 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
1254 primitive_topology_list_restart.pNext = nullptr; 1315 primitive_topology_list_restart.pNext = nullptr;
1255 features.pNext = &primitive_topology_list_restart; 1316 features.pNext = &primitive_topology_list_restart;
1256 physical.GetFeatures2KHR(features); 1317 physical.GetFeatures2(features);
1257 1318
1258 is_topology_list_restart_supported = 1319 is_topology_list_restart_supported =
1259 primitive_topology_list_restart.primitiveTopologyListRestart; 1320 primitive_topology_list_restart.primitiveTopologyListRestart;
@@ -1271,7 +1332,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1271 push_descriptor.pNext = nullptr; 1332 push_descriptor.pNext = nullptr;
1272 1333
1273 physical_properties.pNext = &push_descriptor; 1334 physical_properties.pNext = &push_descriptor;
1274 physical.GetProperties2KHR(physical_properties); 1335 physical.GetProperties2(physical_properties);
1275 1336
1276 max_push_descriptors = push_descriptor.maxPushDescriptors; 1337 max_push_descriptors = push_descriptor.maxPushDescriptors;
1277 } 1338 }
@@ -1322,18 +1383,18 @@ void Device::SetupFeatures() {
1322} 1383}
1323 1384
1324void Device::SetupProperties() { 1385void Device::SetupProperties() {
1325 float_controls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR; 1386 float_controls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES;
1326 1387
1327 VkPhysicalDeviceProperties2KHR properties2{}; 1388 VkPhysicalDeviceProperties2KHR properties2{};
1328 properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; 1389 properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
1329 properties2.pNext = &float_controls; 1390 properties2.pNext = &float_controls;
1330 1391
1331 physical.GetProperties2KHR(properties2); 1392 physical.GetProperties2(properties2);
1332} 1393}
1333 1394
1334void Device::CollectTelemetryParameters() { 1395void Device::CollectTelemetryParameters() {
1335 VkPhysicalDeviceDriverPropertiesKHR driver{ 1396 VkPhysicalDeviceDriverProperties driver{
1336 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR, 1397 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
1337 .pNext = nullptr, 1398 .pNext = nullptr,
1338 .driverID = {}, 1399 .driverID = {},
1339 .driverName = {}, 1400 .driverName = {},
@@ -1341,12 +1402,12 @@ void Device::CollectTelemetryParameters() {
1341 .conformanceVersion = {}, 1402 .conformanceVersion = {},
1342 }; 1403 };
1343 1404
1344 VkPhysicalDeviceProperties2KHR device_properties{ 1405 VkPhysicalDeviceProperties2 device_properties{
1345 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, 1406 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
1346 .pNext = &driver, 1407 .pNext = &driver,
1347 .properties = {}, 1408 .properties = {},
1348 }; 1409 };
1349 physical.GetProperties2KHR(device_properties); 1410 physical.GetProperties2(device_properties);
1350 1411
1351 driver_id = driver.driverID; 1412 driver_id = driver.driverID;
1352 vendor_name = driver.driverName; 1413 vendor_name = driver.driverName;
@@ -1402,23 +1463,10 @@ void Device::CollectToolingInfo() {
1402 if (!ext_tooling_info) { 1463 if (!ext_tooling_info) {
1403 return; 1464 return;
1404 } 1465 }
1405 const auto vkGetPhysicalDeviceToolPropertiesEXT = 1466 auto tools{physical.GetPhysicalDeviceToolProperties()};
1406 reinterpret_cast<PFN_vkGetPhysicalDeviceToolPropertiesEXT>( 1467 for (const VkPhysicalDeviceToolProperties& tool : tools) {
1407 dld.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceToolPropertiesEXT"));
1408 if (!vkGetPhysicalDeviceToolPropertiesEXT) {
1409 return;
1410 }
1411 u32 tool_count = 0;
1412 if (vkGetPhysicalDeviceToolPropertiesEXT(physical, &tool_count, nullptr) != VK_SUCCESS) {
1413 return;
1414 }
1415 std::vector<VkPhysicalDeviceToolPropertiesEXT> tools(tool_count);
1416 if (vkGetPhysicalDeviceToolPropertiesEXT(physical, &tool_count, tools.data()) != VK_SUCCESS) {
1417 return;
1418 }
1419 for (const VkPhysicalDeviceToolPropertiesEXT& tool : tools) {
1420 const std::string_view name = tool.name; 1468 const std::string_view name = tool.name;
1421 LOG_INFO(Render_Vulkan, "{}", name); 1469 LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name);
1422 has_renderdoc = has_renderdoc || name == "RenderDoc"; 1470 has_renderdoc = has_renderdoc || name == "RenderDoc";
1423 has_nsight_graphics = has_nsight_graphics || name == "NVIDIA Nsight Graphics"; 1471 has_nsight_graphics = has_nsight_graphics || name == "NVIDIA Nsight Graphics";
1424 } 1472 }
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index d7cc6c593..db802437c 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -211,18 +211,13 @@ public:
211 return khr_uniform_buffer_standard_layout; 211 return khr_uniform_buffer_standard_layout;
212 } 212 }
213 213
214 /// Returns true if the device supports VK_KHR_spirv_1_4.
215 bool IsKhrSpirv1_4Supported() const {
216 return khr_spirv_1_4;
217 }
218
219 /// Returns true if the device supports VK_KHR_push_descriptor. 214 /// Returns true if the device supports VK_KHR_push_descriptor.
220 bool IsKhrPushDescriptorSupported() const { 215 bool IsKhrPushDescriptorSupported() const {
221 return khr_push_descriptor; 216 return khr_push_descriptor;
222 } 217 }
223 218
224 /// Returns true if VK_KHR_pipeline_executable_properties is enabled. 219 /// Returns true if VK_KHR_pipeline_executable_properties is enabled.
225 bool IsKhrPipelineEexecutablePropertiesEnabled() const { 220 bool IsKhrPipelineExecutablePropertiesEnabled() const {
226 return khr_pipeline_executable_properties; 221 return khr_pipeline_executable_properties;
227 } 222 }
228 223
@@ -316,6 +311,17 @@ public:
316 return ext_shader_atomic_int64; 311 return ext_shader_atomic_int64;
317 } 312 }
318 313
314 /// Returns the minimum supported version of SPIR-V.
315 u32 SupportedSpirvVersion() const {
316 if (instance_version >= VK_API_VERSION_1_3) {
317 return 0x00010600U;
318 }
319 if (khr_spirv_1_4) {
320 return 0x00010400U;
321 }
322 return 0x00010000U;
323 }
324
319 /// Returns true when a known debugging tool is attached. 325 /// Returns true when a known debugging tool is attached.
320 bool HasDebuggingToolAttached() const { 326 bool HasDebuggingToolAttached() const {
321 return has_renderdoc || has_nsight_graphics; 327 return has_renderdoc || has_nsight_graphics;
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index a082e3059..562039b56 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -9,18 +9,21 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/dynamic_library.h" 10#include "common/dynamic_library.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/polyfill_ranges.h"
12#include "core/frontend/emu_window.h" 13#include "core/frontend/emu_window.h"
13#include "video_core/vulkan_common/vulkan_instance.h" 14#include "video_core/vulkan_common/vulkan_instance.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h" 15#include "video_core/vulkan_common/vulkan_wrapper.h"
15 16
16// Include these late to avoid polluting previous headers 17// Include these late to avoid polluting previous headers
17#ifdef _WIN32 18#if defined(_WIN32)
18#include <windows.h> 19#include <windows.h>
19// ensure include order 20// ensure include order
20#include <vulkan/vulkan_win32.h> 21#include <vulkan/vulkan_win32.h>
21#endif 22#elif defined(__APPLE__)
22 23#include <vulkan/vulkan_macos.h>
23#if !defined(_WIN32) && !defined(__APPLE__) 24#elif defined(__ANDROID__)
25#include <vulkan/vulkan_android.h>
26#else
24#include <X11/Xlib.h> 27#include <X11/Xlib.h>
25#include <vulkan/vulkan_wayland.h> 28#include <vulkan/vulkan_wayland.h>
26#include <vulkan/vulkan_xlib.h> 29#include <vulkan/vulkan_xlib.h>
@@ -39,8 +42,15 @@ namespace {
39 case Core::Frontend::WindowSystemType::Windows: 42 case Core::Frontend::WindowSystemType::Windows:
40 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); 43 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
41 break; 44 break;
42#endif 45#elif defined(__APPLE__)
43#if !defined(_WIN32) && !defined(__APPLE__) 46 case Core::Frontend::WindowSystemType::Cocoa:
47 extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
48 break;
49#elif defined(__ANDROID__)
50 case Core::Frontend::WindowSystemType::Android:
51 extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
52 break;
53#else
44 case Core::Frontend::WindowSystemType::X11: 54 case Core::Frontend::WindowSystemType::X11:
45 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); 55 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
46 break; 56 break;
@@ -59,6 +69,10 @@ namespace {
59 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
60 } 70 }
61 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); 71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
72
73#ifdef __APPLE__
74 extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
75#endif
62 return extensions; 76 return extensions;
63} 77}
64 78
@@ -140,7 +154,7 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
140 } 154 }
141 vk::Instance instance = 155 vk::Instance instance =
142 std::async([&] { 156 std::async([&] {
143 return vk::Instance::Create(required_version, layers, extensions, dld); 157 return vk::Instance::Create(available_version, layers, extensions, dld);
144 }).get(); 158 }).get();
145 if (!vk::Load(*instance, dld)) { 159 if (!vk::Load(*instance, dld)) {
146 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers"); 160 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers");
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index 6442898bd..1732866e0 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -12,6 +12,7 @@
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15#include "common/polyfill_ranges.h"
15#include "video_core/vulkan_common/vulkan_device.h" 16#include "video_core/vulkan_common/vulkan_device.h"
16#include "video_core/vulkan_common/vulkan_memory_allocator.h" 17#include "video_core/vulkan_common/vulkan_memory_allocator.h"
17#include "video_core/vulkan_common/vulkan_wrapper.h" 18#include "video_core/vulkan_common/vulkan_wrapper.h"
diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp
index 69f9c494b..fa9bafa20 100644
--- a/src/video_core/vulkan_common/vulkan_surface.cpp
+++ b/src/video_core/vulkan_common/vulkan_surface.cpp
@@ -11,9 +11,11 @@
11#include <windows.h> 11#include <windows.h>
12// ensure include order 12// ensure include order
13#include <vulkan/vulkan_win32.h> 13#include <vulkan/vulkan_win32.h>
14#endif 14#elif defined(__APPLE__)
15 15#include <vulkan/vulkan_macos.h>
16#if !defined(_WIN32) && !defined(__APPLE__) 16#elif defined(__ANDROID__)
17#include <vulkan/vulkan_android.h>
18#else
17#include <X11/Xlib.h> 19#include <X11/Xlib.h>
18#include <vulkan/vulkan_wayland.h> 20#include <vulkan/vulkan_wayland.h>
19#include <vulkan/vulkan_xlib.h> 21#include <vulkan/vulkan_xlib.h>
@@ -40,8 +42,33 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
40 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 42 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
41 } 43 }
42 } 44 }
43#endif 45#elif defined(__APPLE__)
44#if !defined(_WIN32) && !defined(__APPLE__) 46 if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) {
47 const VkMacOSSurfaceCreateInfoMVK mvk_ci{VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK,
48 nullptr, 0, window_info.render_surface};
49 const auto vkCreateMacOSSurfaceMVK = reinterpret_cast<PFN_vkCreateMacOSSurfaceMVK>(
50 dld.vkGetInstanceProcAddr(*instance, "vkCreateMacOSSurfaceMVK"));
51 if (!vkCreateMacOSSurfaceMVK ||
52 vkCreateMacOSSurfaceMVK(*instance, &mvk_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
53 LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface");
54 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
55 }
56 }
57#elif defined(__ANDROID__)
58 if (window_info.type == Core::Frontend::WindowSystemType::Android) {
59 const VkAndroidSurfaceCreateInfoKHR android_ci{
60 VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, nullptr, 0,
61 reinterpret_cast<ANativeWindow*>(window_info.render_surface)};
62 const auto vkCreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(
63 dld.vkGetInstanceProcAddr(*instance, "vkCreateAndroidSurfaceKHR"));
64 if (!vkCreateAndroidSurfaceKHR ||
65 vkCreateAndroidSurfaceKHR(*instance, &android_ci, nullptr, &unsafe_surface) !=
66 VK_SUCCESS) {
67 LOG_ERROR(Render_Vulkan, "Failed to initialize Android surface");
68 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
69 }
70 }
71#else
45 if (window_info.type == Core::Frontend::WindowSystemType::X11) { 72 if (window_info.type == Core::Frontend::WindowSystemType::X11) {
46 const VkXlibSurfaceCreateInfoKHR xlib_ci{ 73 const VkXlibSurfaceCreateInfoKHR xlib_ci{
47 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, 74 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0,
@@ -70,6 +97,7 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
70 } 97 }
71 } 98 }
72#endif 99#endif
100
73 if (!unsafe_surface) { 101 if (!unsafe_surface) {
74 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); 102 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
75 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 103 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 2ad98dcfe..bda10ee2f 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -130,7 +130,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
130 X(vkCreateComputePipelines); 130 X(vkCreateComputePipelines);
131 X(vkCreateDescriptorPool); 131 X(vkCreateDescriptorPool);
132 X(vkCreateDescriptorSetLayout); 132 X(vkCreateDescriptorSetLayout);
133 X(vkCreateDescriptorUpdateTemplateKHR); 133 X(vkCreateDescriptorUpdateTemplate);
134 X(vkCreateEvent); 134 X(vkCreateEvent);
135 X(vkCreateFence); 135 X(vkCreateFence);
136 X(vkCreateFramebuffer); 136 X(vkCreateFramebuffer);
@@ -149,7 +149,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
149 X(vkDestroyCommandPool); 149 X(vkDestroyCommandPool);
150 X(vkDestroyDescriptorPool); 150 X(vkDestroyDescriptorPool);
151 X(vkDestroyDescriptorSetLayout); 151 X(vkDestroyDescriptorSetLayout);
152 X(vkDestroyDescriptorUpdateTemplateKHR); 152 X(vkDestroyDescriptorUpdateTemplate);
153 X(vkDestroyEvent); 153 X(vkDestroyEvent);
154 X(vkDestroyFence); 154 X(vkDestroyFence);
155 X(vkDestroyFramebuffer); 155 X(vkDestroyFramebuffer);
@@ -180,18 +180,29 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
180 X(vkGetQueryPoolResults); 180 X(vkGetQueryPoolResults);
181 X(vkGetPipelineExecutablePropertiesKHR); 181 X(vkGetPipelineExecutablePropertiesKHR);
182 X(vkGetPipelineExecutableStatisticsKHR); 182 X(vkGetPipelineExecutableStatisticsKHR);
183 X(vkGetSemaphoreCounterValueKHR); 183 X(vkGetSemaphoreCounterValue);
184 X(vkMapMemory); 184 X(vkMapMemory);
185 X(vkQueueSubmit); 185 X(vkQueueSubmit);
186 X(vkResetFences); 186 X(vkResetFences);
187 X(vkResetQueryPoolEXT); 187 X(vkResetQueryPool);
188 X(vkSetDebugUtilsObjectNameEXT); 188 X(vkSetDebugUtilsObjectNameEXT);
189 X(vkSetDebugUtilsObjectTagEXT); 189 X(vkSetDebugUtilsObjectTagEXT);
190 X(vkUnmapMemory); 190 X(vkUnmapMemory);
191 X(vkUpdateDescriptorSetWithTemplateKHR); 191 X(vkUpdateDescriptorSetWithTemplate);
192 X(vkUpdateDescriptorSets); 192 X(vkUpdateDescriptorSets);
193 X(vkWaitForFences); 193 X(vkWaitForFences);
194 X(vkWaitSemaphoresKHR); 194 X(vkWaitSemaphores);
195
196 // Support for timeline semaphores is mandatory in Vulkan 1.2
197 if (!dld.vkGetSemaphoreCounterValue) {
198 Proc(dld.vkGetSemaphoreCounterValue, dld, "vkGetSemaphoreCounterValueKHR", device);
199 Proc(dld.vkWaitSemaphores, dld, "vkWaitSemaphoresKHR", device);
200 }
201
202 // Support for host query reset is mandatory in Vulkan 1.2
203 if (!dld.vkResetQueryPool) {
204 Proc(dld.vkResetQueryPool, dld, "vkResetQueryPoolEXT", device);
205 }
195#undef X 206#undef X
196} 207}
197 208
@@ -224,12 +235,13 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept {
224 X(vkCreateDebugUtilsMessengerEXT); 235 X(vkCreateDebugUtilsMessengerEXT);
225 X(vkDestroyDebugUtilsMessengerEXT); 236 X(vkDestroyDebugUtilsMessengerEXT);
226 X(vkDestroySurfaceKHR); 237 X(vkDestroySurfaceKHR);
227 X(vkGetPhysicalDeviceFeatures2KHR); 238 X(vkGetPhysicalDeviceFeatures2);
228 X(vkGetPhysicalDeviceProperties2KHR); 239 X(vkGetPhysicalDeviceProperties2);
229 X(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); 240 X(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
230 X(vkGetPhysicalDeviceSurfaceFormatsKHR); 241 X(vkGetPhysicalDeviceSurfaceFormatsKHR);
231 X(vkGetPhysicalDeviceSurfacePresentModesKHR); 242 X(vkGetPhysicalDeviceSurfacePresentModesKHR);
232 X(vkGetPhysicalDeviceSurfaceSupportKHR); 243 X(vkGetPhysicalDeviceSurfaceSupportKHR);
244 X(vkGetPhysicalDeviceToolProperties);
233 X(vkGetSwapchainImagesKHR); 245 X(vkGetSwapchainImagesKHR);
234 X(vkQueuePresentKHR); 246 X(vkQueuePresentKHR);
235 247
@@ -359,9 +371,9 @@ void Destroy(VkDevice device, VkDescriptorSetLayout handle, const DeviceDispatch
359 dld.vkDestroyDescriptorSetLayout(device, handle, nullptr); 371 dld.vkDestroyDescriptorSetLayout(device, handle, nullptr);
360} 372}
361 373
362void Destroy(VkDevice device, VkDescriptorUpdateTemplateKHR handle, 374void Destroy(VkDevice device, VkDescriptorUpdateTemplate handle,
363 const DeviceDispatch& dld) noexcept { 375 const DeviceDispatch& dld) noexcept {
364 dld.vkDestroyDescriptorUpdateTemplateKHR(device, handle, nullptr); 376 dld.vkDestroyDescriptorUpdateTemplate(device, handle, nullptr);
365} 377}
366 378
367void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) noexcept { 379void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) noexcept {
@@ -442,6 +454,12 @@ VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffe
442 454
443Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions, 455Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
444 InstanceDispatch& dispatch) { 456 InstanceDispatch& dispatch) {
457#ifdef __APPLE__
458 constexpr VkFlags ci_flags{VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR};
459#else
460 constexpr VkFlags ci_flags{};
461#endif
462
445 const VkApplicationInfo application_info{ 463 const VkApplicationInfo application_info{
446 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, 464 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
447 .pNext = nullptr, 465 .pNext = nullptr,
@@ -454,7 +472,7 @@ Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char
454 const VkInstanceCreateInfo ci{ 472 const VkInstanceCreateInfo ci{
455 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 473 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
456 .pNext = nullptr, 474 .pNext = nullptr,
457 .flags = 0, 475 .flags = ci_flags,
458 .pApplicationInfo = &application_info, 476 .pApplicationInfo = &application_info,
459 .enabledLayerCount = layers.size(), 477 .enabledLayerCount = layers.size(),
460 .ppEnabledLayerNames = layers.data(), 478 .ppEnabledLayerNames = layers.data(),
@@ -737,11 +755,11 @@ CommandPool Device::CreateCommandPool(const VkCommandPoolCreateInfo& ci) const {
737 return CommandPool(object, handle, *dld); 755 return CommandPool(object, handle, *dld);
738} 756}
739 757
740DescriptorUpdateTemplateKHR Device::CreateDescriptorUpdateTemplateKHR( 758DescriptorUpdateTemplate Device::CreateDescriptorUpdateTemplate(
741 const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const { 759 const VkDescriptorUpdateTemplateCreateInfo& ci) const {
742 VkDescriptorUpdateTemplateKHR object; 760 VkDescriptorUpdateTemplate object;
743 Check(dld->vkCreateDescriptorUpdateTemplateKHR(handle, &ci, nullptr, &object)); 761 Check(dld->vkCreateDescriptorUpdateTemplate(handle, &ci, nullptr, &object));
744 return DescriptorUpdateTemplateKHR(object, handle, *dld); 762 return DescriptorUpdateTemplate(object, handle, *dld);
745} 763}
746 764
747QueryPool Device::CreateQueryPool(const VkQueryPoolCreateInfo& ci) const { 765QueryPool Device::CreateQueryPool(const VkQueryPoolCreateInfo& ci) const {
@@ -857,20 +875,20 @@ VkPhysicalDeviceProperties PhysicalDevice::GetProperties() const noexcept {
857 return properties; 875 return properties;
858} 876}
859 877
860void PhysicalDevice::GetProperties2KHR(VkPhysicalDeviceProperties2KHR& properties) const noexcept { 878void PhysicalDevice::GetProperties2(VkPhysicalDeviceProperties2& properties) const noexcept {
861 dld->vkGetPhysicalDeviceProperties2KHR(physical_device, &properties); 879 dld->vkGetPhysicalDeviceProperties2(physical_device, &properties);
862} 880}
863 881
864VkPhysicalDeviceFeatures PhysicalDevice::GetFeatures() const noexcept { 882VkPhysicalDeviceFeatures PhysicalDevice::GetFeatures() const noexcept {
865 VkPhysicalDeviceFeatures2KHR features2; 883 VkPhysicalDeviceFeatures2 features2;
866 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; 884 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
867 features2.pNext = nullptr; 885 features2.pNext = nullptr;
868 dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features2); 886 dld->vkGetPhysicalDeviceFeatures2(physical_device, &features2);
869 return features2.features; 887 return features2.features;
870} 888}
871 889
872void PhysicalDevice::GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR& features) const noexcept { 890void PhysicalDevice::GetFeatures2(VkPhysicalDeviceFeatures2& features) const noexcept {
873 dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features); 891 dld->vkGetPhysicalDeviceFeatures2(physical_device, &features);
874} 892}
875 893
876VkFormatProperties PhysicalDevice::GetFormatProperties(VkFormat format) const noexcept { 894VkFormatProperties PhysicalDevice::GetFormatProperties(VkFormat format) const noexcept {
@@ -895,6 +913,18 @@ std::vector<VkQueueFamilyProperties> PhysicalDevice::GetQueueFamilyProperties()
895 return properties; 913 return properties;
896} 914}
897 915
916std::vector<VkPhysicalDeviceToolProperties> PhysicalDevice::GetPhysicalDeviceToolProperties()
917 const {
918 u32 num = 0;
919 if (!dld->vkGetPhysicalDeviceToolProperties) {
920 return {};
921 }
922 dld->vkGetPhysicalDeviceToolProperties(physical_device, &num, nullptr);
923 std::vector<VkPhysicalDeviceToolProperties> properties(num);
924 dld->vkGetPhysicalDeviceToolProperties(physical_device, &num, properties.data());
925 return properties;
926}
927
898bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR surface) const { 928bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR surface) const {
899 VkBool32 supported; 929 VkBool32 supported;
900 Check(dld->vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_family_index, surface, 930 Check(dld->vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_family_index, surface,
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 1b3f493bd..8395ff2cb 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -168,12 +168,13 @@ struct InstanceDispatch {
168 PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{}; 168 PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{};
169 PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices{}; 169 PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices{};
170 PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr{}; 170 PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr{};
171 PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{}; 171 PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2{};
172 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; 172 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{};
173 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; 173 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{};
174 PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{}; 174 PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{};
175 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; 175 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{};
176 PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{}; 176 PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2{};
177 PFN_vkGetPhysicalDeviceToolProperties vkGetPhysicalDeviceToolProperties{};
177 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{}; 178 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{};
178 PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR{}; 179 PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR{};
179 PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR{}; 180 PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR{};
@@ -247,7 +248,7 @@ struct DeviceDispatch : InstanceDispatch {
247 PFN_vkCreateComputePipelines vkCreateComputePipelines{}; 248 PFN_vkCreateComputePipelines vkCreateComputePipelines{};
248 PFN_vkCreateDescriptorPool vkCreateDescriptorPool{}; 249 PFN_vkCreateDescriptorPool vkCreateDescriptorPool{};
249 PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout{}; 250 PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout{};
250 PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR{}; 251 PFN_vkCreateDescriptorUpdateTemplate vkCreateDescriptorUpdateTemplate{};
251 PFN_vkCreateEvent vkCreateEvent{}; 252 PFN_vkCreateEvent vkCreateEvent{};
252 PFN_vkCreateFence vkCreateFence{}; 253 PFN_vkCreateFence vkCreateFence{};
253 PFN_vkCreateFramebuffer vkCreateFramebuffer{}; 254 PFN_vkCreateFramebuffer vkCreateFramebuffer{};
@@ -266,7 +267,7 @@ struct DeviceDispatch : InstanceDispatch {
266 PFN_vkDestroyCommandPool vkDestroyCommandPool{}; 267 PFN_vkDestroyCommandPool vkDestroyCommandPool{};
267 PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool{}; 268 PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool{};
268 PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout{}; 269 PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout{};
269 PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR{}; 270 PFN_vkDestroyDescriptorUpdateTemplate vkDestroyDescriptorUpdateTemplate{};
270 PFN_vkDestroyEvent vkDestroyEvent{}; 271 PFN_vkDestroyEvent vkDestroyEvent{};
271 PFN_vkDestroyFence vkDestroyFence{}; 272 PFN_vkDestroyFence vkDestroyFence{};
272 PFN_vkDestroyFramebuffer vkDestroyFramebuffer{}; 273 PFN_vkDestroyFramebuffer vkDestroyFramebuffer{};
@@ -297,18 +298,18 @@ struct DeviceDispatch : InstanceDispatch {
297 PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{}; 298 PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
298 PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{}; 299 PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
299 PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; 300 PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
300 PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{}; 301 PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{};
301 PFN_vkMapMemory vkMapMemory{}; 302 PFN_vkMapMemory vkMapMemory{};
302 PFN_vkQueueSubmit vkQueueSubmit{}; 303 PFN_vkQueueSubmit vkQueueSubmit{};
303 PFN_vkResetFences vkResetFences{}; 304 PFN_vkResetFences vkResetFences{};
304 PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT{}; 305 PFN_vkResetQueryPool vkResetQueryPool{};
305 PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{}; 306 PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{};
306 PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT{}; 307 PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT{};
307 PFN_vkUnmapMemory vkUnmapMemory{}; 308 PFN_vkUnmapMemory vkUnmapMemory{};
308 PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR{}; 309 PFN_vkUpdateDescriptorSetWithTemplate vkUpdateDescriptorSetWithTemplate{};
309 PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets{}; 310 PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets{};
310 PFN_vkWaitForFences vkWaitForFences{}; 311 PFN_vkWaitForFences vkWaitForFences{};
311 PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR{}; 312 PFN_vkWaitSemaphores vkWaitSemaphores{};
312}; 313};
313 314
314/// Loads instance agnostic function pointers. 315/// Loads instance agnostic function pointers.
@@ -327,7 +328,7 @@ void Destroy(VkDevice, VkBufferView, const DeviceDispatch&) noexcept;
327void Destroy(VkDevice, VkCommandPool, const DeviceDispatch&) noexcept; 328void Destroy(VkDevice, VkCommandPool, const DeviceDispatch&) noexcept;
328void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept; 329void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept;
329void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept; 330void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept;
330void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept; 331void Destroy(VkDevice, VkDescriptorUpdateTemplate, const DeviceDispatch&) noexcept;
331void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; 332void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept;
332void Destroy(VkDevice, VkEvent, const DeviceDispatch&) noexcept; 333void Destroy(VkDevice, VkEvent, const DeviceDispatch&) noexcept;
333void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept; 334void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept;
@@ -559,7 +560,7 @@ private:
559 560
560using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; 561using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>;
561using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; 562using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>;
562using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>; 563using DescriptorUpdateTemplate = Handle<VkDescriptorUpdateTemplate, VkDevice, DeviceDispatch>;
563using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; 564using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>;
564using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>; 565using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>;
565using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>; 566using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>;
@@ -766,7 +767,7 @@ public:
766 767
767 [[nodiscard]] u64 GetCounter() const { 768 [[nodiscard]] u64 GetCounter() const {
768 u64 value; 769 u64 value;
769 Check(dld->vkGetSemaphoreCounterValueKHR(owner, handle, &value)); 770 Check(dld->vkGetSemaphoreCounterValue(owner, handle, &value));
770 return value; 771 return value;
771 } 772 }
772 773
@@ -778,15 +779,15 @@ public:
778 * @return True on successful wait, false on timeout 779 * @return True on successful wait, false on timeout
779 */ 780 */
780 bool Wait(u64 value, u64 timeout = std::numeric_limits<u64>::max()) const { 781 bool Wait(u64 value, u64 timeout = std::numeric_limits<u64>::max()) const {
781 const VkSemaphoreWaitInfoKHR wait_info{ 782 const VkSemaphoreWaitInfo wait_info{
782 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR, 783 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
783 .pNext = nullptr, 784 .pNext = nullptr,
784 .flags = 0, 785 .flags = 0,
785 .semaphoreCount = 1, 786 .semaphoreCount = 1,
786 .pSemaphores = &handle, 787 .pSemaphores = &handle,
787 .pValues = &value, 788 .pValues = &value,
788 }; 789 };
789 const VkResult result = dld->vkWaitSemaphoresKHR(owner, &wait_info, timeout); 790 const VkResult result = dld->vkWaitSemaphores(owner, &wait_info, timeout);
790 switch (result) { 791 switch (result) {
791 case VK_SUCCESS: 792 case VK_SUCCESS:
792 return true; 793 return true;
@@ -840,8 +841,8 @@ public:
840 841
841 CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const; 842 CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const;
842 843
843 DescriptorUpdateTemplateKHR CreateDescriptorUpdateTemplateKHR( 844 DescriptorUpdateTemplate CreateDescriptorUpdateTemplate(
844 const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const; 845 const VkDescriptorUpdateTemplateCreateInfo& ci) const;
845 846
846 QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const; 847 QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const;
847 848
@@ -869,9 +870,9 @@ public:
869 void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, 870 void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
870 Span<VkCopyDescriptorSet> copies) const noexcept; 871 Span<VkCopyDescriptorSet> copies) const noexcept;
871 872
872 void UpdateDescriptorSet(VkDescriptorSet set, VkDescriptorUpdateTemplateKHR update_template, 873 void UpdateDescriptorSet(VkDescriptorSet set, VkDescriptorUpdateTemplate update_template,
873 const void* data) const noexcept { 874 const void* data) const noexcept {
874 dld->vkUpdateDescriptorSetWithTemplateKHR(handle, set, update_template, data); 875 dld->vkUpdateDescriptorSetWithTemplate(handle, set, update_template, data);
875 } 876 }
876 877
877 VkResult AcquireNextImageKHR(VkSwapchainKHR swapchain, u64 timeout, VkSemaphore semaphore, 878 VkResult AcquireNextImageKHR(VkSwapchainKHR swapchain, u64 timeout, VkSemaphore semaphore,
@@ -884,8 +885,8 @@ public:
884 return dld->vkDeviceWaitIdle(handle); 885 return dld->vkDeviceWaitIdle(handle);
885 } 886 }
886 887
887 void ResetQueryPoolEXT(VkQueryPool query_pool, u32 first, u32 count) const noexcept { 888 void ResetQueryPool(VkQueryPool query_pool, u32 first, u32 count) const noexcept {
888 dld->vkResetQueryPoolEXT(handle, query_pool, first, count); 889 dld->vkResetQueryPool(handle, query_pool, first, count);
889 } 890 }
890 891
891 VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, 892 VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
@@ -910,11 +911,11 @@ public:
910 911
911 VkPhysicalDeviceProperties GetProperties() const noexcept; 912 VkPhysicalDeviceProperties GetProperties() const noexcept;
912 913
913 void GetProperties2KHR(VkPhysicalDeviceProperties2KHR&) const noexcept; 914 void GetProperties2(VkPhysicalDeviceProperties2&) const noexcept;
914 915
915 VkPhysicalDeviceFeatures GetFeatures() const noexcept; 916 VkPhysicalDeviceFeatures GetFeatures() const noexcept;
916 917
917 void GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR&) const noexcept; 918 void GetFeatures2(VkPhysicalDeviceFeatures2&) const noexcept;
918 919
919 VkFormatProperties GetFormatProperties(VkFormat) const noexcept; 920 VkFormatProperties GetFormatProperties(VkFormat) const noexcept;
920 921
@@ -922,6 +923,8 @@ public:
922 923
923 std::vector<VkQueueFamilyProperties> GetQueueFamilyProperties() const; 924 std::vector<VkQueueFamilyProperties> GetQueueFamilyProperties() const;
924 925
926 std::vector<VkPhysicalDeviceToolProperties> GetPhysicalDeviceToolProperties() const;
927
925 bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const; 928 bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const;
926 929
927 VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const; 930 VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const;
@@ -980,7 +983,7 @@ public:
980 dynamic_offsets.size(), dynamic_offsets.data()); 983 dynamic_offsets.size(), dynamic_offsets.data());
981 } 984 }
982 985
983 void PushDescriptorSetWithTemplateKHR(VkDescriptorUpdateTemplateKHR update_template, 986 void PushDescriptorSetWithTemplateKHR(VkDescriptorUpdateTemplate update_template,
984 VkPipelineLayout layout, u32 set, 987 VkPipelineLayout layout, u32 set,
985 const void* data) const noexcept { 988 const void* data) const noexcept {
986 dld->vkCmdPushDescriptorSetWithTemplateKHR(handle, update_template, layout, set, data); 989 dld->vkCmdPushDescriptorSetWithTemplateKHR(handle, update_template, layout, set, data);
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 3f75d97d1..02582aa04 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -4,6 +4,7 @@
4add_library(web_service STATIC 4add_library(web_service STATIC
5 announce_room_json.cpp 5 announce_room_json.cpp
6 announce_room_json.h 6 announce_room_json.h
7 precompiled_headers.h
7 telemetry_json.cpp 8 telemetry_json.cpp
8 telemetry_json.h 9 telemetry_json.h
9 verify_login.cpp 10 verify_login.cpp
@@ -16,4 +17,8 @@ add_library(web_service STATIC
16) 17)
17 18
18create_target_directory_groups(web_service) 19create_target_directory_groups(web_service)
19target_link_libraries(web_service PRIVATE common network nlohmann_json::nlohmann_json httplib cpp-jwt) 20target_link_libraries(web_service PRIVATE common network nlohmann_json::nlohmann_json httplib::httplib cpp-jwt::cpp-jwt)
21
22if (YUZU_USE_PRECOMPILED_HEADERS)
23 target_precompile_headers(web_service PRIVATE precompiled_headers.h)
24endif()
diff --git a/src/web_service/precompiled_headers.h b/src/web_service/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/web_service/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 060de0259..d23eb2907 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -88,6 +88,9 @@ add_executable(yuzu
88 configuration/configure_input_advanced.cpp 88 configuration/configure_input_advanced.cpp
89 configuration/configure_input_advanced.h 89 configuration/configure_input_advanced.h
90 configuration/configure_input_advanced.ui 90 configuration/configure_input_advanced.ui
91 configuration/configure_input_per_game.cpp
92 configuration/configure_input_per_game.h
93 configuration/configure_input_per_game.ui
91 configuration/configure_input_player.cpp 94 configuration/configure_input_player.cpp
92 configuration/configure_input_player.h 95 configuration/configure_input_player.h
93 configuration/configure_input_player.ui 96 configuration/configure_input_player.ui
@@ -186,6 +189,7 @@ add_executable(yuzu
186 multiplayer/state.cpp 189 multiplayer/state.cpp
187 multiplayer/state.h 190 multiplayer/state.h
188 multiplayer/validation.h 191 multiplayer/validation.h
192 precompiled_headers.h
189 startup_checks.cpp 193 startup_checks.cpp
190 startup_checks.h 194 startup_checks.h
191 uisettings.cpp 195 uisettings.cpp
@@ -314,7 +318,7 @@ target_link_libraries(yuzu PRIVATE common core input_common network video_core)
314target_link_libraries(yuzu PRIVATE Boost::boost glad Qt${QT_MAJOR_VERSION}::Widgets) 318target_link_libraries(yuzu PRIVATE Boost::boost glad Qt${QT_MAJOR_VERSION}::Widgets)
315target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 319target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
316 320
317target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) 321target_link_libraries(yuzu PRIVATE Vulkan::Headers)
318if (NOT WIN32) 322if (NOT WIN32)
319 target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}) 323 target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
320endif() 324endif()
@@ -350,7 +354,7 @@ if (USE_DISCORD_PRESENCE)
350 discord_impl.cpp 354 discord_impl.cpp
351 discord_impl.h 355 discord_impl.h
352 ) 356 )
353 target_link_libraries(yuzu PRIVATE discord-rpc) 357 target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc)
354 target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) 358 target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE)
355endif() 359endif()
356 360
@@ -387,11 +391,7 @@ if (YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6)
387endif() 391endif()
388 392
389if (ENABLE_SDL2) 393if (ENABLE_SDL2)
390 if (YUZU_USE_EXTERNAL_SDL2) 394 target_link_libraries(yuzu PRIVATE SDL2::SDL2)
391 target_link_libraries(yuzu PRIVATE SDL2-static)
392 else()
393 target_link_libraries(yuzu PRIVATE SDL2)
394 endif()
395 target_compile_definitions(yuzu PRIVATE HAVE_SDL2) 395 target_compile_definitions(yuzu PRIVATE HAVE_SDL2)
396endif() 396endif()
397 397
@@ -407,5 +407,9 @@ if (NOT APPLE)
407endif() 407endif()
408 408
409if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 409if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
410 target_link_libraries(yuzu PRIVATE dynarmic) 410 target_link_libraries(yuzu PRIVATE dynarmic::dynarmic)
411endif()
412
413if (YUZU_USE_PRECOMPILED_HEADERS)
414 target_precompile_headers(yuzu PRIVATE precompiled_headers.h)
411endif() 415endif()
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 12efdc216..c30b54499 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -685,7 +685,7 @@ QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
685QtControllerSelector::~QtControllerSelector() = default; 685QtControllerSelector::~QtControllerSelector() = default;
686 686
687void QtControllerSelector::ReconfigureControllers( 687void QtControllerSelector::ReconfigureControllers(
688 std::function<void()> callback_, const Core::Frontend::ControllerParameters& parameters) const { 688 ReconfigureCallback callback_, const Core::Frontend::ControllerParameters& parameters) const {
689 callback = std::move(callback_); 689 callback = std::move(callback_);
690 emit MainWindowReconfigureControllers(parameters); 690 emit MainWindowReconfigureControllers(parameters);
691} 691}
diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h
index cf948d2b5..16e99f507 100644
--- a/src/yuzu/applets/qt_controller.h
+++ b/src/yuzu/applets/qt_controller.h
@@ -157,7 +157,7 @@ public:
157 ~QtControllerSelector() override; 157 ~QtControllerSelector() override;
158 158
159 void ReconfigureControllers( 159 void ReconfigureControllers(
160 std::function<void()> callback_, 160 ReconfigureCallback callback_,
161 const Core::Frontend::ControllerParameters& parameters) const override; 161 const Core::Frontend::ControllerParameters& parameters) const override;
162 162
163signals: 163signals:
@@ -167,5 +167,5 @@ signals:
167private: 167private:
168 void MainWindowReconfigureFinished(); 168 void MainWindowReconfigureFinished();
169 169
170 mutable std::function<void()> callback; 170 mutable ReconfigureCallback callback;
171}; 171};
diff --git a/src/yuzu/applets/qt_error.cpp b/src/yuzu/applets/qt_error.cpp
index 367d5352d..e0190a979 100644
--- a/src/yuzu/applets/qt_error.cpp
+++ b/src/yuzu/applets/qt_error.cpp
@@ -14,7 +14,7 @@ QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) {
14 14
15QtErrorDisplay::~QtErrorDisplay() = default; 15QtErrorDisplay::~QtErrorDisplay() = default;
16 16
17void QtErrorDisplay::ShowError(Result error, std::function<void()> finished) const { 17void QtErrorDisplay::ShowError(Result error, FinishedCallback finished) const {
18 callback = std::move(finished); 18 callback = std::move(finished);
19 emit MainWindowDisplayError( 19 emit MainWindowDisplayError(
20 tr("Error Code: %1-%2 (0x%3)") 20 tr("Error Code: %1-%2 (0x%3)")
@@ -25,7 +25,7 @@ void QtErrorDisplay::ShowError(Result error, std::function<void()> finished) con
25} 25}
26 26
27void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, 27void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
28 std::function<void()> finished) const { 28 FinishedCallback finished) const {
29 callback = std::move(finished); 29 callback = std::move(finished);
30 30
31 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); 31 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
@@ -42,7 +42,7 @@ void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds t
42 42
43void QtErrorDisplay::ShowCustomErrorText(Result error, std::string dialog_text, 43void QtErrorDisplay::ShowCustomErrorText(Result error, std::string dialog_text,
44 std::string fullscreen_text, 44 std::string fullscreen_text,
45 std::function<void()> finished) const { 45 FinishedCallback finished) const {
46 callback = std::move(finished); 46 callback = std::move(finished);
47 emit MainWindowDisplayError( 47 emit MainWindowDisplayError(
48 tr("Error Code: %1-%2 (0x%3)") 48 tr("Error Code: %1-%2 (0x%3)")
diff --git a/src/yuzu/applets/qt_error.h b/src/yuzu/applets/qt_error.h
index eb4107c7e..e4e174721 100644
--- a/src/yuzu/applets/qt_error.h
+++ b/src/yuzu/applets/qt_error.h
@@ -16,11 +16,11 @@ public:
16 explicit QtErrorDisplay(GMainWindow& parent); 16 explicit QtErrorDisplay(GMainWindow& parent);
17 ~QtErrorDisplay() override; 17 ~QtErrorDisplay() override;
18 18
19 void ShowError(Result error, std::function<void()> finished) const override; 19 void ShowError(Result error, FinishedCallback finished) const override;
20 void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, 20 void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
21 std::function<void()> finished) const override; 21 FinishedCallback finished) const override;
22 void ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text, 22 void ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text,
23 std::function<void()> finished) const override; 23 FinishedCallback finished) const override;
24 24
25signals: 25signals:
26 void MainWindowDisplayError(QString error_code, QString error_text) const; 26 void MainWindowDisplayError(QString error_code, QString error_text) const;
@@ -28,5 +28,5 @@ signals:
28private: 28private:
29 void MainWindowFinishedError(); 29 void MainWindowFinishedError();
30 30
31 mutable std::function<void()> callback; 31 mutable FinishedCallback callback;
32}; 32};
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp
index c8bcfb223..4145c5299 100644
--- a/src/yuzu/applets/qt_profile_select.cpp
+++ b/src/yuzu/applets/qt_profile_select.cpp
@@ -163,8 +163,7 @@ QtProfileSelector::QtProfileSelector(GMainWindow& parent) {
163 163
164QtProfileSelector::~QtProfileSelector() = default; 164QtProfileSelector::~QtProfileSelector() = default;
165 165
166void QtProfileSelector::SelectProfile( 166void QtProfileSelector::SelectProfile(SelectProfileCallback callback_) const {
167 std::function<void(std::optional<Common::UUID>)> callback_) const {
168 callback = std::move(callback_); 167 callback = std::move(callback_);
169 emit MainWindowSelectProfile(); 168 emit MainWindowSelectProfile();
170} 169}
diff --git a/src/yuzu/applets/qt_profile_select.h b/src/yuzu/applets/qt_profile_select.h
index 124f2cdbd..637a3bda2 100644
--- a/src/yuzu/applets/qt_profile_select.h
+++ b/src/yuzu/applets/qt_profile_select.h
@@ -65,7 +65,7 @@ public:
65 explicit QtProfileSelector(GMainWindow& parent); 65 explicit QtProfileSelector(GMainWindow& parent);
66 ~QtProfileSelector() override; 66 ~QtProfileSelector() override;
67 67
68 void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback_) const override; 68 void SelectProfile(SelectProfileCallback callback_) const override;
69 69
70signals: 70signals:
71 void MainWindowSelectProfile() const; 71 void MainWindowSelectProfile() const;
@@ -73,5 +73,5 @@ signals:
73private: 73private:
74 void MainWindowFinishedSelection(std::optional<Common::UUID> uuid); 74 void MainWindowFinishedSelection(std::optional<Common::UUID> uuid);
75 75
76 mutable std::function<void(std::optional<Common::UUID>)> callback; 76 mutable SelectProfileCallback callback;
77}; 77};
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index e60506197..734b0ea40 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -1566,10 +1566,7 @@ QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
1566 1566
1567void QtSoftwareKeyboard::InitializeKeyboard( 1567void QtSoftwareKeyboard::InitializeKeyboard(
1568 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, 1568 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
1569 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> 1569 SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) {
1570 submit_normal_callback_,
1571 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
1572 submit_inline_callback_) {
1573 if (is_inline) { 1570 if (is_inline) {
1574 submit_inline_callback = std::move(submit_inline_callback_); 1571 submit_inline_callback = std::move(submit_inline_callback_);
1575 } else { 1572 } else {
diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h
index 35d4ee2ef..30ac8ecf6 100644
--- a/src/yuzu/applets/qt_software_keyboard.h
+++ b/src/yuzu/applets/qt_software_keyboard.h
@@ -233,12 +233,10 @@ public:
233 explicit QtSoftwareKeyboard(GMainWindow& parent); 233 explicit QtSoftwareKeyboard(GMainWindow& parent);
234 ~QtSoftwareKeyboard() override; 234 ~QtSoftwareKeyboard() override;
235 235
236 void InitializeKeyboard( 236 void InitializeKeyboard(bool is_inline,
237 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, 237 Core::Frontend::KeyboardInitializeParameters initialize_parameters,
238 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> 238 SubmitNormalCallback submit_normal_callback_,
239 submit_normal_callback_, 239 SubmitInlineCallback submit_inline_callback_) override;
240 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
241 submit_inline_callback_) override;
242 240
243 void ShowNormalKeyboard() const override; 241 void ShowNormalKeyboard() const override;
244 242
@@ -279,8 +277,6 @@ private:
279 void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, 277 void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
280 std::u16string submitted_text, s32 cursor_position) const; 278 std::u16string submitted_text, s32 cursor_position) const;
281 279
282 mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> 280 mutable SubmitNormalCallback submit_normal_callback;
283 submit_normal_callback; 281 mutable SubmitInlineCallback submit_inline_callback;
284 mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
285 submit_inline_callback;
286}; 282};
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index 89bd482e0..0a5912326 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -401,9 +401,9 @@ QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
401 401
402QtWebBrowser::~QtWebBrowser() = default; 402QtWebBrowser::~QtWebBrowser() = default;
403 403
404void QtWebBrowser::OpenLocalWebPage( 404void QtWebBrowser::OpenLocalWebPage(const std::string& local_url,
405 const std::string& local_url, std::function<void()> extract_romfs_callback_, 405 ExtractROMFSCallback extract_romfs_callback_,
406 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback_) const { 406 OpenWebPageCallback callback_) const {
407 extract_romfs_callback = std::move(extract_romfs_callback_); 407 extract_romfs_callback = std::move(extract_romfs_callback_);
408 callback = std::move(callback_); 408 callback = std::move(callback_);
409 409
@@ -416,9 +416,8 @@ void QtWebBrowser::OpenLocalWebPage(
416 } 416 }
417} 417}
418 418
419void QtWebBrowser::OpenExternalWebPage( 419void QtWebBrowser::OpenExternalWebPage(const std::string& external_url,
420 const std::string& external_url, 420 OpenWebPageCallback callback_) const {
421 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback_) const {
422 callback = std::move(callback_); 421 callback = std::move(callback_);
423 422
424 const auto index = external_url.find('?'); 423 const auto index = external_url.find('?');
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h
index 043800853..e8fe511ed 100644
--- a/src/yuzu/applets/qt_web_browser.h
+++ b/src/yuzu/applets/qt_web_browser.h
@@ -197,13 +197,11 @@ public:
197 ~QtWebBrowser() override; 197 ~QtWebBrowser() override;
198 198
199 void OpenLocalWebPage(const std::string& local_url, 199 void OpenLocalWebPage(const std::string& local_url,
200 std::function<void()> extract_romfs_callback_, 200 ExtractROMFSCallback extract_romfs_callback_,
201 std::function<void(Service::AM::Applets::WebExitReason, std::string)> 201 OpenWebPageCallback callback_) const override;
202 callback_) const override;
203 202
204 void OpenExternalWebPage(const std::string& external_url, 203 void OpenExternalWebPage(const std::string& external_url,
205 std::function<void(Service::AM::Applets::WebExitReason, std::string)> 204 OpenWebPageCallback callback_) const override;
206 callback_) const override;
207 205
208signals: 206signals:
209 void MainWindowOpenWebPage(const std::string& main_url, const std::string& additional_args, 207 void MainWindowOpenWebPage(const std::string& main_url, const std::string& additional_args,
@@ -215,7 +213,6 @@ private:
215 void MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, 213 void MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason,
216 std::string last_url); 214 std::string last_url);
217 215
218 mutable std::function<void()> extract_romfs_callback; 216 mutable ExtractROMFSCallback extract_romfs_callback;
219 217 mutable OpenWebPageCallback callback;
220 mutable std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback;
221}; 218};
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index c934069dd..5b5b6fed8 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -118,7 +118,7 @@ void EmuThread::run() {
118 } 118 }
119 } else { 119 } else {
120 std::unique_lock lock{running_mutex}; 120 std::unique_lock lock{running_mutex};
121 running_cv.wait(lock, stop_token, [this] { return IsRunning(); }); 121 Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); });
122 } 122 }
123 } 123 }
124 124
@@ -237,8 +237,7 @@ private:
237 GRenderWindow* render_window; 237 GRenderWindow* render_window;
238}; 238};
239 239
240class OpenGLRenderWidget : public RenderWidget { 240struct OpenGLRenderWidget : public RenderWidget {
241public:
242 explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { 241 explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
243 windowHandle()->setSurfaceType(QWindow::OpenGLSurface); 242 windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
244 } 243 }
@@ -251,13 +250,16 @@ private:
251 std::unique_ptr<Core::Frontend::GraphicsContext> context; 250 std::unique_ptr<Core::Frontend::GraphicsContext> context;
252}; 251};
253 252
254class VulkanRenderWidget : public RenderWidget { 253struct VulkanRenderWidget : public RenderWidget {
255public:
256 explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { 254 explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
257 windowHandle()->setSurfaceType(QWindow::VulkanSurface); 255 windowHandle()->setSurfaceType(QWindow::VulkanSurface);
258 } 256 }
259}; 257};
260 258
259struct NullRenderWidget : public RenderWidget {
260 explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {}
261};
262
261static Core::Frontend::WindowSystemType GetWindowSystemType() { 263static Core::Frontend::WindowSystemType GetWindowSystemType() {
262 // Determine WSI type based on Qt platform. 264 // Determine WSI type based on Qt platform.
263 QString platform_name = QGuiApplication::platformName(); 265 QString platform_name = QGuiApplication::platformName();
@@ -267,6 +269,10 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() {
267 return Core::Frontend::WindowSystemType::X11; 269 return Core::Frontend::WindowSystemType::X11;
268 else if (platform_name == QStringLiteral("wayland")) 270 else if (platform_name == QStringLiteral("wayland"))
269 return Core::Frontend::WindowSystemType::Wayland; 271 return Core::Frontend::WindowSystemType::Wayland;
272 else if (platform_name == QStringLiteral("cocoa"))
273 return Core::Frontend::WindowSystemType::Cocoa;
274 else if (platform_name == QStringLiteral("android"))
275 return Core::Frontend::WindowSystemType::Android;
270 276
271 LOG_CRITICAL(Frontend, "Unknown Qt platform!"); 277 LOG_CRITICAL(Frontend, "Unknown Qt platform!");
272 return Core::Frontend::WindowSystemType::Windows; 278 return Core::Frontend::WindowSystemType::Windows;
@@ -874,6 +880,9 @@ bool GRenderWindow::InitRenderTarget() {
874 return false; 880 return false;
875 } 881 }
876 break; 882 break;
883 case Settings::RendererBackend::Null:
884 InitializeNull();
885 break;
877 } 886 }
878 887
879 // Update the Window System information with the new render target 888 // Update the Window System information with the new render target
@@ -970,6 +979,11 @@ bool GRenderWindow::InitializeVulkan() {
970 return true; 979 return true;
971} 980}
972 981
982void GRenderWindow::InitializeNull() {
983 child_widget = new NullRenderWidget(this);
984 main_context = std::make_unique<DummyContext>();
985}
986
973bool GRenderWindow::LoadOpenGL() { 987bool GRenderWindow::LoadOpenGL() {
974 auto context = CreateSharedContext(); 988 auto context = CreateSharedContext();
975 auto scope = context->Acquire(); 989 auto scope = context->Acquire();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 4a01481cd..f4deae4ee 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -14,6 +14,7 @@
14#include <QTouchEvent> 14#include <QTouchEvent>
15#include <QWidget> 15#include <QWidget>
16 16
17#include "common/polyfill_thread.h"
17#include "common/thread.h" 18#include "common/thread.h"
18#include "core/frontend/emu_window.h" 19#include "core/frontend/emu_window.h"
19 20
@@ -218,6 +219,7 @@ private:
218 219
219 bool InitializeOpenGL(); 220 bool InitializeOpenGL();
220 bool InitializeVulkan(); 221 bool InitializeVulkan();
222 void InitializeNull();
221 bool LoadOpenGL(); 223 bool LoadOpenGL();
222 QStringList GetUnsupportedGLExtensions() const; 224 QStringList GetUnsupportedGLExtensions() const;
223 225
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 0c93df428..722fc708e 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -124,6 +124,10 @@ void Config::Initialize(const std::string& config_name) {
124 } 124 }
125} 125}
126 126
127bool Config::IsCustomConfig() {
128 return type == ConfigType::PerGameConfig;
129}
130
127/* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their 131/* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their
128 * usages later in this file. This allows explicit definition of some types that don't work 132 * usages later in this file. This allows explicit definition of some types that don't work
129 * nicely with the general version. 133 * nicely with the general version.
@@ -194,8 +198,20 @@ void Config::ReadPlayerValue(std::size_t player_index) {
194 }(); 198 }();
195 199
196 auto& player = Settings::values.players.GetValue()[player_index]; 200 auto& player = Settings::values.players.GetValue()[player_index];
201 if (IsCustomConfig()) {
202 const auto profile_name =
203 qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{})
204 .toString()
205 .toStdString();
206 if (profile_name.empty()) {
207 // Use the global input config
208 player = Settings::values.players.GetValue(true)[player_index];
209 return;
210 }
211 player.profile_name = profile_name;
212 }
197 213
198 if (player_prefix.isEmpty()) { 214 if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) {
199 const auto controller = static_cast<Settings::ControllerType>( 215 const auto controller = static_cast<Settings::ControllerType>(
200 qt_config 216 qt_config
201 ->value(QStringLiteral("%1type").arg(player_prefix), 217 ->value(QStringLiteral("%1type").arg(player_prefix),
@@ -388,9 +404,26 @@ void Config::ReadAudioValues() {
388void Config::ReadControlValues() { 404void Config::ReadControlValues() {
389 qt_config->beginGroup(QStringLiteral("Controls")); 405 qt_config->beginGroup(QStringLiteral("Controls"));
390 406
407 Settings::values.players.SetGlobal(!IsCustomConfig());
391 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { 408 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
392 ReadPlayerValue(p); 409 ReadPlayerValue(p);
393 } 410 }
411 ReadGlobalSetting(Settings::values.use_docked_mode);
412
413 // Disable docked mode if handheld is selected
414 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
415 if (controller_type == Settings::ControllerType::Handheld) {
416 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
417 Settings::values.use_docked_mode.SetValue(false);
418 }
419
420 ReadGlobalSetting(Settings::values.vibration_enabled);
421 ReadGlobalSetting(Settings::values.enable_accurate_vibrations);
422 ReadGlobalSetting(Settings::values.motion_enabled);
423 if (IsCustomConfig()) {
424 qt_config->endGroup();
425 return;
426 }
394 ReadDebugValues(); 427 ReadDebugValues();
395 ReadKeyboardValues(); 428 ReadKeyboardValues();
396 ReadMouseValues(); 429 ReadMouseValues();
@@ -412,18 +445,6 @@ void Config::ReadControlValues() {
412 ReadBasicSetting(Settings::values.tas_loop); 445 ReadBasicSetting(Settings::values.tas_loop);
413 ReadBasicSetting(Settings::values.pause_tas_on_load); 446 ReadBasicSetting(Settings::values.pause_tas_on_load);
414 447
415 ReadGlobalSetting(Settings::values.use_docked_mode);
416
417 // Disable docked mode if handheld is selected
418 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
419 if (controller_type == Settings::ControllerType::Handheld) {
420 Settings::values.use_docked_mode.SetValue(false);
421 }
422
423 ReadGlobalSetting(Settings::values.vibration_enabled);
424 ReadGlobalSetting(Settings::values.enable_accurate_vibrations);
425 ReadGlobalSetting(Settings::values.motion_enabled);
426
427 ReadBasicSetting(Settings::values.controller_navigation); 448 ReadBasicSetting(Settings::values.controller_navigation);
428 449
429 qt_config->endGroup(); 450 qt_config->endGroup();
@@ -658,6 +679,7 @@ void Config::ReadCpuValues() {
658 ReadBasicSetting(Settings::values.cpuopt_fastmem); 679 ReadBasicSetting(Settings::values.cpuopt_fastmem);
659 ReadBasicSetting(Settings::values.cpuopt_fastmem_exclusives); 680 ReadBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
660 ReadBasicSetting(Settings::values.cpuopt_recompile_exclusives); 681 ReadBasicSetting(Settings::values.cpuopt_recompile_exclusives);
682 ReadBasicSetting(Settings::values.cpuopt_ignore_memory_aborts);
661 } 683 }
662 684
663 qt_config->endGroup(); 685 qt_config->endGroup();
@@ -905,7 +927,6 @@ void Config::ReadMultiplayerValues() {
905 927
906void Config::ReadValues() { 928void Config::ReadValues() {
907 if (global) { 929 if (global) {
908 ReadControlValues();
909 ReadDataStorageValues(); 930 ReadDataStorageValues();
910 ReadDebuggingValues(); 931 ReadDebuggingValues();
911 ReadDisabledAddOnValues(); 932 ReadDisabledAddOnValues();
@@ -914,6 +935,7 @@ void Config::ReadValues() {
914 ReadWebServiceValues(); 935 ReadWebServiceValues();
915 ReadMiscellaneousValues(); 936 ReadMiscellaneousValues();
916 } 937 }
938 ReadControlValues();
917 ReadCoreValues(); 939 ReadCoreValues();
918 ReadCpuValues(); 940 ReadCpuValues();
919 ReadRendererValues(); 941 ReadRendererValues();
@@ -932,12 +954,20 @@ void Config::SavePlayerValue(std::size_t player_index) {
932 }(); 954 }();
933 955
934 const auto& player = Settings::values.players.GetValue()[player_index]; 956 const auto& player = Settings::values.players.GetValue()[player_index];
957 if (IsCustomConfig()) {
958 if (player.profile_name.empty()) {
959 // No custom profile selected
960 return;
961 }
962 WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix),
963 QString::fromStdString(player.profile_name), QString{});
964 }
935 965
936 WriteSetting(QStringLiteral("%1type").arg(player_prefix), 966 WriteSetting(QStringLiteral("%1type").arg(player_prefix),
937 static_cast<u8>(player.controller_type), 967 static_cast<u8>(player.controller_type),
938 static_cast<u8>(Settings::ControllerType::ProController)); 968 static_cast<u8>(Settings::ControllerType::ProController));
939 969
940 if (!player_prefix.isEmpty()) { 970 if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) {
941 WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, 971 WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected,
942 player_index == 0); 972 player_index == 0);
943 WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), 973 WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix),
@@ -1055,7 +1085,6 @@ void Config::SaveIrCameraValues() {
1055 1085
1056void Config::SaveValues() { 1086void Config::SaveValues() {
1057 if (global) { 1087 if (global) {
1058 SaveControlValues();
1059 SaveDataStorageValues(); 1088 SaveDataStorageValues();
1060 SaveDebuggingValues(); 1089 SaveDebuggingValues();
1061 SaveDisabledAddOnValues(); 1090 SaveDisabledAddOnValues();
@@ -1064,6 +1093,7 @@ void Config::SaveValues() {
1064 SaveWebServiceValues(); 1093 SaveWebServiceValues();
1065 SaveMiscellaneousValues(); 1094 SaveMiscellaneousValues();
1066 } 1095 }
1096 SaveControlValues();
1067 SaveCoreValues(); 1097 SaveCoreValues();
1068 SaveCpuValues(); 1098 SaveCpuValues();
1069 SaveRendererValues(); 1099 SaveRendererValues();
@@ -1088,9 +1118,14 @@ void Config::SaveAudioValues() {
1088void Config::SaveControlValues() { 1118void Config::SaveControlValues() {
1089 qt_config->beginGroup(QStringLiteral("Controls")); 1119 qt_config->beginGroup(QStringLiteral("Controls"));
1090 1120
1121 Settings::values.players.SetGlobal(!IsCustomConfig());
1091 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { 1122 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
1092 SavePlayerValue(p); 1123 SavePlayerValue(p);
1093 } 1124 }
1125 if (IsCustomConfig()) {
1126 qt_config->endGroup();
1127 return;
1128 }
1094 SaveDebugValues(); 1129 SaveDebugValues();
1095 SaveMouseValues(); 1130 SaveMouseValues();
1096 SaveTouchscreenValues(); 1131 SaveTouchscreenValues();
@@ -1257,6 +1292,7 @@ void Config::SaveCpuValues() {
1257 WriteBasicSetting(Settings::values.cpuopt_fastmem); 1292 WriteBasicSetting(Settings::values.cpuopt_fastmem);
1258 WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives); 1293 WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
1259 WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives); 1294 WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives);
1295 WriteBasicSetting(Settings::values.cpuopt_ignore_memory_aborts);
1260 } 1296 }
1261 1297
1262 qt_config->endGroup(); 1298 qt_config->endGroup();
@@ -1579,6 +1615,13 @@ void Config::SaveControlPlayerValue(std::size_t player_index) {
1579 qt_config->endGroup(); 1615 qt_config->endGroup();
1580} 1616}
1581 1617
1618void Config::ClearControlPlayerValues() {
1619 qt_config->beginGroup(QStringLiteral("Controls"));
1620 // If key is an empty string, all keys in the current group() are removed.
1621 qt_config->remove(QString{});
1622 qt_config->endGroup();
1623}
1624
1582const std::string& Config::GetConfigFilePath() const { 1625const std::string& Config::GetConfigFilePath() const {
1583 return qt_config_loc; 1626 return qt_config_loc;
1584} 1627}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 06fa7d2d0..7d26e9ab6 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -34,6 +34,7 @@ public:
34 34
35 void ReadControlPlayerValue(std::size_t player_index); 35 void ReadControlPlayerValue(std::size_t player_index);
36 void SaveControlPlayerValue(std::size_t player_index); 36 void SaveControlPlayerValue(std::size_t player_index);
37 void ClearControlPlayerValues();
37 38
38 const std::string& GetConfigFilePath() const; 39 const std::string& GetConfigFilePath() const;
39 40
@@ -58,6 +59,7 @@ public:
58 59
59private: 60private:
60 void Initialize(const std::string& config_name); 61 void Initialize(const std::string& config_name);
62 bool IsCustomConfig();
61 63
62 void ReadValues(); 64 void ReadValues();
63 void ReadPlayerValue(std::size_t player_index); 65 void ReadPlayerValue(std::size_t player_index);
diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp
index 3c302ec16..8cfef0cc1 100644
--- a/src/yuzu/configuration/configure_cpu_debug.cpp
+++ b/src/yuzu/configuration/configure_cpu_debug.cpp
@@ -45,6 +45,9 @@ void ConfigureCpuDebug::SetConfiguration() {
45 ui->cpuopt_recompile_exclusives->setEnabled(runtime_lock); 45 ui->cpuopt_recompile_exclusives->setEnabled(runtime_lock);
46 ui->cpuopt_recompile_exclusives->setChecked( 46 ui->cpuopt_recompile_exclusives->setChecked(
47 Settings::values.cpuopt_recompile_exclusives.GetValue()); 47 Settings::values.cpuopt_recompile_exclusives.GetValue());
48 ui->cpuopt_ignore_memory_aborts->setEnabled(runtime_lock);
49 ui->cpuopt_ignore_memory_aborts->setChecked(
50 Settings::values.cpuopt_ignore_memory_aborts.GetValue());
48} 51}
49 52
50void ConfigureCpuDebug::ApplyConfiguration() { 53void ConfigureCpuDebug::ApplyConfiguration() {
@@ -59,6 +62,7 @@ void ConfigureCpuDebug::ApplyConfiguration() {
59 Settings::values.cpuopt_fastmem = ui->cpuopt_fastmem->isChecked(); 62 Settings::values.cpuopt_fastmem = ui->cpuopt_fastmem->isChecked();
60 Settings::values.cpuopt_fastmem_exclusives = ui->cpuopt_fastmem_exclusives->isChecked(); 63 Settings::values.cpuopt_fastmem_exclusives = ui->cpuopt_fastmem_exclusives->isChecked();
61 Settings::values.cpuopt_recompile_exclusives = ui->cpuopt_recompile_exclusives->isChecked(); 64 Settings::values.cpuopt_recompile_exclusives = ui->cpuopt_recompile_exclusives->isChecked();
65 Settings::values.cpuopt_ignore_memory_aborts = ui->cpuopt_ignore_memory_aborts->isChecked();
62} 66}
63 67
64void ConfigureCpuDebug::changeEvent(QEvent* event) { 68void ConfigureCpuDebug::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui
index 2bc268810..3010f7fad 100644
--- a/src/yuzu/configuration/configure_cpu_debug.ui
+++ b/src/yuzu/configuration/configure_cpu_debug.ui
@@ -175,6 +175,19 @@
175 </property> 175 </property>
176 </widget> 176 </widget>
177 </item> 177 </item>
178 <item>
179 <widget class="QCheckBox" name="cpuopt_ignore_memory_aborts">
180 <property name="toolTip">
181 <string>
182 &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by allowing invalid memory accesses to succeed.&lt;/div&gt;
183 &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it reduces the overhead of all memory accesses and has no impact on programs that don't access invalid memory.&lt;/div&gt;
184 </string>
185 </property>
186 <property name="text">
187 <string>Enable fallbacks for invalid memory accesses</string>
188 </property>
189 </widget>
190 </item>
178 </layout> 191 </layout>
179 </widget> 192 </widget>
180 </item> 193 </item>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index f1385e972..e9388daad 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -31,7 +31,7 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
31 31
32 ui->backend->addItem(QStringLiteral("GLSL")); 32 ui->backend->addItem(QStringLiteral("GLSL"));
33 ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)")); 33 ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)"));
34 ui->backend->addItem(QStringLiteral("SPIR-V (Experimental, Mesa Only)")); 34 ui->backend->addItem(tr("SPIR-V (Experimental, Mesa Only)"));
35 35
36 SetupPerGameUI(); 36 SetupPerGameUI();
37 37
@@ -260,6 +260,7 @@ void ConfigureGraphics::ApplyConfiguration() {
260 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); 260 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
261 switch (GetCurrentGraphicsBackend()) { 261 switch (GetCurrentGraphicsBackend()) {
262 case Settings::RendererBackend::OpenGL: 262 case Settings::RendererBackend::OpenGL:
263 case Settings::RendererBackend::Null:
263 Settings::values.shader_backend.SetGlobal(false); 264 Settings::values.shader_backend.SetGlobal(false);
264 Settings::values.vulkan_device.SetGlobal(true); 265 Settings::values.vulkan_device.SetGlobal(true);
265 Settings::values.shader_backend.SetValue(shader_backend); 266 Settings::values.shader_backend.SetValue(shader_backend);
@@ -348,6 +349,10 @@ void ConfigureGraphics::UpdateAPILayout() {
348 ui->device_widget->setVisible(true); 349 ui->device_widget->setVisible(true);
349 ui->backend_widget->setVisible(false); 350 ui->backend_widget->setVisible(false);
350 break; 351 break;
352 case Settings::RendererBackend::Null:
353 ui->device_widget->setVisible(false);
354 ui->backend_widget->setVisible(false);
355 break;
351 } 356 }
352} 357}
353 358
@@ -360,7 +365,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
360 365
361 vk::InstanceDispatch dld; 366 vk::InstanceDispatch dld;
362 const Common::DynamicLibrary library = OpenLibrary(); 367 const Common::DynamicLibrary library = OpenLibrary();
363 const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_0); 368 const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1);
364 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices(); 369 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
365 370
366 vulkan_devices.clear(); 371 vulkan_devices.clear();
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 37271f956..f78396690 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -139,6 +139,11 @@
139 <string notr="true">Vulkan</string> 139 <string notr="true">Vulkan</string>
140 </property> 140 </property>
141 </item> 141 </item>
142 <item>
143 <property name="text">
144 <string>None</string>
145 </property>
146 </item>
142 </widget> 147 </widget>
143 </item> 148 </item>
144 </layout> 149 </layout>
diff --git a/src/yuzu/configuration/configure_input_per_game.cpp b/src/yuzu/configuration/configure_input_per_game.cpp
new file mode 100644
index 000000000..78e65d468
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_per_game.cpp
@@ -0,0 +1,115 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "core/core.h"
6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h"
8#include "ui_configure_input_per_game.h"
9#include "yuzu/configuration/config.h"
10#include "yuzu/configuration/configure_input_per_game.h"
11#include "yuzu/configuration/input_profiles.h"
12
13ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, Config* config_,
14 QWidget* parent)
15 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()),
16 profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} {
17 ui->setupUi(this);
18 const std::array labels = {
19 ui->label_player_1, ui->label_player_2, ui->label_player_3, ui->label_player_4,
20 ui->label_player_5, ui->label_player_6, ui->label_player_7, ui->label_player_8,
21 };
22 profile_comboboxes = {
23 ui->profile_player_1, ui->profile_player_2, ui->profile_player_3, ui->profile_player_4,
24 ui->profile_player_5, ui->profile_player_6, ui->profile_player_7, ui->profile_player_8,
25 };
26
27 Settings::values.players.SetGlobal(false);
28
29 const auto& profile_names = profiles->GetInputProfileNames();
30 const auto populate_profiles = [this, &profile_names](size_t player_index) {
31 const auto previous_profile =
32 Settings::values.players.GetValue()[player_index].profile_name;
33
34 auto* const player_combobox = profile_comboboxes[player_index];
35 player_combobox->addItem(tr("Use global input configuration"));
36
37 for (size_t index = 0; index < profile_names.size(); ++index) {
38 const auto& profile_name = profile_names[index];
39 player_combobox->addItem(QString::fromStdString(profile_name));
40 if (profile_name == previous_profile) {
41 // offset by 1 since the first element is the global config
42 player_combobox->setCurrentIndex(static_cast<int>(index + 1));
43 }
44 }
45 };
46 for (size_t index = 0; index < profile_comboboxes.size(); ++index) {
47 labels[index]->setText(tr("Player %1 profile").arg(index + 1));
48 populate_profiles(index);
49 }
50
51 LoadConfiguration();
52}
53
54void ConfigureInputPerGame::ApplyConfiguration() {
55 LoadConfiguration();
56 SaveConfiguration();
57}
58
59void ConfigureInputPerGame::LoadConfiguration() {
60 static constexpr size_t HANDHELD_INDEX = 8;
61
62 auto& hid_core = system.HIDCore();
63 for (size_t player_index = 0; player_index < profile_comboboxes.size(); ++player_index) {
64 Settings::values.players.SetGlobal(false);
65
66 auto* emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index);
67 auto* const player_combobox = profile_comboboxes[player_index];
68
69 const auto selection_index = player_combobox->currentIndex();
70 if (selection_index == 0) {
71 Settings::values.players.GetValue()[player_index].profile_name = "";
72 if (player_index == 0) {
73 Settings::values.players.GetValue()[HANDHELD_INDEX] = {};
74 }
75 Settings::values.players.SetGlobal(true);
76 emulated_controller->ReloadFromSettings();
77 continue;
78 }
79 const auto profile_name = player_combobox->itemText(selection_index).toStdString();
80 if (profile_name.empty()) {
81 continue;
82 }
83 auto& player = Settings::values.players.GetValue()[player_index];
84 player.profile_name = profile_name;
85 // Read from the profile into the custom player settings
86 profiles->LoadProfile(profile_name, player_index);
87 // Make sure the controller is connected
88 player.connected = true;
89
90 emulated_controller->ReloadFromSettings();
91
92 if (player_index > 0) {
93 continue;
94 }
95 // Handle Handheld cases
96 auto& handheld_player = Settings::values.players.GetValue()[HANDHELD_INDEX];
97 auto* handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
98 if (player.controller_type == Settings::ControllerType::Handheld) {
99 handheld_player = player;
100 } else {
101 handheld_player = {};
102 }
103 handheld_controller->ReloadFromSettings();
104 }
105}
106
107void ConfigureInputPerGame::SaveConfiguration() {
108 Settings::values.players.SetGlobal(false);
109
110 // Clear all controls from the config in case the user reverted back to globals
111 config->ClearControlPlayerValues();
112 for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) {
113 config->SaveControlPlayerValue(index);
114 }
115}
diff --git a/src/yuzu/configuration/configure_input_per_game.h b/src/yuzu/configuration/configure_input_per_game.h
new file mode 100644
index 000000000..660faf574
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_per_game.h
@@ -0,0 +1,45 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include <QWidget>
9
10#include "ui_configure_input_per_game.h"
11#include "yuzu/configuration/input_profiles.h"
12
13class QComboBox;
14
15namespace Core {
16class System;
17} // namespace Core
18
19class Config;
20
21class ConfigureInputPerGame : public QWidget {
22 Q_OBJECT
23
24public:
25 explicit ConfigureInputPerGame(Core::System& system_, Config* config_,
26 QWidget* parent = nullptr);
27
28 /// Load and Save configurations to settings file.
29 void ApplyConfiguration();
30
31private:
32 /// Load configuration from settings file.
33 void LoadConfiguration();
34
35 /// Save configuration to settings file.
36 void SaveConfiguration();
37
38 std::unique_ptr<Ui::ConfigureInputPerGame> ui;
39 std::unique_ptr<InputProfiles> profiles;
40
41 std::array<QComboBox*, 8> profile_comboboxes;
42
43 Core::System& system;
44 Config* config;
45};
diff --git a/src/yuzu/configuration/configure_input_per_game.ui b/src/yuzu/configuration/configure_input_per_game.ui
new file mode 100644
index 000000000..fbd8eab1c
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_per_game.ui
@@ -0,0 +1,333 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputPerGame</class>
4 <widget class="QWidget" name="PerGameInput">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>541</width>
10 <height>759</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <property name="accessibleName">
17 <string>Graphics</string>
18 </property>
19 <layout class="QVBoxLayout" name="verticalLayout_1">
20 <item>
21 <layout class="QVBoxLayout" name="verticalLayout_2">
22 <property name="spacing">
23 <number>0</number>
24 </property>
25 <item>
26 <widget class="QGroupBox" name="groupBox">
27 <property name="title">
28 <string>Input Profiles</string>
29 </property>
30 <layout class="QVBoxLayout" name="verticalLayout_4">
31 <item>
32 <widget class="QWidget" name="player_1" native="true">
33 <layout class="QHBoxLayout" name="input_profile_layout_1">
34 <property name="leftMargin">
35 <number>0</number>
36 </property>
37 <property name="topMargin">
38 <number>0</number>
39 </property>
40 <property name="rightMargin">
41 <number>0</number>
42 </property>
43 <property name="bottomMargin">
44 <number>0</number>
45 </property>
46 <item>
47 <widget class="QLabel" name="label_player_1">
48 <property name="text">
49 <string>Player 1 Profile</string>
50 </property>
51 </widget>
52 </item>
53 <item>
54 <widget class="QComboBox" name="profile_player_1">
55 <property name="sizePolicy">
56 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
57 <horstretch>0</horstretch>
58 <verstretch>0</verstretch>
59 </sizepolicy>
60 </property>
61 </widget>
62 </item>
63 </layout>
64 </widget>
65 </item>
66 <item>
67 <widget class="QWidget" name="player_2" native="true">
68 <layout class="QHBoxLayout" name="input_profile_layout_2">
69 <property name="leftMargin">
70 <number>0</number>
71 </property>
72 <property name="topMargin">
73 <number>0</number>
74 </property>
75 <property name="rightMargin">
76 <number>0</number>
77 </property>
78 <property name="bottomMargin">
79 <number>0</number>
80 </property>
81 <item>
82 <widget class="QLabel" name="label_player_2">
83 <property name="text">
84 <string>Player 2 Profile</string>
85 </property>
86 </widget>
87 </item>
88 <item>
89 <widget class="QComboBox" name="profile_player_2">
90 <property name="sizePolicy">
91 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
92 <horstretch>0</horstretch>
93 <verstretch>0</verstretch>
94 </sizepolicy>
95 </property>
96 </widget>
97 </item>
98 </layout>
99 </widget>
100 </item>
101 <item>
102 <widget class="QWidget" name="player_3" native="true">
103 <layout class="QHBoxLayout" name="input_profile_layout_3">
104 <property name="leftMargin">
105 <number>0</number>
106 </property>
107 <property name="topMargin">
108 <number>0</number>
109 </property>
110 <property name="rightMargin">
111 <number>0</number>
112 </property>
113 <property name="bottomMargin">
114 <number>0</number>
115 </property>
116 <item>
117 <widget class="QLabel" name="label_player_3">
118 <property name="text">
119 <string>Player 3 Profile</string>
120 </property>
121 </widget>
122 </item>
123 <item>
124 <widget class="QComboBox" name="profile_player_3">
125 <property name="sizePolicy">
126 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
127 <horstretch>0</horstretch>
128 <verstretch>0</verstretch>
129 </sizepolicy>
130 </property>
131 </widget>
132 </item>
133 </layout>
134 </widget>
135 </item>
136 <item>
137 <widget class="QWidget" name="player_4" native="true">
138 <layout class="QHBoxLayout" name="input_profile_layout_4">
139 <property name="leftMargin">
140 <number>0</number>
141 </property>
142 <property name="topMargin">
143 <number>0</number>
144 </property>
145 <property name="rightMargin">
146 <number>0</number>
147 </property>
148 <property name="bottomMargin">
149 <number>0</number>
150 </property>
151 <item>
152 <widget class="QLabel" name="label_player_4">
153 <property name="text">
154 <string>Player 4 Profile</string>
155 </property>
156 </widget>
157 </item>
158 <item>
159 <widget class="QComboBox" name="profile_player_4">
160 <property name="sizePolicy">
161 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
162 <horstretch>0</horstretch>
163 <verstretch>0</verstretch>
164 </sizepolicy>
165 </property>
166 </widget>
167 </item>
168 </layout>
169 </widget>
170 </item>
171 <item>
172 <widget class="QWidget" name="player_5" native="true">
173 <layout class="QHBoxLayout" name="input_profile_layout_5">
174 <property name="leftMargin">
175 <number>0</number>
176 </property>
177 <property name="topMargin">
178 <number>0</number>
179 </property>
180 <property name="rightMargin">
181 <number>0</number>
182 </property>
183 <property name="bottomMargin">
184 <number>0</number>
185 </property>
186 <item>
187 <widget class="QLabel" name="label_player_5">
188 <property name="text">
189 <string>Player 5 Profile</string>
190 </property>
191 </widget>
192 </item>
193 <item>
194 <widget class="QComboBox" name="profile_player_5">
195 <property name="sizePolicy">
196 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
197 <horstretch>0</horstretch>
198 <verstretch>0</verstretch>
199 </sizepolicy>
200 </property>
201 </widget>
202 </item>
203 </layout>
204 </widget>
205 </item>
206 <item>
207 <widget class="QWidget" name="player_6" native="true">
208 <layout class="QHBoxLayout" name="input_profile_layout_6">
209 <property name="leftMargin">
210 <number>0</number>
211 </property>
212 <property name="topMargin">
213 <number>0</number>
214 </property>
215 <property name="rightMargin">
216 <number>0</number>
217 </property>
218 <property name="bottomMargin">
219 <number>0</number>
220 </property>
221 <item>
222 <widget class="QLabel" name="label_player_6">
223 <property name="text">
224 <string>Player 6 Profile</string>
225 </property>
226 </widget>
227 </item>
228 <item>
229 <widget class="QComboBox" name="profile_player_6">
230 <property name="sizePolicy">
231 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
232 <horstretch>0</horstretch>
233 <verstretch>0</verstretch>
234 </sizepolicy>
235 </property>
236 </widget>
237 </item>
238 </layout>
239 </widget>
240 </item>
241 <item>
242 <widget class="QWidget" name="player_7" native="true">
243 <layout class="QHBoxLayout" name="input_profile_layout_7">
244 <property name="leftMargin">
245 <number>0</number>
246 </property>
247 <property name="topMargin">
248 <number>0</number>
249 </property>
250 <property name="rightMargin">
251 <number>0</number>
252 </property>
253 <property name="bottomMargin">
254 <number>0</number>
255 </property>
256 <item>
257 <widget class="QLabel" name="label_player_7">
258 <property name="text">
259 <string>Player 7 Profile</string>
260 </property>
261 </widget>
262 </item>
263 <item>
264 <widget class="QComboBox" name="profile_player_7">
265 <property name="sizePolicy">
266 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
267 <horstretch>0</horstretch>
268 <verstretch>0</verstretch>
269 </sizepolicy>
270 </property>
271 </widget>
272 </item>
273 </layout>
274 </widget>
275 </item>
276 <item>
277 <widget class="QWidget" name="player_8" native="true">
278 <layout class="QHBoxLayout" name="input_profile_layout_8">
279 <property name="leftMargin">
280 <number>0</number>
281 </property>
282 <property name="topMargin">
283 <number>0</number>
284 </property>
285 <property name="rightMargin">
286 <number>0</number>
287 </property>
288 <property name="bottomMargin">
289 <number>0</number>
290 </property>
291 <item>
292 <widget class="QLabel" name="label_player_8">
293 <property name="text">
294 <string>Player 8 Profile</string>
295 </property>
296 </widget>
297 </item>
298 <item>
299 <widget class="QComboBox" name="profile_player_8">
300 <property name="sizePolicy">
301 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
302 <horstretch>0</horstretch>
303 <verstretch>0</verstretch>
304 </sizepolicy>
305 </property>
306 </widget>
307 </item>
308 </layout>
309 </widget>
310 </item>
311 </layout>
312 </widget>
313 </item>
314 </layout>
315 </item>
316 <item>
317 <spacer name="verticalSpacer">
318 <property name="orientation">
319 <enum>Qt::Vertical</enum>
320 </property>
321 <property name="sizeHint" stdset="0">
322 <size>
323 <width>20</width>
324 <height>40</height>
325 </size>
326 </property>
327 </spacer>
328 </item>
329 </layout>
330 </widget>
331 <resources/>
332 <connections/>
333</ui>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 9e5a40fe7..b1575b0d3 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -855,8 +855,7 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
855 return; 855 return;
856 } 856 }
857 857
858 const auto devices = 858 const auto devices = emulated_controller->GetMappedDevices();
859 emulated_controller->GetMappedDevices(Core::HID::EmulatedDeviceIndex::AllDevices);
860 UpdateInputDevices(); 859 UpdateInputDevices();
861 860
862 if (devices.empty()) { 861 if (devices.empty()) {
@@ -1553,6 +1552,7 @@ void ConfigureInputPlayer::LoadProfile() {
1553} 1552}
1554 1553
1555void ConfigureInputPlayer::SaveProfile() { 1554void ConfigureInputPlayer::SaveProfile() {
1555 static constexpr size_t HANDHELD_INDEX = 8;
1556 const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); 1556 const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
1557 1557
1558 if (profile_name.isEmpty()) { 1558 if (profile_name.isEmpty()) {
@@ -1561,7 +1561,12 @@ void ConfigureInputPlayer::SaveProfile() {
1561 1561
1562 ApplyConfiguration(); 1562 ApplyConfiguration();
1563 1563
1564 if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) { 1564 // When we're in handheld mode, only the handheld emulated controller bindings are updated
1565 const bool is_handheld = player_index == 0 && emulated_controller->GetNpadIdType() ==
1566 Core::HID::NpadIdType::Handheld;
1567 const auto profile_player_index = is_handheld ? HANDHELD_INDEX : player_index;
1568
1569 if (!profiles->SaveProfile(profile_name.toStdString(), profile_player_index)) {
1565 QMessageBox::critical(this, tr("Save Input Profile"), 1570 QMessageBox::critical(this, tr("Save Input Profile"),
1566 tr("Failed to save the input profile \"%1\"").arg(profile_name)); 1571 tr("Failed to save the input profile \"%1\"").arg(profile_name));
1567 UpdateInputProfiles(); 1572 UpdateInputProfiles();
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 79434fdd8..26f60d121 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -38,7 +38,7 @@ enum class InputType;
38 38
39namespace Ui { 39namespace Ui {
40class ConfigureInputPlayer; 40class ConfigureInputPlayer;
41} 41} // namespace Ui
42 42
43namespace Core::HID { 43namespace Core::HID {
44class HIDCore; 44class HIDCore;
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index c3cb8f61d..93db47cfd 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -28,7 +28,7 @@
28#include "yuzu/configuration/configure_general.h" 28#include "yuzu/configuration/configure_general.h"
29#include "yuzu/configuration/configure_graphics.h" 29#include "yuzu/configuration/configure_graphics.h"
30#include "yuzu/configuration/configure_graphics_advanced.h" 30#include "yuzu/configuration/configure_graphics_advanced.h"
31#include "yuzu/configuration/configure_input.h" 31#include "yuzu/configuration/configure_input_per_game.h"
32#include "yuzu/configuration/configure_per_game.h" 32#include "yuzu/configuration/configure_per_game.h"
33#include "yuzu/configuration/configure_per_game_addons.h" 33#include "yuzu/configuration/configure_per_game_addons.h"
34#include "yuzu/configuration/configure_system.h" 34#include "yuzu/configuration/configure_system.h"
@@ -50,6 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
50 general_tab = std::make_unique<ConfigureGeneral>(system_, this); 50 general_tab = std::make_unique<ConfigureGeneral>(system_, this);
51 graphics_tab = std::make_unique<ConfigureGraphics>(system_, this); 51 graphics_tab = std::make_unique<ConfigureGraphics>(system_, this);
52 graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this); 52 graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this);
53 input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this);
53 system_tab = std::make_unique<ConfigureSystem>(system_, this); 54 system_tab = std::make_unique<ConfigureSystem>(system_, this);
54 55
55 ui->setupUi(this); 56 ui->setupUi(this);
@@ -61,6 +62,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
61 ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); 62 ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
62 ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics")); 63 ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics"));
63 ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); 64 ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
65 ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles"));
64 66
65 setFocusPolicy(Qt::ClickFocus); 67 setFocusPolicy(Qt::ClickFocus);
66 setWindowTitle(tr("Properties")); 68 setWindowTitle(tr("Properties"));
@@ -91,6 +93,7 @@ void ConfigurePerGame::ApplyConfiguration() {
91 graphics_tab->ApplyConfiguration(); 93 graphics_tab->ApplyConfiguration();
92 graphics_advanced_tab->ApplyConfiguration(); 94 graphics_advanced_tab->ApplyConfiguration();
93 audio_tab->ApplyConfiguration(); 95 audio_tab->ApplyConfiguration();
96 input_tab->ApplyConfiguration();
94 97
95 system.ApplySettings(); 98 system.ApplySettings();
96 Settings::LogSettings(); 99 Settings::LogSettings();
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index 17a98a0f3..4ecc43541 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -16,12 +16,17 @@ namespace Core {
16class System; 16class System;
17} 17}
18 18
19namespace InputCommon {
20class InputSubsystem;
21}
22
19class ConfigurePerGameAddons; 23class ConfigurePerGameAddons;
20class ConfigureAudio; 24class ConfigureAudio;
21class ConfigureCpu; 25class ConfigureCpu;
22class ConfigureGeneral; 26class ConfigureGeneral;
23class ConfigureGraphics; 27class ConfigureGraphics;
24class ConfigureGraphicsAdvanced; 28class ConfigureGraphicsAdvanced;
29class ConfigureInputPerGame;
25class ConfigureSystem; 30class ConfigureSystem;
26 31
27class QGraphicsScene; 32class QGraphicsScene;
@@ -72,5 +77,6 @@ private:
72 std::unique_ptr<ConfigureGeneral> general_tab; 77 std::unique_ptr<ConfigureGeneral> general_tab;
73 std::unique_ptr<ConfigureGraphics> graphics_tab; 78 std::unique_ptr<ConfigureGraphics> graphics_tab;
74 std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab; 79 std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab;
80 std::unique_ptr<ConfigureInputPerGame> input_tab;
75 std::unique_ptr<ConfigureSystem> system_tab; 81 std::unique_ptr<ConfigureSystem> system_tab;
76}; 82};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index c21153560..c0afb2e5f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -126,6 +126,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
126#include "yuzu/compatibility_list.h" 126#include "yuzu/compatibility_list.h"
127#include "yuzu/configuration/config.h" 127#include "yuzu/configuration/config.h"
128#include "yuzu/configuration/configure_dialog.h" 128#include "yuzu/configuration/configure_dialog.h"
129#include "yuzu/configuration/configure_input_per_game.h"
129#include "yuzu/debugger/console.h" 130#include "yuzu/debugger/console.h"
130#include "yuzu/debugger/controller.h" 131#include "yuzu/debugger/controller.h"
131#include "yuzu/debugger/profiler.h" 132#include "yuzu/debugger/profiler.h"
@@ -1012,29 +1013,11 @@ void GMainWindow::InitializeWidgets() {
1012 renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); 1013 renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton"));
1013 renderer_status_button->setCheckable(true); 1014 renderer_status_button->setCheckable(true);
1014 renderer_status_button->setFocusPolicy(Qt::NoFocus); 1015 renderer_status_button->setFocusPolicy(Qt::NoFocus);
1015 connect(renderer_status_button, &QPushButton::toggled, [this](bool checked) { 1016 connect(renderer_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleGraphicsAPI);
1016 renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); 1017 UpdateAPIText();
1017 }); 1018 renderer_status_button->setCheckable(true);
1018 renderer_status_button->toggle();
1019
1020 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 1019 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
1021 Settings::RendererBackend::Vulkan); 1020 Settings::RendererBackend::Vulkan);
1022 connect(renderer_status_button, &QPushButton::clicked, [this] {
1023 if (emulation_running) {
1024 return;
1025 }
1026 if (renderer_status_button->isChecked()) {
1027 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan);
1028 } else {
1029 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL);
1030 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
1031 Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor);
1032 UpdateFilterText();
1033 }
1034 }
1035
1036 system->ApplySettings();
1037 });
1038 statusBar()->insertPermanentWidget(0, renderer_status_button); 1021 statusBar()->insertPermanentWidget(0, renderer_status_button);
1039 1022
1040 statusBar()->setVisible(true); 1023 statusBar()->setVisible(true);
@@ -1676,6 +1659,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1676 LOG_INFO(Frontend, "yuzu starting..."); 1659 LOG_INFO(Frontend, "yuzu starting...");
1677 StoreRecentFile(filename); // Put the filename on top of the list 1660 StoreRecentFile(filename); // Put the filename on top of the list
1678 1661
1662 // Save configurations
1663 UpdateUISettings();
1664 game_list->SaveInterfaceLayout();
1665 config->Save();
1666
1679 u64 title_id{0}; 1667 u64 title_id{0};
1680 1668
1681 last_filename_booted = filename; 1669 last_filename_booted = filename;
@@ -1692,14 +1680,10 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1692 ? Common::FS::PathToUTF8String(file_path.filename()) 1680 ? Common::FS::PathToUTF8String(file_path.filename())
1693 : fmt::format("{:016X}", title_id); 1681 : fmt::format("{:016X}", title_id);
1694 Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); 1682 Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
1683 system->HIDCore().ReloadInputDevices();
1695 system->ApplySettings(); 1684 system->ApplySettings();
1696 } 1685 }
1697 1686
1698 // Save configurations
1699 UpdateUISettings();
1700 game_list->SaveInterfaceLayout();
1701 config->Save();
1702
1703 Settings::LogSettings(); 1687 Settings::LogSettings();
1704 1688
1705 if (UISettings::values.select_user_on_boot) { 1689 if (UISettings::values.select_user_on_boot) {
@@ -2820,6 +2804,7 @@ void GMainWindow::OnStopGame() {
2820 ShutdownGame(); 2804 ShutdownGame();
2821 2805
2822 Settings::RestoreGlobalState(system->IsPoweredOn()); 2806 Settings::RestoreGlobalState(system->IsPoweredOn());
2807 system->HIDCore().ReloadInputDevices();
2823 UpdateStatusButtons(); 2808 UpdateStatusButtons();
2824} 2809}
2825 2810
@@ -2850,6 +2835,7 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex
2850} 2835}
2851 2836
2852void GMainWindow::OnMenuReportCompatibility() { 2837void GMainWindow::OnMenuReportCompatibility() {
2838#if defined(ARCHITECTURE_x86_64) && !defined(__APPLE__)
2853 const auto& caps = Common::GetCPUCaps(); 2839 const auto& caps = Common::GetCPUCaps();
2854 const bool has_fma = caps.fma || caps.fma4; 2840 const bool has_fma = caps.fma || caps.fma4;
2855 const auto processor_count = std::thread::hardware_concurrency(); 2841 const auto processor_count = std::thread::hardware_concurrency();
@@ -2876,6 +2862,11 @@ void GMainWindow::OnMenuReportCompatibility() {
2876 "&gt; " 2862 "&gt; "
2877 "Web.")); 2863 "Web."));
2878 } 2864 }
2865#else
2866 QMessageBox::critical(this, tr("Hardware requirements not met"),
2867 tr("Your system does not meet the recommended hardware requirements. "
2868 "Compatibility reporting has been disabled."));
2869#endif
2879} 2870}
2880 2871
2881void GMainWindow::OpenURL(const QUrl& url) { 2872void GMainWindow::OpenURL(const QUrl& url) {
@@ -3253,6 +3244,18 @@ void GMainWindow::OnToggleAdaptingFilter() {
3253 UpdateFilterText(); 3244 UpdateFilterText();
3254} 3245}
3255 3246
3247void GMainWindow::OnToggleGraphicsAPI() {
3248 auto api = Settings::values.renderer_backend.GetValue();
3249 if (api == Settings::RendererBackend::OpenGL) {
3250 api = Settings::RendererBackend::Vulkan;
3251 } else {
3252 api = Settings::RendererBackend::OpenGL;
3253 }
3254 Settings::values.renderer_backend.SetValue(api);
3255 renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan);
3256 UpdateAPIText();
3257}
3258
3256void GMainWindow::OnConfigurePerGame() { 3259void GMainWindow::OnConfigurePerGame() {
3257 const u64 title_id = system->GetCurrentProcessProgramID(); 3260 const u64 title_id = system->GetCurrentProcessProgramID();
3258 OpenPerGameConfiguration(title_id, current_game_path.toStdString()); 3261 OpenPerGameConfiguration(title_id, current_game_path.toStdString());
@@ -3281,6 +3284,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
3281 // Do not cause the global config to write local settings into the config file 3284 // Do not cause the global config to write local settings into the config file
3282 const bool is_powered_on = system->IsPoweredOn(); 3285 const bool is_powered_on = system->IsPoweredOn();
3283 Settings::RestoreGlobalState(is_powered_on); 3286 Settings::RestoreGlobalState(is_powered_on);
3287 system->HIDCore().ReloadInputDevices();
3284 3288
3285 UISettings::values.configuration_applied = false; 3289 UISettings::values.configuration_applied = false;
3286 3290
@@ -3573,6 +3577,21 @@ void GMainWindow::UpdateDockedButton() {
3573 dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD")); 3577 dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD"));
3574} 3578}
3575 3579
3580void GMainWindow::UpdateAPIText() {
3581 const auto api = Settings::values.renderer_backend.GetValue();
3582 switch (api) {
3583 case Settings::RendererBackend::OpenGL:
3584 renderer_status_button->setText(tr("OPENGL"));
3585 break;
3586 case Settings::RendererBackend::Vulkan:
3587 renderer_status_button->setText(tr("VULKAN"));
3588 break;
3589 case Settings::RendererBackend::Null:
3590 renderer_status_button->setText(tr("NULL"));
3591 break;
3592 }
3593}
3594
3576void GMainWindow::UpdateFilterText() { 3595void GMainWindow::UpdateFilterText() {
3577 const auto filter = Settings::values.scaling_filter.GetValue(); 3596 const auto filter = Settings::values.scaling_filter.GetValue();
3578 switch (filter) { 3597 switch (filter) {
@@ -3618,6 +3637,7 @@ void GMainWindow::UpdateAAText() {
3618void GMainWindow::UpdateStatusButtons() { 3637void GMainWindow::UpdateStatusButtons() {
3619 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 3638 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
3620 Settings::RendererBackend::Vulkan); 3639 Settings::RendererBackend::Vulkan);
3640 UpdateAPIText();
3621 UpdateGPUAccuracyButton(); 3641 UpdateGPUAccuracyButton();
3622 UpdateDockedButton(); 3642 UpdateDockedButton();
3623 UpdateFilterText(); 3643 UpdateFilterText();
@@ -3748,6 +3768,7 @@ void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string detai
3748 ShutdownGame(); 3768 ShutdownGame();
3749 3769
3750 Settings::RestoreGlobalState(system->IsPoweredOn()); 3770 Settings::RestoreGlobalState(system->IsPoweredOn());
3771 system->HIDCore().ReloadInputDevices();
3751 UpdateStatusButtons(); 3772 UpdateStatusButtons();
3752 } 3773 }
3753 } else { 3774 } else {
@@ -3899,18 +3920,19 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
3899 // Unload controllers early 3920 // Unload controllers early
3900 controller_dialog->UnloadController(); 3921 controller_dialog->UnloadController();
3901 game_list->UnloadController(); 3922 game_list->UnloadController();
3902 system->HIDCore().UnloadInputDevices();
3903 3923
3904 // Shutdown session if the emu thread is active... 3924 // Shutdown session if the emu thread is active...
3905 if (emu_thread != nullptr) { 3925 if (emu_thread != nullptr) {
3906 ShutdownGame(); 3926 ShutdownGame();
3907 3927
3908 Settings::RestoreGlobalState(system->IsPoweredOn()); 3928 Settings::RestoreGlobalState(system->IsPoweredOn());
3929 system->HIDCore().ReloadInputDevices();
3909 UpdateStatusButtons(); 3930 UpdateStatusButtons();
3910 } 3931 }
3911 3932
3912 render_window->close(); 3933 render_window->close();
3913 multiplayer_state->Close(); 3934 multiplayer_state->Close();
3935 system->HIDCore().UnloadInputDevices();
3914 system->GetRoomNetwork().Shutdown(); 3936 system->GetRoomNetwork().Shutdown();
3915 3937
3916 QWidget::closeEvent(event); 3938 QWidget::closeEvent(event);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 4f9c3b450..62d629973 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -307,6 +307,7 @@ private slots:
307 void OnTasStartStop(); 307 void OnTasStartStop();
308 void OnTasRecord(); 308 void OnTasRecord();
309 void OnTasReset(); 309 void OnTasReset();
310 void OnToggleGraphicsAPI();
310 void OnToggleDockedMode(); 311 void OnToggleDockedMode();
311 void OnToggleGpuAccuracy(); 312 void OnToggleGpuAccuracy();
312 void OnToggleAdaptingFilter(); 313 void OnToggleAdaptingFilter();
@@ -347,6 +348,7 @@ private:
347 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, 348 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
348 std::string_view gpu_vendor = {}); 349 std::string_view gpu_vendor = {});
349 void UpdateDockedButton(); 350 void UpdateDockedButton();
351 void UpdateAPIText();
350 void UpdateFilterText(); 352 void UpdateFilterText();
351 void UpdateAAText(); 353 void UpdateAAText();
352 void UpdateStatusBar(); 354 void UpdateStatusBar();
diff --git a/src/yuzu/multiplayer/chat_room.h b/src/yuzu/multiplayer/chat_room.h
index 01c70fad0..dd71ea4cd 100644
--- a/src/yuzu/multiplayer/chat_room.h
+++ b/src/yuzu/multiplayer/chat_room.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <unordered_map>
7#include <unordered_set> 8#include <unordered_set>
8#include <QDialog> 9#include <QDialog>
9#include <QSortFilterProxyModel> 10#include <QSortFilterProxyModel>
diff --git a/src/yuzu/precompiled_headers.h b/src/yuzu/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/yuzu/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index ccdcf10fa..563818362 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -27,7 +27,7 @@ void CheckVulkan() {
27 Vulkan::vk::InstanceDispatch dld; 27 Vulkan::vk::InstanceDispatch dld;
28 const Common::DynamicLibrary library = Vulkan::OpenLibrary(); 28 const Common::DynamicLibrary library = Vulkan::OpenLibrary();
29 const Vulkan::vk::Instance instance = 29 const Vulkan::vk::Instance instance =
30 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); 30 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1);
31 31
32 } catch (const Vulkan::vk::Exception& exception) { 32 } catch (const Vulkan::vk::Exception& exception) {
33 fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what()); 33 fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what());
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index 7d8ca3d8a..f6eeb9d8d 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -22,8 +22,11 @@ add_executable(yuzu-cmd
22 emu_window/emu_window_sdl2.h 22 emu_window/emu_window_sdl2.h
23 emu_window/emu_window_sdl2_gl.cpp 23 emu_window/emu_window_sdl2_gl.cpp
24 emu_window/emu_window_sdl2_gl.h 24 emu_window/emu_window_sdl2_gl.h
25 emu_window/emu_window_sdl2_null.cpp
26 emu_window/emu_window_sdl2_null.h
25 emu_window/emu_window_sdl2_vk.cpp 27 emu_window/emu_window_sdl2_vk.cpp
26 emu_window/emu_window_sdl2_vk.h 28 emu_window/emu_window_sdl2_vk.h
29 precompiled_headers.h
27 yuzu.cpp 30 yuzu.cpp
28 yuzu.rc 31 yuzu.rc
29) 32)
@@ -31,21 +34,16 @@ add_executable(yuzu-cmd
31create_target_directory_groups(yuzu-cmd) 34create_target_directory_groups(yuzu-cmd)
32 35
33target_link_libraries(yuzu-cmd PRIVATE common core input_common) 36target_link_libraries(yuzu-cmd PRIVATE common core input_common)
34target_link_libraries(yuzu-cmd PRIVATE inih glad) 37target_link_libraries(yuzu-cmd PRIVATE inih::INIReader glad)
35if (MSVC) 38if (MSVC)
36 target_link_libraries(yuzu-cmd PRIVATE getopt) 39 target_link_libraries(yuzu-cmd PRIVATE getopt)
37endif() 40endif()
38target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads) 41target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
39 42
40create_resource("../../dist/yuzu.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon") 43create_resource("../../dist/yuzu.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
41target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR}) 44target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR})
42 45
43target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include) 46target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2 Vulkan::Headers)
44
45if (YUZU_USE_EXTERNAL_SDL2)
46 target_compile_definitions(yuzu-cmd PRIVATE -DYUZU_USE_EXTERNAL_SDL2)
47 target_include_directories(yuzu-cmd PRIVATE ${PROJECT_BINARY_DIR}/externals/SDL/include)
48endif()
49 47
50if(UNIX AND NOT APPLE) 48if(UNIX AND NOT APPLE)
51 install(TARGETS yuzu-cmd) 49 install(TARGETS yuzu-cmd)
@@ -55,3 +53,7 @@ if (MSVC)
55 include(CopyYuzuSDLDeps) 53 include(CopyYuzuSDLDeps)
56 copy_yuzu_SDL_deps(yuzu-cmd) 54 copy_yuzu_SDL_deps(yuzu-cmd)
57endif() 55endif()
56
57if (YUZU_USE_PRECOMPILED_HEADERS)
58 target_precompile_headers(yuzu-cmd PRIVATE precompiled_headers.h)
59endif()
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 59f9c8e09..de9b220da 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -15,7 +15,7 @@
15#pragma clang diagnostic pop 15#pragma clang diagnostic pop
16#endif 16#endif
17 17
18#include <inih/cpp/INIReader.h> 18#include <INIReader.h>
19#include "common/fs/file.h" 19#include "common/fs/file.h"
20#include "common/fs/fs.h" 20#include "common/fs/fs.h"
21#include "common/fs/path_util.h" 21#include "common/fs/path_util.h"
@@ -286,6 +286,7 @@ void Config::ReadValues() {
286 ReadSetting("Cpu", Settings::values.cpuopt_fastmem); 286 ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
287 ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives); 287 ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
288 ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives); 288 ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
289 ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
289 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma); 290 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
290 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error); 291 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
291 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr); 292 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 5bbc3f532..6fcf04e1b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -208,6 +208,10 @@ cpuopt_fastmem_exclusives =
208# 0: Disabled, 1 (default): Enabled 208# 0: Disabled, 1 (default): Enabled
209cpuopt_recompile_exclusives = 209cpuopt_recompile_exclusives =
210 210
211# Enable optimization to ignore invalid memory accesses (faster guest memory access)
212# 0: Disabled, 1 (default): Enabled
213cpuopt_ignore_memory_aborts =
214
211# Enable unfuse FMA (improve performance on CPUs without FMA) 215# Enable unfuse FMA (improve performance on CPUs without FMA)
212# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. 216# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
213# 0: Disabled, 1 (default): Enabled 217# 0: Disabled, 1 (default): Enabled
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 90bb0b415..25c23e2a5 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -89,3 +89,5 @@ protected:
89 /// yuzu core instance 89 /// yuzu core instance
90 Core::System& system; 90 Core::System& system;
91}; 91};
92
93class DummyContext : public Core::Frontend::GraphicsContext {};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp
new file mode 100644
index 000000000..259192f3c
--- /dev/null
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstdlib>
5#include <memory>
6#include <string>
7
8#include <fmt/format.h>
9
10#include "common/logging/log.h"
11#include "common/scm_rev.h"
12#include "video_core/renderer_null/renderer_null.h"
13#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
14
15#ifdef YUZU_USE_EXTERNAL_SDL2
16// Include this before SDL.h to prevent the external from including a dummy
17#define USING_GENERATED_CONFIG_H
18#include <SDL_config.h>
19#endif
20
21#include <SDL.h>
22
23EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_,
24 Core::System& system_, bool fullscreen)
25 : EmuWindow_SDL2{input_subsystem_, system_} {
26 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
27 Common::g_scm_branch, Common::g_scm_desc);
28 render_window =
29 SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
30 Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
31 SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
32
33 SetWindowIcon();
34
35 if (fullscreen) {
36 Fullscreen();
37 ShowCursor(false);
38 }
39
40 OnResize();
41 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
42 SDL_PumpEvents();
43 LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Null)", Common::g_build_name,
44 Common::g_scm_branch, Common::g_scm_desc);
45}
46
47EmuWindow_SDL2_Null::~EmuWindow_SDL2_Null() = default;
48
49std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Null::CreateSharedContext() const {
50 return std::make_unique<DummyContext>();
51}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h
new file mode 100644
index 000000000..35aee286d
--- /dev/null
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "core/frontend/emu_window.h"
9#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
10
11namespace Core {
12class System;
13}
14
15namespace InputCommon {
16class InputSubsystem;
17}
18
19class EmuWindow_SDL2_Null final : public EmuWindow_SDL2 {
20public:
21 explicit EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_,
22 Core::System& system, bool fullscreen);
23 ~EmuWindow_SDL2_Null() override;
24
25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
26};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index 25948328c..9ed47d453 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -12,12 +12,6 @@
12#include "video_core/renderer_vulkan/renderer_vulkan.h" 12#include "video_core/renderer_vulkan/renderer_vulkan.h"
13#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 13#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
14 14
15#ifdef YUZU_USE_EXTERNAL_SDL2
16// Include this before SDL.h to prevent the external from including a dummy
17#define USING_GENERATED_CONFIG_H
18#include <SDL_config.h>
19#endif
20
21#include <SDL.h> 15#include <SDL.h>
22#include <SDL_syswm.h> 16#include <SDL_syswm.h>
23 17
@@ -51,11 +45,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
51 window_info.type = Core::Frontend::WindowSystemType::Windows; 45 window_info.type = Core::Frontend::WindowSystemType::Windows;
52 window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window); 46 window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
53 break; 47 break;
54#else
55 case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
56 LOG_CRITICAL(Frontend, "Window manager subsystem Windows not compiled");
57 std::exit(EXIT_FAILURE);
58 break;
59#endif 48#endif
60#ifdef SDL_VIDEO_DRIVER_X11 49#ifdef SDL_VIDEO_DRIVER_X11
61 case SDL_SYSWM_TYPE::SDL_SYSWM_X11: 50 case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
@@ -63,11 +52,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
63 window_info.display_connection = wm.info.x11.display; 52 window_info.display_connection = wm.info.x11.display;
64 window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window); 53 window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
65 break; 54 break;
66#else
67 case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
68 LOG_CRITICAL(Frontend, "Window manager subsystem X11 not compiled");
69 std::exit(EXIT_FAILURE);
70 break;
71#endif 55#endif
72#ifdef SDL_VIDEO_DRIVER_WAYLAND 56#ifdef SDL_VIDEO_DRIVER_WAYLAND
73 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: 57 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
@@ -75,14 +59,21 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
75 window_info.display_connection = wm.info.wl.display; 59 window_info.display_connection = wm.info.wl.display;
76 window_info.render_surface = wm.info.wl.surface; 60 window_info.render_surface = wm.info.wl.surface;
77 break; 61 break;
78#else 62#endif
79 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: 63#ifdef SDL_VIDEO_DRIVER_COCOA
80 LOG_CRITICAL(Frontend, "Window manager subsystem Wayland not compiled"); 64 case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA:
81 std::exit(EXIT_FAILURE); 65 window_info.type = Core::Frontend::WindowSystemType::Cocoa;
66 window_info.render_surface = SDL_Metal_CreateView(render_window);
67 break;
68#endif
69#ifdef SDL_VIDEO_DRIVER_ANDROID
70 case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID:
71 window_info.type = Core::Frontend::WindowSystemType::Android;
72 window_info.render_surface = reinterpret_cast<void*>(wm.info.android.window);
82 break; 73 break;
83#endif 74#endif
84 default: 75 default:
85 LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); 76 LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem);
86 std::exit(EXIT_FAILURE); 77 std::exit(EXIT_FAILURE);
87 break; 78 break;
88 } 79 }
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index e39ad754d..9467d164a 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -24,5 +24,3 @@ public:
24 24
25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
26}; 26};
27
28class DummyContext : public Core::Frontend::GraphicsContext {};
diff --git a/src/yuzu_cmd/precompiled_headers.h b/src/yuzu_cmd/precompiled_headers.h
new file mode 100644
index 000000000..aabae730b
--- /dev/null
+++ b/src/yuzu_cmd/precompiled_headers.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_precompiled_headers.h"
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index dfe5a30ea..a80649703 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -34,6 +34,7 @@
34#include "yuzu_cmd/config.h" 34#include "yuzu_cmd/config.h"
35#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 35#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
36#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" 36#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
37#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
37#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 38#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
38 39
39#ifdef _WIN32 40#ifdef _WIN32
@@ -317,6 +318,9 @@ int main(int argc, char** argv) {
317 case Settings::RendererBackend::Vulkan: 318 case Settings::RendererBackend::Vulkan:
318 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen); 319 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
319 break; 320 break;
321 case Settings::RendererBackend::Null:
322 emu_window = std::make_unique<EmuWindow_SDL2_Null>(&input_subsystem, system, fullscreen);
323 break;
320 } 324 }
321 325
322 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 326 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());