summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/device/audio_buffers.h8
-rw-r--r--src/audio_core/device/device_session.cpp6
-rw-r--r--src/audio_core/device/device_session.h5
-rw-r--r--src/audio_core/in/audio_in_system.cpp7
-rw-r--r--src/audio_core/out/audio_out_system.cpp7
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/assert.h4
-rw-r--r--src/common/make_unique_for_overwrite.h25
-rw-r--r--src/common/scratch_buffer.h95
-rw-r--r--src/common/settings.cpp1
-rw-r--r--src/common/settings.h1
-rw-r--r--src/common/thread.h11
-rw-r--r--src/core/CMakeLists.txt7
-rw-r--r--src/core/core.cpp22
-rw-r--r--src/core/core.h4
-rw-r--r--src/core/cpu_manager.cpp15
-rw-r--r--src/core/cpu_manager.h4
-rw-r--r--src/core/frontend/emu_window.h6
-rw-r--r--src/core/hid/emulated_controller.cpp89
-rw-r--r--src/core/hid/emulated_controller.h9
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp22
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp1
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp29
-rw-r--r--src/core/hle/kernel/k_code_memory.h6
-rw-r--r--src/core/hle/kernel/k_hardware_timer.cpp74
-rw-r--r--src/core/hle/kernel/k_hardware_timer.h54
-rw-r--r--src/core/hle/kernel/k_hardware_timer_base.h92
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp8
-rw-r--r--src/core/hle/kernel/k_page_group.cpp121
-rw-r--r--src/core/hle/kernel/k_page_group.h163
-rw-r--r--src/core/hle/kernel/k_page_table.cpp142
-rw-r--r--src/core/hle/kernel/k_page_table.h9
-rw-r--r--src/core/hle/kernel/k_process.cpp11
-rw-r--r--src/core/hle/kernel/k_process.h3
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h4
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp70
-rw-r--r--src/core/hle/kernel/k_shared_memory.h8
-rw-r--r--src/core/hle/kernel/k_thread.h8
-rw-r--r--src/core/hle/kernel/k_thread_queue.cpp6
-rw-r--r--src/core/hle/kernel/k_timer_task.h40
-rw-r--r--src/core/hle/kernel/kernel.cpp77
-rw-r--r--src/core/hle/kernel/kernel.h9
-rw-r--r--src/core/hle/kernel/memory_types.h3
-rw-r--r--src/core/hle/kernel/svc.cpp132
-rw-r--r--src/core/hle/kernel/time_manager.cpp44
-rw-r--r--src/core/hle/kernel/time_manager.h41
-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.cpp15
-rw-r--r--src/core/hle/service/nfc/nfc_user.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp2
-rw-r--r--src/core/hle/service/set/set.cpp9
-rw-r--r--src/core/hle/service/set/set.h1
-rw-r--r--src/core/hle/service/set/set_sys.cpp10
-rw-r--r--src/core/hle/service/set/set_sys.h1
-rw-r--r--src/core/hle/service/time/clock_types.h1
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp17
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h87
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/drivers/camera.cpp2
-rw-r--r--src/input_common/drivers/camera.h5
-rw-r--r--src/input_common/drivers/sdl_driver.cpp2
-rw-r--r--src/input_common/drivers/virtual_gamepad.cpp78
-rw-r--r--src/input_common/drivers/virtual_gamepad.h73
-rw-r--r--src/input_common/input_mapping.cpp6
-rw-r--r--src/input_common/main.cpp286
-rw-r--r--src/input_common/main.h7
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_special.cpp5
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp2
-rw-r--r--src/shader_recompiler/profile.h1
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/scratch_buffer.cpp200
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h11
-rw-r--r--src/video_core/dma_pusher.cpp19
-rw-r--r--src/video_core/dma_pusher.h8
-rw-r--r--src/video_core/engines/draw_manager.cpp58
-rw-r--r--src/video_core/engines/draw_manager.h4
-rw-r--r--src/video_core/engines/engine_upload.cpp4
-rw-r--r--src/video_core/engines/engine_upload.h7
-rw-r--r--src/video_core/engines/maxwell_dma.cpp34
-rw-r--r--src/video_core/engines/maxwell_dma.h8
-rw-r--r--src/video_core/gpu.cpp5
-rw-r--r--src/video_core/host1x/vic.cpp6
-rw-r--r--src/video_core/host1x/vic.h7
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_device.h8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp77
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h1
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp4
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp28
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h14
-rw-r--r--src/video_core/texture_cache/texture_cache.h35
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h6
-rw-r--r--src/video_core/texture_cache/util.cpp20
-rw-r--r--src/video_core/texture_cache/util.h5
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp32
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h16
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp12
-rw-r--r--src/yuzu/bootmanager.cpp117
-rw-r--r--src/yuzu/bootmanager.h61
-rw-r--r--src/yuzu/configuration/config.cpp4
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp12
-rw-r--r--src/yuzu/configuration/configure_input_player.h3
-rw-r--r--src/yuzu/configuration/configure_input_player.ui19
-rw-r--r--src/yuzu/configuration/configure_system.cpp4
-rw-r--r--src/yuzu/configuration/configure_system.ui14
-rw-r--r--src/yuzu/game_list.cpp14
-rw-r--r--src/yuzu/game_list.h7
-rw-r--r--src/yuzu/main.cpp365
-rw-r--r--src/yuzu/main.h16
-rw-r--r--src/yuzu/startup_checks.cpp2
-rw-r--r--src/yuzu/uisettings.h1
-rw-r--r--src/yuzu/util/overlay_dialog.cpp21
-rw-r--r--src/yuzu/util/overlay_dialog.h1
-rw-r--r--src/yuzu_cmd/CMakeLists.txt9
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp2
-rw-r--r--src/yuzu_cmd/yuzu.cpp7
125 files changed, 2391 insertions, 1117 deletions
diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h
index 3dae1a3b7..15082f6c6 100644
--- a/src/audio_core/device/audio_buffers.h
+++ b/src/audio_core/device/audio_buffers.h
@@ -91,9 +91,10 @@ public:
91 * @param core_timing - The CoreTiming instance 91 * @param core_timing - The CoreTiming instance
92 * @param session - The device session 92 * @param session - The device session
93 * 93 *
94 * @return Is the buffer was released. 94 * @return If any buffer was released.
95 */ 95 */
96 bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) { 96 bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session,
97 bool force) {
97 std::scoped_lock l{lock}; 98 std::scoped_lock l{lock};
98 bool buffer_released{false}; 99 bool buffer_released{false};
99 while (registered_count > 0) { 100 while (registered_count > 0) {
@@ -103,7 +104,8 @@ public:
103 } 104 }
104 105
105 // Check with the backend if this buffer can be released yet. 106 // Check with the backend if this buffer can be released yet.
106 if (!session.IsBufferConsumed(buffers[index])) { 107 // If we're shutting down, we don't care if it's been played or not.
108 if (!force && !session.IsBufferConsumed(buffers[index])) {
107 break; 109 break;
108 } 110 }
109 111
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp
index 995060414..5a327a606 100644
--- a/src/audio_core/device/device_session.cpp
+++ b/src/audio_core/device/device_session.cpp
@@ -73,6 +73,12 @@ void DeviceSession::Stop() {
73 } 73 }
74} 74}
75 75
76void DeviceSession::ClearBuffers() {
77 if (stream) {
78 stream->ClearQueue();
79 }
80}
81
76void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const { 82void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
77 for (const auto& buffer : buffers) { 83 for (const auto& buffer : buffers) {
78 Sink::SinkBuffer new_buffer{ 84 Sink::SinkBuffer new_buffer{
diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h
index 74f4dc085..75f766c68 100644
--- a/src/audio_core/device/device_session.h
+++ b/src/audio_core/device/device_session.h
@@ -91,6 +91,11 @@ public:
91 void Stop(); 91 void Stop();
92 92
93 /** 93 /**
94 * Clear out the underlying audio buffers in the backend stream.
95 */
96 void ClearBuffers();
97
98 /**
94 * Set this device session's volume. 99 * Set this device session's volume.
95 * 100 *
96 * @param volume - New volume for this session. 101 * @param volume - New volume for this session.
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp
index 4324cafd8..934ef8c1c 100644
--- a/src/audio_core/in/audio_in_system.cpp
+++ b/src/audio_core/in/audio_in_system.cpp
@@ -23,7 +23,6 @@ System::~System() {
23void System::Finalize() { 23void System::Finalize() {
24 Stop(); 24 Stop();
25 session->Finalize(); 25 session->Finalize();
26 buffer_event->Signal();
27} 26}
28 27
29void System::StartSession() { 28void System::StartSession() {
@@ -102,6 +101,10 @@ Result System::Stop() {
102 if (state == State::Started) { 101 if (state == State::Started) {
103 session->Stop(); 102 session->Stop();
104 session->SetVolume(0.0f); 103 session->SetVolume(0.0f);
104 session->ClearBuffers();
105 if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) {
106 buffer_event->Signal();
107 }
105 state = State::Stopped; 108 state = State::Stopped;
106 } 109 }
107 110
@@ -138,7 +141,7 @@ void System::RegisterBuffers() {
138} 141}
139 142
140void System::ReleaseBuffers() { 143void System::ReleaseBuffers() {
141 bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; 144 bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};
142 145
143 if (signal) { 146 if (signal) {
144 // Signal if any buffer was released, or if none are registered, we need more. 147 // Signal if any buffer was released, or if none are registered, we need more.
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp
index a66208ed9..e096a1dac 100644
--- a/src/audio_core/out/audio_out_system.cpp
+++ b/src/audio_core/out/audio_out_system.cpp
@@ -24,7 +24,6 @@ System::~System() {
24void System::Finalize() { 24void System::Finalize() {
25 Stop(); 25 Stop();
26 session->Finalize(); 26 session->Finalize();
27 buffer_event->Signal();
28} 27}
29 28
30std::string_view System::GetDefaultOutputDeviceName() const { 29std::string_view System::GetDefaultOutputDeviceName() const {
@@ -102,6 +101,10 @@ Result System::Stop() {
102 if (state == State::Started) { 101 if (state == State::Started) {
103 session->Stop(); 102 session->Stop();
104 session->SetVolume(0.0f); 103 session->SetVolume(0.0f);
104 session->ClearBuffers();
105 if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) {
106 buffer_event->Signal();
107 }
105 state = State::Stopped; 108 state = State::Stopped;
106 } 109 }
107 110
@@ -138,7 +141,7 @@ void System::RegisterBuffers() {
138} 141}
139 142
140void System::ReleaseBuffers() { 143void System::ReleaseBuffers() {
141 bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; 144 bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};
142 if (signal) { 145 if (signal) {
143 // Signal if any buffer was released, or if none are registered, we need more. 146 // Signal if any buffer was released, or if none are registered, we need more.
144 buffer_event->Signal(); 147 buffer_event->Signal();
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 25b22a281..eb05e46a8 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -78,6 +78,7 @@ add_library(common STATIC
78 logging/types.h 78 logging/types.h
79 lz4_compression.cpp 79 lz4_compression.cpp
80 lz4_compression.h 80 lz4_compression.h
81 make_unique_for_overwrite.h
81 math_util.h 82 math_util.h
82 memory_detect.cpp 83 memory_detect.cpp
83 memory_detect.h 84 memory_detect.h
@@ -101,6 +102,7 @@ add_library(common STATIC
101 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp 102 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
102 scm_rev.h 103 scm_rev.h
103 scope_exit.h 104 scope_exit.h
105 scratch_buffer.h
104 settings.cpp 106 settings.cpp
105 settings.h 107 settings.h
106 settings_input.cpp 108 settings_input.cpp
diff --git a/src/common/assert.h b/src/common/assert.h
index 8c927fcc0..67e7e9375 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -69,7 +69,7 @@ void assert_fail_impl();
69#define ASSERT_OR_EXECUTE(_a_, _b_) \ 69#define ASSERT_OR_EXECUTE(_a_, _b_) \
70 do { \ 70 do { \
71 ASSERT(_a_); \ 71 ASSERT(_a_); \
72 if (!(_a_)) { \ 72 if (!(_a_)) [[unlikely]] { \
73 _b_ \ 73 _b_ \
74 } \ 74 } \
75 } while (0) 75 } while (0)
@@ -78,7 +78,7 @@ void assert_fail_impl();
78#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \ 78#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \
79 do { \ 79 do { \
80 ASSERT_MSG(_a_, __VA_ARGS__); \ 80 ASSERT_MSG(_a_, __VA_ARGS__); \
81 if (!(_a_)) { \ 81 if (!(_a_)) [[unlikely]] { \
82 _b_ \ 82 _b_ \
83 } \ 83 } \
84 } while (0) 84 } while (0)
diff --git a/src/common/make_unique_for_overwrite.h b/src/common/make_unique_for_overwrite.h
new file mode 100644
index 000000000..c7413cf51
--- /dev/null
+++ b/src/common/make_unique_for_overwrite.h
@@ -0,0 +1,25 @@
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 <type_traits>
8
9namespace Common {
10
11template <class T>
12requires(!std::is_array_v<T>) std::unique_ptr<T> make_unique_for_overwrite() {
13 return std::unique_ptr<T>(new T);
14}
15
16template <class T>
17requires std::is_unbounded_array_v<T> std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
18 return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
19}
20
21template <class T, class... Args>
22requires std::is_bounded_array_v<T>
23void make_unique_for_overwrite(Args&&...) = delete;
24
25} // namespace Common
diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h
new file mode 100644
index 000000000..1245a5086
--- /dev/null
+++ b/src/common/scratch_buffer.h
@@ -0,0 +1,95 @@
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/make_unique_for_overwrite.h"
7
8namespace Common {
9
10/**
11 * ScratchBuffer class
12 * This class creates a default initialized heap allocated buffer for cases such as intermediate
13 * buffers being copied into entirely, where value initializing members during allocation or resize
14 * is redundant.
15 */
16template <typename T>
17class ScratchBuffer {
18public:
19 ScratchBuffer() = default;
20
21 explicit ScratchBuffer(size_t initial_capacity)
22 : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
23 buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
24
25 ~ScratchBuffer() = default;
26
27 /// This will only grow the buffer's capacity if size is greater than the current capacity.
28 /// The previously held data will remain intact.
29 void resize(size_t size) {
30 if (size > buffer_capacity) {
31 auto new_buffer = Common::make_unique_for_overwrite<T[]>(size);
32 std::move(buffer.get(), buffer.get() + buffer_capacity, new_buffer.get());
33 buffer = std::move(new_buffer);
34 buffer_capacity = size;
35 }
36 last_requested_size = size;
37 }
38
39 /// This will only grow the buffer's capacity if size is greater than the current capacity.
40 /// The previously held data will be destroyed if a reallocation occurs.
41 void resize_destructive(size_t size) {
42 if (size > buffer_capacity) {
43 buffer_capacity = size;
44 buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
45 }
46 last_requested_size = size;
47 }
48
49 [[nodiscard]] T* data() noexcept {
50 return buffer.get();
51 }
52
53 [[nodiscard]] const T* data() const noexcept {
54 return buffer.get();
55 }
56
57 [[nodiscard]] T* begin() noexcept {
58 return data();
59 }
60
61 [[nodiscard]] const T* begin() const noexcept {
62 return data();
63 }
64
65 [[nodiscard]] T* end() noexcept {
66 return data() + last_requested_size;
67 }
68
69 [[nodiscard]] const T* end() const noexcept {
70 return data() + last_requested_size;
71 }
72
73 [[nodiscard]] T& operator[](size_t i) {
74 return buffer[i];
75 }
76
77 [[nodiscard]] const T& operator[](size_t i) const {
78 return buffer[i];
79 }
80
81 [[nodiscard]] size_t size() const noexcept {
82 return last_requested_size;
83 }
84
85 [[nodiscard]] size_t capacity() const noexcept {
86 return buffer_capacity;
87 }
88
89private:
90 size_t last_requested_size{};
91 size_t buffer_capacity{};
92 std::unique_ptr<T[]> buffer{};
93};
94
95} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index d8ffe34c3..149e621f9 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -40,6 +40,7 @@ void LogSettings() {
40 LOG_INFO(Config, "yuzu Configuration:"); 40 LOG_INFO(Config, "yuzu Configuration:");
41 log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); 41 log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
42 log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); 42 log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
43 log_setting("System_DeviceName", values.device_name.GetValue());
43 log_setting("System_CurrentUser", values.current_user.GetValue()); 44 log_setting("System_CurrentUser", values.current_user.GetValue());
44 log_setting("System_LanguageIndex", values.language_index.GetValue()); 45 log_setting("System_LanguageIndex", values.language_index.GetValue());
45 log_setting("System_RegionIndex", values.region_index.GetValue()); 46 log_setting("System_RegionIndex", values.region_index.GetValue());
diff --git a/src/common/settings.h b/src/common/settings.h
index 7ce9ea23c..6b199af93 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -458,6 +458,7 @@ struct Values {
458 458
459 // System 459 // System
460 SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; 460 SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"};
461 Setting<std::string> device_name{"Yuzu", "device_name"};
461 // Measured in seconds since epoch 462 // Measured in seconds since epoch
462 std::optional<s64> custom_rtc; 463 std::optional<s64> custom_rtc;
463 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` 464 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
diff --git a/src/common/thread.h b/src/common/thread.h
index e17a7850f..8ae169b4e 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -11,6 +11,7 @@
11#include <mutex> 11#include <mutex>
12#include <thread> 12#include <thread>
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/polyfill_thread.h"
14 15
15namespace Common { 16namespace Common {
16 17
@@ -69,7 +70,7 @@ public:
69 explicit Barrier(std::size_t count_) : count(count_) {} 70 explicit Barrier(std::size_t count_) : count(count_) {}
70 71
71 /// Blocks until all "count" threads have called Sync() 72 /// Blocks until all "count" threads have called Sync()
72 void Sync() { 73 bool Sync(std::stop_token token = {}) {
73 std::unique_lock lk{mutex}; 74 std::unique_lock lk{mutex};
74 const std::size_t current_generation = generation; 75 const std::size_t current_generation = generation;
75 76
@@ -77,14 +78,16 @@ public:
77 generation++; 78 generation++;
78 waiting = 0; 79 waiting = 0;
79 condvar.notify_all(); 80 condvar.notify_all();
81 return true;
80 } else { 82 } else {
81 condvar.wait(lk, 83 CondvarWait(condvar, lk, token,
82 [this, current_generation] { return current_generation != generation; }); 84 [this, current_generation] { return current_generation != generation; });
85 return !token.stop_requested();
83 } 86 }
84 } 87 }
85 88
86private: 89private:
87 std::condition_variable condvar; 90 std::condition_variable_any condvar;
88 std::mutex mutex; 91 std::mutex mutex;
89 std::size_t count; 92 std::size_t count;
90 std::size_t waiting = 0; 93 std::size_t waiting = 0;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c6b5ac196..5afdeb5ff 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -201,6 +201,9 @@ add_library(core STATIC
201 hle/kernel/k_event_info.h 201 hle/kernel/k_event_info.h
202 hle/kernel/k_handle_table.cpp 202 hle/kernel/k_handle_table.cpp
203 hle/kernel/k_handle_table.h 203 hle/kernel/k_handle_table.h
204 hle/kernel/k_hardware_timer_base.h
205 hle/kernel/k_hardware_timer.cpp
206 hle/kernel/k_hardware_timer.h
204 hle/kernel/k_interrupt_manager.cpp 207 hle/kernel/k_interrupt_manager.cpp
205 hle/kernel/k_interrupt_manager.h 208 hle/kernel/k_interrupt_manager.h
206 hle/kernel/k_light_condition_variable.cpp 209 hle/kernel/k_light_condition_variable.cpp
@@ -223,6 +226,7 @@ add_library(core STATIC
223 hle/kernel/k_page_buffer.h 226 hle/kernel/k_page_buffer.h
224 hle/kernel/k_page_heap.cpp 227 hle/kernel/k_page_heap.cpp
225 hle/kernel/k_page_heap.h 228 hle/kernel/k_page_heap.h
229 hle/kernel/k_page_group.cpp
226 hle/kernel/k_page_group.h 230 hle/kernel/k_page_group.h
227 hle/kernel/k_page_table.cpp 231 hle/kernel/k_page_table.cpp
228 hle/kernel/k_page_table.h 232 hle/kernel/k_page_table.h
@@ -268,6 +272,7 @@ add_library(core STATIC
268 hle/kernel/k_thread_local_page.h 272 hle/kernel/k_thread_local_page.h
269 hle/kernel/k_thread_queue.cpp 273 hle/kernel/k_thread_queue.cpp
270 hle/kernel/k_thread_queue.h 274 hle/kernel/k_thread_queue.h
275 hle/kernel/k_timer_task.h
271 hle/kernel/k_trace.h 276 hle/kernel/k_trace.h
272 hle/kernel/k_transfer_memory.cpp 277 hle/kernel/k_transfer_memory.cpp
273 hle/kernel/k_transfer_memory.h 278 hle/kernel/k_transfer_memory.h
@@ -290,8 +295,6 @@ add_library(core STATIC
290 hle/kernel/svc_common.h 295 hle/kernel/svc_common.h
291 hle/kernel/svc_types.h 296 hle/kernel/svc_types.h
292 hle/kernel/svc_wrap.h 297 hle/kernel/svc_wrap.h
293 hle/kernel/time_manager.cpp
294 hle/kernel/time_manager.h
295 hle/result.h 298 hle/result.h
296 hle/service/acc/acc.cpp 299 hle/service/acc/acc.cpp
297 hle/service/acc/acc.h 300 hle/service/acc/acc.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 94d4e2212..47292cd78 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -183,26 +183,20 @@ struct System::Impl {
183 Initialize(system); 183 Initialize(system);
184 } 184 }
185 185
186 SystemResultStatus Run() { 186 void Run() {
187 std::unique_lock<std::mutex> lk(suspend_guard); 187 std::unique_lock<std::mutex> lk(suspend_guard);
188 status = SystemResultStatus::Success;
189 188
190 kernel.Suspend(false); 189 kernel.Suspend(false);
191 core_timing.SyncPause(false); 190 core_timing.SyncPause(false);
192 is_paused.store(false, std::memory_order_relaxed); 191 is_paused.store(false, std::memory_order_relaxed);
193
194 return status;
195 } 192 }
196 193
197 SystemResultStatus Pause() { 194 void Pause() {
198 std::unique_lock<std::mutex> lk(suspend_guard); 195 std::unique_lock<std::mutex> lk(suspend_guard);
199 status = SystemResultStatus::Success;
200 196
201 core_timing.SyncPause(true); 197 core_timing.SyncPause(true);
202 kernel.Suspend(true); 198 kernel.Suspend(true);
203 is_paused.store(true, std::memory_order_relaxed); 199 is_paused.store(true, std::memory_order_relaxed);
204
205 return status;
206 } 200 }
207 201
208 bool IsPaused() const { 202 bool IsPaused() const {
@@ -389,7 +383,9 @@ struct System::Impl {
389 kernel.ShutdownCores(); 383 kernel.ShutdownCores();
390 cpu_manager.Shutdown(); 384 cpu_manager.Shutdown();
391 debugger.reset(); 385 debugger.reset();
392 services->KillNVNFlinger(); 386 if (services) {
387 services->KillNVNFlinger();
388 }
393 kernel.CloseServices(); 389 kernel.CloseServices();
394 services.reset(); 390 services.reset();
395 service_manager.reset(); 391 service_manager.reset();
@@ -551,12 +547,12 @@ void System::Initialize() {
551 impl->Initialize(*this); 547 impl->Initialize(*this);
552} 548}
553 549
554SystemResultStatus System::Run() { 550void System::Run() {
555 return impl->Run(); 551 impl->Run();
556} 552}
557 553
558SystemResultStatus System::Pause() { 554void System::Pause() {
559 return impl->Pause(); 555 impl->Pause();
560} 556}
561 557
562bool System::IsPaused() const { 558bool System::IsPaused() const {
diff --git a/src/core/core.h b/src/core/core.h
index 4ebedffd9..fb5cda2f5 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -152,13 +152,13 @@ public:
152 * Run the OS and Application 152 * Run the OS and Application
153 * This function will start emulation and run the relevant devices 153 * This function will start emulation and run the relevant devices
154 */ 154 */
155 [[nodiscard]] SystemResultStatus Run(); 155 void Run();
156 156
157 /** 157 /**
158 * Pause the OS and Application 158 * Pause the OS and Application
159 * This function will pause emulation and stop the relevant devices 159 * This function will pause emulation and stop the relevant devices
160 */ 160 */
161 [[nodiscard]] SystemResultStatus Pause(); 161 void Pause();
162 162
163 /// Check if the core is currently paused. 163 /// Check if the core is currently paused.
164 [[nodiscard]] bool IsPaused() const; 164 [[nodiscard]] bool IsPaused() const;
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 0dd4c2196..04a11f444 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -20,23 +20,20 @@ namespace Core {
20CpuManager::CpuManager(System& system_) : system{system_} {} 20CpuManager::CpuManager(System& system_) : system{system_} {}
21CpuManager::~CpuManager() = default; 21CpuManager::~CpuManager() = default;
22 22
23void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
24 std::size_t core) {
25 cpu_manager.RunThread(core);
26}
27
28void CpuManager::Initialize() { 23void CpuManager::Initialize() {
29 num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1; 24 num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
30 gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1); 25 gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
31 26
32 for (std::size_t core = 0; core < num_cores; core++) { 27 for (std::size_t core = 0; core < num_cores; core++) {
33 core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); 28 core_data[core].host_thread =
29 std::jthread([this, core](std::stop_token token) { RunThread(token, core); });
34 } 30 }
35} 31}
36 32
37void CpuManager::Shutdown() { 33void CpuManager::Shutdown() {
38 for (std::size_t core = 0; core < num_cores; core++) { 34 for (std::size_t core = 0; core < num_cores; core++) {
39 if (core_data[core].host_thread.joinable()) { 35 if (core_data[core].host_thread.joinable()) {
36 core_data[core].host_thread.request_stop();
40 core_data[core].host_thread.join(); 37 core_data[core].host_thread.join();
41 } 38 }
42 } 39 }
@@ -184,7 +181,7 @@ void CpuManager::ShutdownThread() {
184 UNREACHABLE(); 181 UNREACHABLE();
185} 182}
186 183
187void CpuManager::RunThread(std::size_t core) { 184void CpuManager::RunThread(std::stop_token token, std::size_t core) {
188 /// Initialization 185 /// Initialization
189 system.RegisterCoreThread(core); 186 system.RegisterCoreThread(core);
190 std::string name; 187 std::string name;
@@ -206,7 +203,9 @@ void CpuManager::RunThread(std::size_t core) {
206 }); 203 });
207 204
208 // Running 205 // Running
209 gpu_barrier->Sync(); 206 if (!gpu_barrier->Sync(token)) {
207 return;
208 }
210 209
211 if (!is_async_gpu && !is_multicore) { 210 if (!is_async_gpu && !is_multicore) {
212 system.GPU().ObtainContext(); 211 system.GPU().ObtainContext();
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 374367468..0deea9c58 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -81,12 +81,10 @@ private:
81 void SingleCoreRunGuestThread(); 81 void SingleCoreRunGuestThread();
82 void SingleCoreRunIdleThread(); 82 void SingleCoreRunIdleThread();
83 83
84 static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
85
86 void GuestActivate(); 84 void GuestActivate();
87 void HandleInterrupt(); 85 void HandleInterrupt();
88 void ShutdownThread(); 86 void ShutdownThread();
89 void RunThread(std::size_t core); 87 void RunThread(std::stop_token stop_token, std::size_t core);
90 88
91 struct CoreData { 89 struct CoreData {
92 std::shared_ptr<Common::Fiber> host_context; 90 std::shared_ptr<Common::Fiber> host_context;
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 95363b645..cf85ba29e 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -131,6 +131,10 @@ public:
131 return active_config; 131 return active_config;
132 } 132 }
133 133
134 bool StrictContextRequired() const {
135 return strict_context_required;
136 }
137
134 /** 138 /**
135 * Requests the internal configuration to be replaced by the specified argument at some point in 139 * Requests the internal configuration to be replaced by the specified argument at some point in
136 * the future. 140 * the future.
@@ -207,6 +211,8 @@ protected:
207 211
208 WindowSystemInfo window_info; 212 WindowSystemInfo window_info;
209 213
214 bool strict_context_required = false;
215
210private: 216private:
211 /** 217 /**
212 * Handler called when the minimal client area was requested to be changed via SetConfig. 218 * Handler called when the minimal client area was requested to be changed via SetConfig.
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 67969e938..5587ee097 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -145,6 +145,7 @@ void EmulatedController::LoadDevices() {
145 output_params[3].Set("output", true); 145 output_params[3].Set("output", true);
146 146
147 LoadTASParams(); 147 LoadTASParams();
148 LoadVirtualGamepadParams();
148 149
149 std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice); 150 std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice);
150 std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice); 151 std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice);
@@ -163,6 +164,12 @@ void EmulatedController::LoadDevices() {
163 Common::Input::CreateInputDevice); 164 Common::Input::CreateInputDevice);
164 std::ranges::transform(tas_stick_params, tas_stick_devices.begin(), 165 std::ranges::transform(tas_stick_params, tas_stick_devices.begin(),
165 Common::Input::CreateInputDevice); 166 Common::Input::CreateInputDevice);
167
168 // Initialize virtual gamepad devices
169 std::ranges::transform(virtual_button_params, virtual_button_devices.begin(),
170 Common::Input::CreateInputDevice);
171 std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(),
172 Common::Input::CreateInputDevice);
166} 173}
167 174
168void EmulatedController::LoadTASParams() { 175void EmulatedController::LoadTASParams() {
@@ -203,6 +210,53 @@ void EmulatedController::LoadTASParams() {
203 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); 210 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
204 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); 211 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
205 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); 212 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
213
214 // set to optimal stick to avoid sanitizing the stick and tweaking the coordinates
215 // making sure they play back in the game as originally written down in the script file
216 tas_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f);
217 tas_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f);
218 tas_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f);
219 tas_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f);
220}
221
222void EmulatedController::LoadVirtualGamepadParams() {
223 const auto player_index = NpadIdTypeToIndex(npad_id_type);
224 Common::ParamPackage common_params{};
225 common_params.Set("engine", "virtual_gamepad");
226 common_params.Set("port", static_cast<int>(player_index));
227 for (auto& param : virtual_button_params) {
228 param = common_params;
229 }
230 for (auto& param : virtual_stick_params) {
231 param = common_params;
232 }
233
234 // TODO(german77): Replace this with an input profile or something better
235 virtual_button_params[Settings::NativeButton::A].Set("button", 0);
236 virtual_button_params[Settings::NativeButton::B].Set("button", 1);
237 virtual_button_params[Settings::NativeButton::X].Set("button", 2);
238 virtual_button_params[Settings::NativeButton::Y].Set("button", 3);
239 virtual_button_params[Settings::NativeButton::LStick].Set("button", 4);
240 virtual_button_params[Settings::NativeButton::RStick].Set("button", 5);
241 virtual_button_params[Settings::NativeButton::L].Set("button", 6);
242 virtual_button_params[Settings::NativeButton::R].Set("button", 7);
243 virtual_button_params[Settings::NativeButton::ZL].Set("button", 8);
244 virtual_button_params[Settings::NativeButton::ZR].Set("button", 9);
245 virtual_button_params[Settings::NativeButton::Plus].Set("button", 10);
246 virtual_button_params[Settings::NativeButton::Minus].Set("button", 11);
247 virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12);
248 virtual_button_params[Settings::NativeButton::DUp].Set("button", 13);
249 virtual_button_params[Settings::NativeButton::DRight].Set("button", 14);
250 virtual_button_params[Settings::NativeButton::DDown].Set("button", 15);
251 virtual_button_params[Settings::NativeButton::SL].Set("button", 16);
252 virtual_button_params[Settings::NativeButton::SR].Set("button", 17);
253 virtual_button_params[Settings::NativeButton::Home].Set("button", 18);
254 virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
255
256 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
257 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
258 virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
259 virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
206} 260}
207 261
208void EmulatedController::ReloadInput() { 262void EmulatedController::ReloadInput() {
@@ -322,6 +376,35 @@ void EmulatedController::ReloadInput() {
322 }, 376 },
323 }); 377 });
324 } 378 }
379
380 // Use a common UUID for Virtual Gamepad
381 static constexpr Common::UUID VIRTUAL_UUID = Common::UUID{
382 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
383
384 // Register virtual devices. No need to force update
385 for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) {
386 if (!virtual_button_devices[index]) {
387 continue;
388 }
389 virtual_button_devices[index]->SetCallback({
390 .on_change =
391 [this, index](const Common::Input::CallbackStatus& callback) {
392 SetButton(callback, index, VIRTUAL_UUID);
393 },
394 });
395 }
396
397 for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) {
398 if (!virtual_stick_devices[index]) {
399 continue;
400 }
401 virtual_stick_devices[index]->SetCallback({
402 .on_change =
403 [this, index](const Common::Input::CallbackStatus& callback) {
404 SetStick(callback, index, VIRTUAL_UUID);
405 },
406 });
407 }
325} 408}
326 409
327void EmulatedController::UnloadInput() { 410void EmulatedController::UnloadInput() {
@@ -349,6 +432,12 @@ void EmulatedController::UnloadInput() {
349 for (auto& stick : tas_stick_devices) { 432 for (auto& stick : tas_stick_devices) {
350 stick.reset(); 433 stick.reset();
351 } 434 }
435 for (auto& button : virtual_button_devices) {
436 button.reset();
437 }
438 for (auto& stick : virtual_stick_devices) {
439 stick.reset();
440 }
352 camera_devices.reset(); 441 camera_devices.reset();
353 nfc_devices.reset(); 442 nfc_devices.reset();
354} 443}
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index fa7a34278..a398543a6 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -385,6 +385,9 @@ private:
385 /// Set the params for TAS devices 385 /// Set the params for TAS devices
386 void LoadTASParams(); 386 void LoadTASParams();
387 387
388 /// Set the params for virtual pad devices
389 void LoadVirtualGamepadParams();
390
388 /** 391 /**
389 * @param use_temporary_value If true tmp_npad_type will be used 392 * @param use_temporary_value If true tmp_npad_type will be used
390 * @return true if the controller style is fullkey 393 * @return true if the controller style is fullkey
@@ -500,6 +503,12 @@ private:
500 ButtonDevices tas_button_devices; 503 ButtonDevices tas_button_devices;
501 StickDevices tas_stick_devices; 504 StickDevices tas_stick_devices;
502 505
506 // Virtual gamepad related variables
507 ButtonParams virtual_button_params;
508 StickParams virtual_stick_params;
509 ButtonDevices virtual_button_devices;
510 StickDevices virtual_stick_devices;
511
503 mutable std::mutex mutex; 512 mutable std::mutex mutex;
504 mutable std::mutex callback_mutex; 513 mutable std::mutex callback_mutex;
505 std::unordered_map<int, ControllerUpdateCallback> callback_list; 514 std::unordered_map<int, ControllerUpdateCallback> callback_list;
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 06010b8d1..738b6d0f1 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -167,6 +167,9 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
167 } 167 }
168 if (incoming) { 168 if (incoming) {
169 // Populate the object lists with the data in the IPC request. 169 // Populate the object lists with the data in the IPC request.
170 incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy);
171 incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move);
172
170 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { 173 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
171 incoming_copy_handles.push_back(rp.Pop<Handle>()); 174 incoming_copy_handles.push_back(rp.Pop<Handle>());
172 } 175 }
@@ -181,6 +184,11 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
181 } 184 }
182 } 185 }
183 186
187 buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors);
188 buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors);
189 buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors);
190 buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors);
191
184 for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { 192 for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
185 buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>()); 193 buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
186 } 194 }
@@ -318,25 +326,23 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
318} 326}
319 327
320std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { 328std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
321 std::vector<u8> buffer{};
322 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 329 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
323 BufferDescriptorA()[buffer_index].Size()}; 330 BufferDescriptorA()[buffer_index].Size()};
324
325 if (is_buffer_a) { 331 if (is_buffer_a) {
326 ASSERT_OR_EXECUTE_MSG( 332 ASSERT_OR_EXECUTE_MSG(
327 BufferDescriptorA().size() > buffer_index, { return buffer; }, 333 BufferDescriptorA().size() > buffer_index, { return {}; },
328 "BufferDescriptorA invalid buffer_index {}", buffer_index); 334 "BufferDescriptorA invalid buffer_index {}", buffer_index);
329 buffer.resize(BufferDescriptorA()[buffer_index].Size()); 335 std::vector<u8> buffer(BufferDescriptorA()[buffer_index].Size());
330 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); 336 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
337 return buffer;
331 } else { 338 } else {
332 ASSERT_OR_EXECUTE_MSG( 339 ASSERT_OR_EXECUTE_MSG(
333 BufferDescriptorX().size() > buffer_index, { return buffer; }, 340 BufferDescriptorX().size() > buffer_index, { return {}; },
334 "BufferDescriptorX invalid buffer_index {}", buffer_index); 341 "BufferDescriptorX invalid buffer_index {}", buffer_index);
335 buffer.resize(BufferDescriptorX()[buffer_index].Size()); 342 std::vector<u8> buffer(BufferDescriptorX()[buffer_index].Size());
336 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); 343 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
344 return buffer;
337 } 345 }
338
339 return buffer;
340} 346}
341 347
342std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, 348std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index f85b11557..a442a3b98 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -10,7 +10,6 @@
10#include "core/hle/kernel/k_thread_queue.h" 10#include "core/hle/kernel/k_thread_queue.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/svc_results.h" 12#include "core/hle/kernel/svc_results.h"
13#include "core/hle/kernel/time_manager.h"
14#include "core/memory.h" 13#include "core/memory.h"
15 14
16namespace Kernel { 15namespace Kernel {
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index 4b1c134d4..d9da1e600 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -27,13 +27,13 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si
27 auto& page_table = m_owner->PageTable(); 27 auto& page_table = m_owner->PageTable();
28 28
29 // Construct the page group. 29 // Construct the page group.
30 m_page_group = {}; 30 m_page_group.emplace(kernel, page_table.GetBlockInfoManager());
31 31
32 // Lock the memory. 32 // Lock the memory.
33 R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size)) 33 R_TRY(page_table.LockForCodeMemory(std::addressof(*m_page_group), addr, size))
34 34
35 // Clear the memory. 35 // Clear the memory.
36 for (const auto& block : m_page_group.Nodes()) { 36 for (const auto& block : *m_page_group) {
37 std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize()); 37 std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize());
38 } 38 }
39 39
@@ -51,12 +51,13 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si
51void KCodeMemory::Finalize() { 51void KCodeMemory::Finalize() {
52 // Unlock. 52 // Unlock.
53 if (!m_is_mapped && !m_is_owner_mapped) { 53 if (!m_is_mapped && !m_is_owner_mapped) {
54 const size_t size = m_page_group.GetNumPages() * PageSize; 54 const size_t size = m_page_group->GetNumPages() * PageSize;
55 m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group); 55 m_owner->PageTable().UnlockForCodeMemory(m_address, size, *m_page_group);
56 } 56 }
57 57
58 // Close the page group. 58 // Close the page group.
59 m_page_group = {}; 59 m_page_group->Close();
60 m_page_group->Finalize();
60 61
61 // Close our reference to our owner. 62 // Close our reference to our owner.
62 m_owner->Close(); 63 m_owner->Close();
@@ -64,7 +65,7 @@ void KCodeMemory::Finalize() {
64 65
65Result KCodeMemory::Map(VAddr address, size_t size) { 66Result KCodeMemory::Map(VAddr address, size_t size) {
66 // Validate the size. 67 // Validate the size.
67 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); 68 R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
68 69
69 // Lock ourselves. 70 // Lock ourselves.
70 KScopedLightLock lk(m_lock); 71 KScopedLightLock lk(m_lock);
@@ -74,7 +75,7 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
74 75
75 // Map the memory. 76 // Map the memory.
76 R_TRY(kernel.CurrentProcess()->PageTable().MapPages( 77 R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
77 address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); 78 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
78 79
79 // Mark ourselves as mapped. 80 // Mark ourselves as mapped.
80 m_is_mapped = true; 81 m_is_mapped = true;
@@ -84,13 +85,13 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
84 85
85Result KCodeMemory::Unmap(VAddr address, size_t size) { 86Result KCodeMemory::Unmap(VAddr address, size_t size) {
86 // Validate the size. 87 // Validate the size.
87 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); 88 R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
88 89
89 // Lock ourselves. 90 // Lock ourselves.
90 KScopedLightLock lk(m_lock); 91 KScopedLightLock lk(m_lock);
91 92
92 // Unmap the memory. 93 // Unmap the memory.
93 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group, 94 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, *m_page_group,
94 KMemoryState::CodeOut)); 95 KMemoryState::CodeOut));
95 96
96 // Mark ourselves as unmapped. 97 // Mark ourselves as unmapped.
@@ -101,7 +102,7 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) {
101 102
102Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { 103Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
103 // Validate the size. 104 // Validate the size.
104 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); 105 R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
105 106
106 // Lock ourselves. 107 // Lock ourselves.
107 KScopedLightLock lk(m_lock); 108 KScopedLightLock lk(m_lock);
@@ -125,7 +126,7 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
125 126
126 // Map the memory. 127 // Map the memory.
127 R_TRY( 128 R_TRY(
128 m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm)); 129 m_owner->PageTable().MapPages(address, *m_page_group, KMemoryState::GeneratedCode, k_perm));
129 130
130 // Mark ourselves as mapped. 131 // Mark ourselves as mapped.
131 m_is_owner_mapped = true; 132 m_is_owner_mapped = true;
@@ -135,13 +136,13 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
135 136
136Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { 137Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
137 // Validate the size. 138 // Validate the size.
138 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); 139 R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
139 140
140 // Lock ourselves. 141 // Lock ourselves.
141 KScopedLightLock lk(m_lock); 142 KScopedLightLock lk(m_lock);
142 143
143 // Unmap the memory. 144 // Unmap the memory.
144 R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode)); 145 R_TRY(m_owner->PageTable().UnmapPages(address, *m_page_group, KMemoryState::GeneratedCode));
145 146
146 // Mark ourselves as unmapped. 147 // Mark ourselves as unmapped.
147 m_is_owner_mapped = false; 148 m_is_owner_mapped = false;
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
index 2e7e1436a..5b260b385 100644
--- a/src/core/hle/kernel/k_code_memory.h
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <optional>
7
6#include "common/common_types.h" 8#include "common/common_types.h"
7#include "core/device_memory.h" 9#include "core/device_memory.h"
8#include "core/hle/kernel/k_auto_object.h" 10#include "core/hle/kernel/k_auto_object.h"
@@ -49,11 +51,11 @@ public:
49 return m_address; 51 return m_address;
50 } 52 }
51 size_t GetSize() const { 53 size_t GetSize() const {
52 return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0; 54 return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0;
53 } 55 }
54 56
55private: 57private:
56 KPageGroup m_page_group{}; 58 std::optional<KPageGroup> m_page_group{};
57 KProcess* m_owner{}; 59 KProcess* m_owner{};
58 VAddr m_address{}; 60 VAddr m_address{};
59 KLightLock m_lock; 61 KLightLock m_lock;
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp
new file mode 100644
index 000000000..6bba79ea0
--- /dev/null
+++ b/src/core/hle/kernel/k_hardware_timer.cpp
@@ -0,0 +1,74 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/k_hardware_timer.h"
7#include "core/hle/kernel/k_scheduler.h"
8
9namespace Kernel {
10
11void KHardwareTimer::Initialize() {
12 // Create the timing callback to register with CoreTiming.
13 m_event_type = Core::Timing::CreateEvent(
14 "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) {
15 reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask();
16 return std::nullopt;
17 });
18}
19
20void KHardwareTimer::Finalize() {
21 this->DisableInterrupt();
22 m_event_type.reset();
23}
24
25void KHardwareTimer::DoTask() {
26 // Handle the interrupt.
27 {
28 KScopedSchedulerLock slk{m_kernel};
29 KScopedSpinLock lk(this->GetLock());
30
31 //! Ignore this event if needed.
32 if (!this->GetInterruptEnabled()) {
33 return;
34 }
35
36 // Disable the timer interrupt while we handle this.
37 this->DisableInterrupt();
38
39 if (const s64 next_time = this->DoInterruptTaskImpl(GetTick());
40 0 < next_time && next_time <= m_wakeup_time) {
41 // We have a next time, so we should set the time to interrupt and turn the interrupt
42 // on.
43 this->EnableInterrupt(next_time);
44 }
45 }
46
47 // Clear the timer interrupt.
48 // Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_NonSecurePhysicalTimer,
49 // GetCurrentCoreId());
50}
51
52void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
53 this->DisableInterrupt();
54
55 m_wakeup_time = wakeup_time;
56 m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
57 m_event_type, reinterpret_cast<uintptr_t>(this),
58 true);
59}
60
61void KHardwareTimer::DisableInterrupt() {
62 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
63 m_wakeup_time = std::numeric_limits<s64>::max();
64}
65
66s64 KHardwareTimer::GetTick() const {
67 return m_kernel.System().CoreTiming().GetGlobalTimeNs().count();
68}
69
70bool KHardwareTimer::GetInterruptEnabled() {
71 return m_wakeup_time != std::numeric_limits<s64>::max();
72}
73
74} // namespace Kernel
diff --git a/src/core/hle/kernel/k_hardware_timer.h b/src/core/hle/kernel/k_hardware_timer.h
new file mode 100644
index 000000000..00bef6ea1
--- /dev/null
+++ b/src/core/hle/kernel/k_hardware_timer.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_hardware_timer_base.h"
7
8namespace Core::Timing {
9struct EventType;
10} // namespace Core::Timing
11
12namespace Kernel {
13
14class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase {
15public:
16 explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {}
17
18 // Public API.
19 void Initialize();
20 void Finalize();
21
22 s64 GetCount() const {
23 return GetTick();
24 }
25
26 void RegisterTask(KTimerTask* task, s64 time_from_now) {
27 this->RegisterAbsoluteTask(task, GetTick() + time_from_now);
28 }
29
30 void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) {
31 KScopedDisableDispatch dd{m_kernel};
32 KScopedSpinLock lk{this->GetLock()};
33
34 if (this->RegisterAbsoluteTaskImpl(task, task_time)) {
35 if (task_time <= m_wakeup_time) {
36 this->EnableInterrupt(task_time);
37 }
38 }
39 }
40
41private:
42 void EnableInterrupt(s64 wakeup_time);
43 void DisableInterrupt();
44 bool GetInterruptEnabled();
45 s64 GetTick() const;
46 void DoTask();
47
48private:
49 // Absolute time in nanoseconds
50 s64 m_wakeup_time{std::numeric_limits<s64>::max()};
51 std::shared_ptr<Core::Timing::EventType> m_event_type{};
52};
53
54} // namespace Kernel
diff --git a/src/core/hle/kernel/k_hardware_timer_base.h b/src/core/hle/kernel/k_hardware_timer_base.h
new file mode 100644
index 000000000..6318b35bd
--- /dev/null
+++ b/src/core/hle/kernel/k_hardware_timer_base.h
@@ -0,0 +1,92 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_spin_lock.h"
7#include "core/hle/kernel/k_thread.h"
8#include "core/hle/kernel/k_timer_task.h"
9
10namespace Kernel {
11
12class KHardwareTimerBase {
13public:
14 explicit KHardwareTimerBase(KernelCore& kernel) : m_kernel{kernel} {}
15
16 void CancelTask(KTimerTask* task) {
17 KScopedDisableDispatch dd{m_kernel};
18 KScopedSpinLock lk{m_lock};
19
20 if (const s64 task_time = task->GetTime(); task_time > 0) {
21 this->RemoveTaskFromTree(task);
22 }
23 }
24
25protected:
26 KSpinLock& GetLock() {
27 return m_lock;
28 }
29
30 s64 DoInterruptTaskImpl(s64 cur_time) {
31 // We want to handle all tasks, returning the next time that a task is scheduled.
32 while (true) {
33 // Get the next task. If there isn't one, return 0.
34 KTimerTask* task = m_next_task;
35 if (task == nullptr) {
36 return 0;
37 }
38
39 // If the task needs to be done in the future, do it in the future and not now.
40 if (const s64 task_time = task->GetTime(); task_time > cur_time) {
41 return task_time;
42 }
43
44 // Remove the task from the tree of tasks, and update our next task.
45 this->RemoveTaskFromTree(task);
46
47 // Handle the task.
48 task->OnTimer();
49 }
50 }
51
52 bool RegisterAbsoluteTaskImpl(KTimerTask* task, s64 task_time) {
53 ASSERT(task_time > 0);
54
55 // Set the task's time, and insert it into our tree.
56 task->SetTime(task_time);
57 m_task_tree.insert(*task);
58
59 // Update our next task if relevant.
60 if (m_next_task != nullptr && m_next_task->GetTime() <= task_time) {
61 return false;
62 }
63 m_next_task = task;
64 return true;
65 }
66
67private:
68 void RemoveTaskFromTree(KTimerTask* task) {
69 // Erase from the tree.
70 auto it = m_task_tree.erase(m_task_tree.iterator_to(*task));
71
72 // Clear the task's scheduled time.
73 task->SetTime(0);
74
75 // Update our next task if relevant.
76 if (m_next_task == task) {
77 m_next_task = (it != m_task_tree.end()) ? std::addressof(*it) : nullptr;
78 }
79 }
80
81protected:
82 KernelCore& m_kernel;
83
84private:
85 using TimerTaskTree = Common::IntrusiveRedBlackTreeBaseTraits<KTimerTask>::TreeType<KTimerTask>;
86
87 KSpinLock m_lock{};
88 TimerTaskTree m_task_tree{};
89 KTimerTask* m_next_task{};
90};
91
92} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index bd33571da..cd6ea388e 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -223,7 +223,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
223 223
224 // Ensure that we don't leave anything un-freed. 224 // Ensure that we don't leave anything un-freed.
225 ON_RESULT_FAILURE { 225 ON_RESULT_FAILURE {
226 for (const auto& it : out->Nodes()) { 226 for (const auto& it : *out) {
227 auto& manager = this->GetManager(it.GetAddress()); 227 auto& manager = this->GetManager(it.GetAddress());
228 const size_t node_num_pages = std::min<u64>( 228 const size_t node_num_pages = std::min<u64>(
229 it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); 229 it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
@@ -285,7 +285,7 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
285 m_has_optimized_process[static_cast<size_t>(pool)], true)); 285 m_has_optimized_process[static_cast<size_t>(pool)], true));
286 286
287 // Open the first reference to the pages. 287 // Open the first reference to the pages.
288 for (const auto& block : out->Nodes()) { 288 for (const auto& block : *out) {
289 PAddr cur_address = block.GetAddress(); 289 PAddr cur_address = block.GetAddress();
290 size_t remaining_pages = block.GetNumPages(); 290 size_t remaining_pages = block.GetNumPages();
291 while (remaining_pages > 0) { 291 while (remaining_pages > 0) {
@@ -335,7 +335,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
335 // Perform optimized memory tracking, if we should. 335 // Perform optimized memory tracking, if we should.
336 if (optimized) { 336 if (optimized) {
337 // Iterate over the allocated blocks. 337 // Iterate over the allocated blocks.
338 for (const auto& block : out->Nodes()) { 338 for (const auto& block : *out) {
339 // Get the block extents. 339 // Get the block extents.
340 const PAddr block_address = block.GetAddress(); 340 const PAddr block_address = block.GetAddress();
341 const size_t block_pages = block.GetNumPages(); 341 const size_t block_pages = block.GetNumPages();
@@ -391,7 +391,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
391 } 391 }
392 } else { 392 } else {
393 // Set all the allocated memory. 393 // Set all the allocated memory.
394 for (const auto& block : out->Nodes()) { 394 for (const auto& block : *out) {
395 std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern, 395 std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
396 block.GetSize()); 396 block.GetSize());
397 } 397 }
diff --git a/src/core/hle/kernel/k_page_group.cpp b/src/core/hle/kernel/k_page_group.cpp
new file mode 100644
index 000000000..d8c644a33
--- /dev/null
+++ b/src/core/hle/kernel/k_page_group.cpp
@@ -0,0 +1,121 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_dynamic_resource_manager.h"
5#include "core/hle/kernel/k_memory_manager.h"
6#include "core/hle/kernel/k_page_group.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc_results.h"
9
10namespace Kernel {
11
12void KPageGroup::Finalize() {
13 KBlockInfo* cur = m_first_block;
14 while (cur != nullptr) {
15 KBlockInfo* next = cur->GetNext();
16 m_manager->Free(cur);
17 cur = next;
18 }
19
20 m_first_block = nullptr;
21 m_last_block = nullptr;
22}
23
24void KPageGroup::CloseAndReset() {
25 auto& mm = m_kernel.MemoryManager();
26
27 KBlockInfo* cur = m_first_block;
28 while (cur != nullptr) {
29 KBlockInfo* next = cur->GetNext();
30 mm.Close(cur->GetAddress(), cur->GetNumPages());
31 m_manager->Free(cur);
32 cur = next;
33 }
34
35 m_first_block = nullptr;
36 m_last_block = nullptr;
37}
38
39size_t KPageGroup::GetNumPages() const {
40 size_t num_pages = 0;
41
42 for (const auto& it : *this) {
43 num_pages += it.GetNumPages();
44 }
45
46 return num_pages;
47}
48
49Result KPageGroup::AddBlock(KPhysicalAddress addr, size_t num_pages) {
50 // Succeed immediately if we're adding no pages.
51 R_SUCCEED_IF(num_pages == 0);
52
53 // Check for overflow.
54 ASSERT(addr < addr + num_pages * PageSize);
55
56 // Try to just append to the last block.
57 if (m_last_block != nullptr) {
58 R_SUCCEED_IF(m_last_block->TryConcatenate(addr, num_pages));
59 }
60
61 // Allocate a new block.
62 KBlockInfo* new_block = m_manager->Allocate();
63 R_UNLESS(new_block != nullptr, ResultOutOfResource);
64
65 // Initialize the block.
66 new_block->Initialize(addr, num_pages);
67
68 // Add the block to our list.
69 if (m_last_block != nullptr) {
70 m_last_block->SetNext(new_block);
71 } else {
72 m_first_block = new_block;
73 }
74 m_last_block = new_block;
75
76 R_SUCCEED();
77}
78
79void KPageGroup::Open() const {
80 auto& mm = m_kernel.MemoryManager();
81
82 for (const auto& it : *this) {
83 mm.Open(it.GetAddress(), it.GetNumPages());
84 }
85}
86
87void KPageGroup::OpenFirst() const {
88 auto& mm = m_kernel.MemoryManager();
89
90 for (const auto& it : *this) {
91 mm.OpenFirst(it.GetAddress(), it.GetNumPages());
92 }
93}
94
95void KPageGroup::Close() const {
96 auto& mm = m_kernel.MemoryManager();
97
98 for (const auto& it : *this) {
99 mm.Close(it.GetAddress(), it.GetNumPages());
100 }
101}
102
103bool KPageGroup::IsEquivalentTo(const KPageGroup& rhs) const {
104 auto lit = this->begin();
105 auto rit = rhs.begin();
106 auto lend = this->end();
107 auto rend = rhs.end();
108
109 while (lit != lend && rit != rend) {
110 if (*lit != *rit) {
111 return false;
112 }
113
114 ++lit;
115 ++rit;
116 }
117
118 return lit == lend && rit == rend;
119}
120
121} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_group.h b/src/core/hle/kernel/k_page_group.h
index 316f172f2..c07f17663 100644
--- a/src/core/hle/kernel/k_page_group.h
+++ b/src/core/hle/kernel/k_page_group.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
@@ -13,24 +13,23 @@
13 13
14namespace Kernel { 14namespace Kernel {
15 15
16class KBlockInfoManager;
17class KernelCore;
16class KPageGroup; 18class KPageGroup;
17 19
18class KBlockInfo { 20class KBlockInfo {
19private:
20 friend class KPageGroup;
21
22public: 21public:
23 constexpr KBlockInfo() = default; 22 constexpr explicit KBlockInfo() : m_next(nullptr) {}
24 23
25 constexpr void Initialize(PAddr addr, size_t np) { 24 constexpr void Initialize(KPhysicalAddress addr, size_t np) {
26 ASSERT(Common::IsAligned(addr, PageSize)); 25 ASSERT(Common::IsAligned(addr, PageSize));
27 ASSERT(static_cast<u32>(np) == np); 26 ASSERT(static_cast<u32>(np) == np);
28 27
29 m_page_index = static_cast<u32>(addr) / PageSize; 28 m_page_index = static_cast<u32>(addr / PageSize);
30 m_num_pages = static_cast<u32>(np); 29 m_num_pages = static_cast<u32>(np);
31 } 30 }
32 31
33 constexpr PAddr GetAddress() const { 32 constexpr KPhysicalAddress GetAddress() const {
34 return m_page_index * PageSize; 33 return m_page_index * PageSize;
35 } 34 }
36 constexpr size_t GetNumPages() const { 35 constexpr size_t GetNumPages() const {
@@ -39,10 +38,10 @@ public:
39 constexpr size_t GetSize() const { 38 constexpr size_t GetSize() const {
40 return this->GetNumPages() * PageSize; 39 return this->GetNumPages() * PageSize;
41 } 40 }
42 constexpr PAddr GetEndAddress() const { 41 constexpr KPhysicalAddress GetEndAddress() const {
43 return (m_page_index + m_num_pages) * PageSize; 42 return (m_page_index + m_num_pages) * PageSize;
44 } 43 }
45 constexpr PAddr GetLastAddress() const { 44 constexpr KPhysicalAddress GetLastAddress() const {
46 return this->GetEndAddress() - 1; 45 return this->GetEndAddress() - 1;
47 } 46 }
48 47
@@ -62,8 +61,8 @@ public:
62 return !(*this == rhs); 61 return !(*this == rhs);
63 } 62 }
64 63
65 constexpr bool IsStrictlyBefore(PAddr addr) const { 64 constexpr bool IsStrictlyBefore(KPhysicalAddress addr) const {
66 const PAddr end = this->GetEndAddress(); 65 const KPhysicalAddress end = this->GetEndAddress();
67 66
68 if (m_page_index != 0 && end == 0) { 67 if (m_page_index != 0 && end == 0) {
69 return false; 68 return false;
@@ -72,11 +71,11 @@ public:
72 return end < addr; 71 return end < addr;
73 } 72 }
74 73
75 constexpr bool operator<(PAddr addr) const { 74 constexpr bool operator<(KPhysicalAddress addr) const {
76 return this->IsStrictlyBefore(addr); 75 return this->IsStrictlyBefore(addr);
77 } 76 }
78 77
79 constexpr bool TryConcatenate(PAddr addr, size_t np) { 78 constexpr bool TryConcatenate(KPhysicalAddress addr, size_t np) {
80 if (addr != 0 && addr == this->GetEndAddress()) { 79 if (addr != 0 && addr == this->GetEndAddress()) {
81 m_num_pages += static_cast<u32>(np); 80 m_num_pages += static_cast<u32>(np);
82 return true; 81 return true;
@@ -90,96 +89,118 @@ private:
90 } 89 }
91 90
92private: 91private:
92 friend class KPageGroup;
93
93 KBlockInfo* m_next{}; 94 KBlockInfo* m_next{};
94 u32 m_page_index{}; 95 u32 m_page_index{};
95 u32 m_num_pages{}; 96 u32 m_num_pages{};
96}; 97};
97static_assert(sizeof(KBlockInfo) <= 0x10); 98static_assert(sizeof(KBlockInfo) <= 0x10);
98 99
99class KPageGroup final { 100class KPageGroup {
100public: 101public:
101 class Node final { 102 class Iterator {
102 public: 103 public:
103 constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {} 104 using iterator_category = std::forward_iterator_tag;
105 using value_type = const KBlockInfo;
106 using difference_type = std::ptrdiff_t;
107 using pointer = value_type*;
108 using reference = value_type&;
109
110 constexpr explicit Iterator(pointer n) : m_node(n) {}
111
112 constexpr bool operator==(const Iterator& rhs) const {
113 return m_node == rhs.m_node;
114 }
115 constexpr bool operator!=(const Iterator& rhs) const {
116 return !(*this == rhs);
117 }
104 118
105 constexpr u64 GetAddress() const { 119 constexpr pointer operator->() const {
106 return addr; 120 return m_node;
121 }
122 constexpr reference operator*() const {
123 return *m_node;
107 } 124 }
108 125
109 constexpr std::size_t GetNumPages() const { 126 constexpr Iterator& operator++() {
110 return num_pages; 127 m_node = m_node->GetNext();
128 return *this;
111 } 129 }
112 130
113 constexpr std::size_t GetSize() const { 131 constexpr Iterator operator++(int) {
114 return GetNumPages() * PageSize; 132 const Iterator it{*this};
133 ++(*this);
134 return it;
115 } 135 }
116 136
117 private: 137 private:
118 u64 addr{}; 138 pointer m_node{};
119 std::size_t num_pages{};
120 }; 139 };
121 140
122public: 141 explicit KPageGroup(KernelCore& kernel, KBlockInfoManager* m)
123 KPageGroup() = default; 142 : m_kernel{kernel}, m_manager{m} {}
124 KPageGroup(u64 address, u64 num_pages) { 143 ~KPageGroup() {
125 ASSERT(AddBlock(address, num_pages).IsSuccess()); 144 this->Finalize();
126 } 145 }
127 146
128 constexpr std::list<Node>& Nodes() { 147 void CloseAndReset();
129 return nodes; 148 void Finalize();
130 }
131 149
132 constexpr const std::list<Node>& Nodes() const { 150 Iterator begin() const {
133 return nodes; 151 return Iterator{m_first_block};
152 }
153 Iterator end() const {
154 return Iterator{nullptr};
155 }
156 bool empty() const {
157 return m_first_block == nullptr;
134 } 158 }
135 159
136 std::size_t GetNumPages() const { 160 Result AddBlock(KPhysicalAddress addr, size_t num_pages);
137 std::size_t num_pages = 0; 161 void Open() const;
138 for (const Node& node : nodes) { 162 void OpenFirst() const;
139 num_pages += node.GetNumPages(); 163 void Close() const;
140 } 164
141 return num_pages; 165 size_t GetNumPages() const;
142 } 166
143 167 bool IsEquivalentTo(const KPageGroup& rhs) const;
144 bool IsEqual(KPageGroup& other) const { 168
145 auto this_node = nodes.begin(); 169 bool operator==(const KPageGroup& rhs) const {
146 auto other_node = other.nodes.begin(); 170 return this->IsEquivalentTo(rhs);
147 while (this_node != nodes.end() && other_node != other.nodes.end()) { 171 }
148 if (this_node->GetAddress() != other_node->GetAddress() ||
149 this_node->GetNumPages() != other_node->GetNumPages()) {
150 return false;
151 }
152 this_node = std::next(this_node);
153 other_node = std::next(other_node);
154 }
155 172
156 return this_node == nodes.end() && other_node == other.nodes.end(); 173 bool operator!=(const KPageGroup& rhs) const {
174 return !(*this == rhs);
157 } 175 }
158 176
159 Result AddBlock(u64 address, u64 num_pages) { 177private:
160 if (!num_pages) { 178 KernelCore& m_kernel;
161 return ResultSuccess; 179 KBlockInfo* m_first_block{};
180 KBlockInfo* m_last_block{};
181 KBlockInfoManager* m_manager{};
182};
183
184class KScopedPageGroup {
185public:
186 explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) {
187 if (m_pg) {
188 m_pg->Open();
162 } 189 }
163 if (!nodes.empty()) { 190 }
164 const auto node = nodes.back(); 191 explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {}
165 if (node.GetAddress() + node.GetNumPages() * PageSize == address) { 192 ~KScopedPageGroup() {
166 address = node.GetAddress(); 193 if (m_pg) {
167 num_pages += node.GetNumPages(); 194 m_pg->Close();
168 nodes.pop_back();
169 }
170 } 195 }
171 nodes.push_back({address, num_pages});
172 return ResultSuccess;
173 } 196 }
174 197
175 bool Empty() const { 198 void CancelClose() {
176 return nodes.empty(); 199 m_pg = nullptr;
177 } 200 }
178 201
179 void Finalize() {}
180
181private: 202private:
182 std::list<Node> nodes; 203 const KPageGroup* m_pg{};
183}; 204};
184 205
185} // namespace Kernel 206} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 612fc76fa..9c7ac22dc 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -100,7 +100,7 @@ constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType a
100 100
101KPageTable::KPageTable(Core::System& system_) 101KPageTable::KPageTable(Core::System& system_)
102 : m_general_lock{system_.Kernel()}, 102 : m_general_lock{system_.Kernel()},
103 m_map_physical_memory_lock{system_.Kernel()}, m_system{system_} {} 103 m_map_physical_memory_lock{system_.Kernel()}, m_system{system_}, m_kernel{system_.Kernel()} {}
104 104
105KPageTable::~KPageTable() = default; 105KPageTable::~KPageTable() = default;
106 106
@@ -373,7 +373,7 @@ Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState sta
373 m_memory_block_slab_manager); 373 m_memory_block_slab_manager);
374 374
375 // Allocate and open. 375 // Allocate and open.
376 KPageGroup pg; 376 KPageGroup pg{m_kernel, m_block_info_manager};
377 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( 377 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
378 &pg, num_pages, 378 &pg, num_pages,
379 KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option))); 379 KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option)));
@@ -432,7 +432,7 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
432 const size_t num_pages = size / PageSize; 432 const size_t num_pages = size / PageSize;
433 433
434 // Create page groups for the memory being mapped. 434 // Create page groups for the memory being mapped.
435 KPageGroup pg; 435 KPageGroup pg{m_kernel, m_block_info_manager};
436 AddRegionToPages(src_address, num_pages, pg); 436 AddRegionToPages(src_address, num_pages, pg);
437 437
438 // Reprotect the source as kernel-read/not mapped. 438 // Reprotect the source as kernel-read/not mapped.
@@ -593,7 +593,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
593 const size_t size = num_pages * PageSize; 593 const size_t size = num_pages * PageSize;
594 594
595 // We're making a new group, not adding to an existing one. 595 // We're making a new group, not adding to an existing one.
596 R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory); 596 R_UNLESS(pg.empty(), ResultInvalidCurrentMemory);
597 597
598 // Begin traversal. 598 // Begin traversal.
599 Common::PageTable::TraversalContext context; 599 Common::PageTable::TraversalContext context;
@@ -640,11 +640,10 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
640 R_SUCCEED(); 640 R_SUCCEED();
641} 641}
642 642
643bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) { 643bool KPageTable::IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages) {
644 ASSERT(this->IsLockedByCurrentThread()); 644 ASSERT(this->IsLockedByCurrentThread());
645 645
646 const size_t size = num_pages * PageSize; 646 const size_t size = num_pages * PageSize;
647 const auto& pg = pg_ll.Nodes();
648 const auto& memory_layout = m_system.Kernel().MemoryLayout(); 647 const auto& memory_layout = m_system.Kernel().MemoryLayout();
649 648
650 // Empty groups are necessarily invalid. 649 // Empty groups are necessarily invalid.
@@ -942,9 +941,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
942 941
943 ON_RESULT_FAILURE { 942 ON_RESULT_FAILURE {
944 if (cur_mapped_addr != dst_addr) { 943 if (cur_mapped_addr != dst_addr) {
945 // HACK: Manually close the pages.
946 HACK_ClosePages(dst_addr, (cur_mapped_addr - dst_addr) / PageSize);
947
948 ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize, 944 ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize,
949 KMemoryPermission::None, OperationType::Unmap) 945 KMemoryPermission::None, OperationType::Unmap)
950 .IsSuccess()); 946 .IsSuccess());
@@ -1020,9 +1016,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
1020 // Map the page. 1016 // Map the page.
1021 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page)); 1017 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page));
1022 1018
1023 // HACK: Manually open the pages.
1024 HACK_OpenPages(start_partial_page, 1);
1025
1026 // Update tracking extents. 1019 // Update tracking extents.
1027 cur_mapped_addr += PageSize; 1020 cur_mapped_addr += PageSize;
1028 cur_block_addr += PageSize; 1021 cur_block_addr += PageSize;
@@ -1051,9 +1044,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
1051 R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map, 1044 R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map,
1052 cur_block_addr)); 1045 cur_block_addr));
1053 1046
1054 // HACK: Manually open the pages.
1055 HACK_OpenPages(cur_block_addr, cur_block_size / PageSize);
1056
1057 // Update tracking extents. 1047 // Update tracking extents.
1058 cur_mapped_addr += cur_block_size; 1048 cur_mapped_addr += cur_block_size;
1059 cur_block_addr = next_entry.phys_addr; 1049 cur_block_addr = next_entry.phys_addr;
@@ -1073,9 +1063,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
1073 R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map, 1063 R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map,
1074 cur_block_addr)); 1064 cur_block_addr));
1075 1065
1076 // HACK: Manually open the pages.
1077 HACK_OpenPages(cur_block_addr, last_block_size / PageSize);
1078
1079 // Update tracking extents. 1066 // Update tracking extents.
1080 cur_mapped_addr += last_block_size; 1067 cur_mapped_addr += last_block_size;
1081 cur_block_addr += last_block_size; 1068 cur_block_addr += last_block_size;
@@ -1107,9 +1094,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
1107 1094
1108 // Map the page. 1095 // Map the page.
1109 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page)); 1096 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page));
1110
1111 // HACK: Manually open the pages.
1112 HACK_OpenPages(end_partial_page, 1);
1113 } 1097 }
1114 1098
1115 // Update memory blocks to reflect our changes 1099 // Update memory blocks to reflect our changes
@@ -1211,9 +1195,6 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState
1211 const size_t aligned_size = aligned_end - aligned_start; 1195 const size_t aligned_size = aligned_end - aligned_start;
1212 const size_t aligned_num_pages = aligned_size / PageSize; 1196 const size_t aligned_num_pages = aligned_size / PageSize;
1213 1197
1214 // HACK: Manually close the pages.
1215 HACK_ClosePages(aligned_start, aligned_num_pages);
1216
1217 // Unmap the pages. 1198 // Unmap the pages.
1218 R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap)); 1199 R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap));
1219 1200
@@ -1501,17 +1482,6 @@ void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLi
1501 } 1482 }
1502} 1483}
1503 1484
1504void KPageTable::HACK_OpenPages(PAddr phys_addr, size_t num_pages) {
1505 m_system.Kernel().MemoryManager().OpenFirst(phys_addr, num_pages);
1506}
1507
1508void KPageTable::HACK_ClosePages(VAddr virt_addr, size_t num_pages) {
1509 for (size_t index = 0; index < num_pages; ++index) {
1510 const auto paddr = GetPhysicalAddr(virt_addr + (index * PageSize));
1511 m_system.Kernel().MemoryManager().Close(paddr, 1);
1512 }
1513}
1514
1515Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { 1485Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1516 // Lock the physical memory lock. 1486 // Lock the physical memory lock.
1517 KScopedLightLock phys_lk(m_map_physical_memory_lock); 1487 KScopedLightLock phys_lk(m_map_physical_memory_lock);
@@ -1572,7 +1542,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1572 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 1542 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
1573 1543
1574 // Allocate pages for the new memory. 1544 // Allocate pages for the new memory.
1575 KPageGroup pg; 1545 KPageGroup pg{m_kernel, m_block_info_manager};
1576 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( 1546 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
1577 &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0)); 1547 &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0));
1578 1548
@@ -1650,7 +1620,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1650 KScopedPageTableUpdater updater(this); 1620 KScopedPageTableUpdater updater(this);
1651 1621
1652 // Prepare to iterate over the memory. 1622 // Prepare to iterate over the memory.
1653 auto pg_it = pg.Nodes().begin(); 1623 auto pg_it = pg.begin();
1654 PAddr pg_phys_addr = pg_it->GetAddress(); 1624 PAddr pg_phys_addr = pg_it->GetAddress();
1655 size_t pg_pages = pg_it->GetNumPages(); 1625 size_t pg_pages = pg_it->GetNumPages();
1656 1626
@@ -1680,9 +1650,6 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1680 last_unmap_address + 1 - cur_address) / 1650 last_unmap_address + 1 - cur_address) /
1681 PageSize; 1651 PageSize;
1682 1652
1683 // HACK: Manually close the pages.
1684 HACK_ClosePages(cur_address, cur_pages);
1685
1686 // Unmap. 1653 // Unmap.
1687 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, 1654 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
1688 OperationType::Unmap) 1655 OperationType::Unmap)
@@ -1703,7 +1670,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1703 // Release any remaining unmapped memory. 1670 // Release any remaining unmapped memory.
1704 m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages); 1671 m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
1705 m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages); 1672 m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages);
1706 for (++pg_it; pg_it != pg.Nodes().end(); ++pg_it) { 1673 for (++pg_it; pg_it != pg.end(); ++pg_it) {
1707 m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(), 1674 m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(),
1708 pg_it->GetNumPages()); 1675 pg_it->GetNumPages());
1709 m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(), 1676 m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(),
@@ -1731,7 +1698,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1731 // Check if we're at the end of the physical block. 1698 // Check if we're at the end of the physical block.
1732 if (pg_pages == 0) { 1699 if (pg_pages == 0) {
1733 // Ensure there are more pages to map. 1700 // Ensure there are more pages to map.
1734 ASSERT(pg_it != pg.Nodes().end()); 1701 ASSERT(pg_it != pg.end());
1735 1702
1736 // Advance our physical block. 1703 // Advance our physical block.
1737 ++pg_it; 1704 ++pg_it;
@@ -1742,10 +1709,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1742 // Map whatever we can. 1709 // Map whatever we can.
1743 const size_t cur_pages = std::min(pg_pages, map_pages); 1710 const size_t cur_pages = std::min(pg_pages, map_pages);
1744 R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, 1711 R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
1745 OperationType::Map, pg_phys_addr)); 1712 OperationType::MapFirst, pg_phys_addr));
1746
1747 // HACK: Manually open the pages.
1748 HACK_OpenPages(pg_phys_addr, cur_pages);
1749 1713
1750 // Advance. 1714 // Advance.
1751 cur_address += cur_pages * PageSize; 1715 cur_address += cur_pages * PageSize;
@@ -1888,9 +1852,6 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1888 last_address + 1 - cur_address) / 1852 last_address + 1 - cur_address) /
1889 PageSize; 1853 PageSize;
1890 1854
1891 // HACK: Manually close the pages.
1892 HACK_ClosePages(cur_address, cur_pages);
1893
1894 // Unmap. 1855 // Unmap.
1895 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap) 1856 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)
1896 .IsSuccess()); 1857 .IsSuccess());
@@ -1955,7 +1916,7 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size)
1955 R_TRY(dst_allocator_result); 1916 R_TRY(dst_allocator_result);
1956 1917
1957 // Map the memory. 1918 // Map the memory.
1958 KPageGroup page_linked_list; 1919 KPageGroup page_linked_list{m_kernel, m_block_info_manager};
1959 const size_t num_pages{size / PageSize}; 1920 const size_t num_pages{size / PageSize};
1960 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( 1921 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1961 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); 1922 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
@@ -2022,14 +1983,14 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size
2022 num_dst_allocator_blocks); 1983 num_dst_allocator_blocks);
2023 R_TRY(dst_allocator_result); 1984 R_TRY(dst_allocator_result);
2024 1985
2025 KPageGroup src_pages; 1986 KPageGroup src_pages{m_kernel, m_block_info_manager};
2026 KPageGroup dst_pages; 1987 KPageGroup dst_pages{m_kernel, m_block_info_manager};
2027 const size_t num_pages{size / PageSize}; 1988 const size_t num_pages{size / PageSize};
2028 1989
2029 AddRegionToPages(src_address, num_pages, src_pages); 1990 AddRegionToPages(src_address, num_pages, src_pages);
2030 AddRegionToPages(dst_address, num_pages, dst_pages); 1991 AddRegionToPages(dst_address, num_pages, dst_pages);
2031 1992
2032 R_UNLESS(dst_pages.IsEqual(src_pages), ResultInvalidMemoryRegion); 1993 R_UNLESS(dst_pages.IsEquivalentTo(src_pages), ResultInvalidMemoryRegion);
2033 1994
2034 { 1995 {
2035 auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); 1996 auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); });
@@ -2060,7 +2021,7 @@ Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list,
2060 2021
2061 VAddr cur_addr{addr}; 2022 VAddr cur_addr{addr};
2062 2023
2063 for (const auto& node : page_linked_list.Nodes()) { 2024 for (const auto& node : page_linked_list) {
2064 if (const auto result{ 2025 if (const auto result{
2065 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 2026 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
2066 result.IsError()) { 2027 result.IsError()) {
@@ -2160,7 +2121,7 @@ Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) {
2160 2121
2161 VAddr cur_addr{addr}; 2122 VAddr cur_addr{addr};
2162 2123
2163 for (const auto& node : page_linked_list.Nodes()) { 2124 for (const auto& node : page_linked_list) {
2164 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, 2125 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
2165 OperationType::Unmap)}; 2126 OperationType::Unmap)};
2166 result.IsError()) { 2127 result.IsError()) {
@@ -2527,13 +2488,13 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
2527 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 2488 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
2528 2489
2529 // Allocate pages for the heap extension. 2490 // Allocate pages for the heap extension.
2530 KPageGroup pg; 2491 KPageGroup pg{m_kernel, m_block_info_manager};
2531 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( 2492 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
2532 &pg, allocation_size / PageSize, 2493 &pg, allocation_size / PageSize,
2533 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); 2494 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
2534 2495
2535 // Clear all the newly allocated pages. 2496 // Clear all the newly allocated pages.
2536 for (const auto& it : pg.Nodes()) { 2497 for (const auto& it : pg) {
2537 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value, 2498 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2538 it.GetSize()); 2499 it.GetSize());
2539 } 2500 }
@@ -2610,11 +2571,23 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_
2610 if (is_map_only) { 2571 if (is_map_only) {
2611 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); 2572 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
2612 } else { 2573 } else {
2613 KPageGroup page_group; 2574 // Create a page group tohold the pages we allocate.
2614 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( 2575 KPageGroup pg{m_kernel, m_block_info_manager};
2615 &page_group, needed_num_pages, 2576
2616 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0)); 2577 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
2617 R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); 2578 &pg, needed_num_pages,
2579 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
2580
2581 // Ensure that the page group is closed when we're done working with it.
2582 SCOPE_EXIT({ pg.Close(); });
2583
2584 // Clear all pages.
2585 for (const auto& it : pg) {
2586 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()),
2587 m_heap_fill_value, it.GetSize());
2588 }
2589
2590 R_TRY(Operate(addr, needed_num_pages, pg, OperationType::MapGroup));
2618 } 2591 }
2619 2592
2620 // Update the blocks. 2593 // Update the blocks.
@@ -2795,19 +2768,28 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_
2795 ASSERT(num_pages > 0); 2768 ASSERT(num_pages > 0);
2796 ASSERT(num_pages == page_group.GetNumPages()); 2769 ASSERT(num_pages == page_group.GetNumPages());
2797 2770
2798 for (const auto& node : page_group.Nodes()) { 2771 switch (operation) {
2799 const size_t size{node.GetNumPages() * PageSize}; 2772 case OperationType::MapGroup: {
2773 // We want to maintain a new reference to every page in the group.
2774 KScopedPageGroup spg(page_group);
2775
2776 for (const auto& node : page_group) {
2777 const size_t size{node.GetNumPages() * PageSize};
2800 2778
2801 switch (operation) { 2779 // Map the pages.
2802 case OperationType::MapGroup:
2803 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress()); 2780 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress());
2804 break; 2781
2805 default: 2782 addr += size;
2806 ASSERT(false);
2807 break;
2808 } 2783 }
2809 2784
2810 addr += size; 2785 // We succeeded! We want to persist the reference to the pages.
2786 spg.CancelClose();
2787
2788 break;
2789 }
2790 default:
2791 ASSERT(false);
2792 break;
2811 } 2793 }
2812 2794
2813 R_SUCCEED(); 2795 R_SUCCEED();
@@ -2822,13 +2804,29 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
2822 ASSERT(ContainsPages(addr, num_pages)); 2804 ASSERT(ContainsPages(addr, num_pages));
2823 2805
2824 switch (operation) { 2806 switch (operation) {
2825 case OperationType::Unmap: 2807 case OperationType::Unmap: {
2808 // Ensure that any pages we track close on exit.
2809 KPageGroup pages_to_close{m_kernel, this->GetBlockInfoManager()};
2810 SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
2811
2812 this->AddRegionToPages(addr, num_pages, pages_to_close);
2826 m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); 2813 m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
2827 break; 2814 break;
2815 }
2816 case OperationType::MapFirst:
2828 case OperationType::Map: { 2817 case OperationType::Map: {
2829 ASSERT(map_addr); 2818 ASSERT(map_addr);
2830 ASSERT(Common::IsAligned(map_addr, PageSize)); 2819 ASSERT(Common::IsAligned(map_addr, PageSize));
2831 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); 2820 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr);
2821
2822 // Open references to pages, if we should.
2823 if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
2824 if (operation == OperationType::MapFirst) {
2825 m_kernel.MemoryManager().OpenFirst(map_addr, num_pages);
2826 } else {
2827 m_kernel.MemoryManager().Open(map_addr, num_pages);
2828 }
2829 }
2832 break; 2830 break;
2833 } 2831 }
2834 case OperationType::Separate: { 2832 case OperationType::Separate: {
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index f1ca785d7..0a454b05b 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -107,6 +107,10 @@ public:
107 return *m_page_table_impl; 107 return *m_page_table_impl;
108 } 108 }
109 109
110 KBlockInfoManager* GetBlockInfoManager() {
111 return m_block_info_manager;
112 }
113
110 bool CanContain(VAddr addr, size_t size, KMemoryState state) const; 114 bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
111 115
112protected: 116protected:
@@ -261,10 +265,6 @@ private:
261 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, 265 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
262 size_t size, KMemoryPermission prot_perm); 266 size_t size, KMemoryPermission prot_perm);
263 267
264 // HACK: These will be removed once we automatically manage page reference counts.
265 void HACK_OpenPages(PAddr phys_addr, size_t num_pages);
266 void HACK_ClosePages(VAddr virt_addr, size_t num_pages);
267
268 mutable KLightLock m_general_lock; 268 mutable KLightLock m_general_lock;
269 mutable KLightLock m_map_physical_memory_lock; 269 mutable KLightLock m_map_physical_memory_lock;
270 270
@@ -488,6 +488,7 @@ private:
488 std::unique_ptr<Common::PageTable> m_page_table_impl; 488 std::unique_ptr<Common::PageTable> m_page_table_impl;
489 489
490 Core::System& m_system; 490 Core::System& m_system;
491 KernelCore& m_kernel;
491}; 492};
492 493
493} // namespace Kernel 494} // namespace Kernel
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index d1dc62401..a1abf5d68 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -285,6 +285,17 @@ void KProcess::UnregisterThread(KThread* thread) {
285 thread_list.remove(thread); 285 thread_list.remove(thread);
286} 286}
287 287
288u64 KProcess::GetFreeThreadCount() const {
289 if (resource_limit != nullptr) {
290 const auto current_value =
291 resource_limit->GetCurrentValue(LimitableResource::ThreadCountMax);
292 const auto limit_value = resource_limit->GetLimitValue(LimitableResource::ThreadCountMax);
293 return limit_value - current_value;
294 } else {
295 return 0;
296 }
297}
298
288Result KProcess::Reset() { 299Result KProcess::Reset() {
289 // Lock the process and the scheduler. 300 // Lock the process and the scheduler.
290 KScopedLightLock lk(state_lock); 301 KScopedLightLock lk(state_lock);
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 2e0cc3d0b..09bf2f1d0 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -304,6 +304,9 @@ public:
304 /// from this process' thread list. 304 /// from this process' thread list.
305 void UnregisterThread(KThread* thread); 305 void UnregisterThread(KThread* thread);
306 306
307 /// Retrieves the number of available threads for this process.
308 u64 GetFreeThreadCount() const;
309
307 /// Clears the signaled state of the process if and only if it's signaled. 310 /// Clears the signaled state of the process if and only if it's signaled.
308 /// 311 ///
309 /// @pre The process must not be already terminated. If this is called on a 312 /// @pre The process must not be already terminated. If this is called on a
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index 76c095e69..76db65a4d 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -5,9 +5,9 @@
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/kernel/global_scheduler_context.h" 7#include "core/hle/kernel/global_scheduler_context.h"
8#include "core/hle/kernel/k_hardware_timer.h"
8#include "core/hle/kernel/k_thread.h" 9#include "core/hle/kernel/k_thread.h"
9#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/time_manager.h"
11 11
12namespace Kernel { 12namespace Kernel {
13 13
@@ -22,7 +22,7 @@ public:
22 ~KScopedSchedulerLockAndSleep() { 22 ~KScopedSchedulerLockAndSleep() {
23 // Register the sleep. 23 // Register the sleep.
24 if (timeout_tick > 0) { 24 if (timeout_tick > 0) {
25 kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick); 25 kernel.HardwareTimer().RegisterTask(thread, timeout_tick);
26 } 26 }
27 27
28 // Unlock the scheduler. 28 // Unlock the scheduler.
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 10cd4c43d..3cf2b5d91 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -6,31 +6,29 @@
6#include "core/hle/kernel/k_page_table.h" 6#include "core/hle/kernel/k_page_table.h"
7#include "core/hle/kernel/k_scoped_resource_reservation.h" 7#include "core/hle/kernel/k_scoped_resource_reservation.h"
8#include "core/hle/kernel/k_shared_memory.h" 8#include "core/hle/kernel/k_shared_memory.h"
9#include "core/hle/kernel/k_system_resource.h"
9#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/svc_results.h" 11#include "core/hle/kernel/svc_results.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
14KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} 15KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
15 16KSharedMemory::~KSharedMemory() = default;
16KSharedMemory::~KSharedMemory() {
17 kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size);
18}
19 17
20Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, 18Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
21 KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_, 19 Svc::MemoryPermission owner_permission_,
22 Svc::MemoryPermission user_permission_, PAddr physical_address_, 20 Svc::MemoryPermission user_permission_, std::size_t size_,
23 std::size_t size_, std::string name_) { 21 std::string name_) {
24 // Set members. 22 // Set members.
25 owner_process = owner_process_; 23 owner_process = owner_process_;
26 device_memory = &device_memory_; 24 device_memory = &device_memory_;
27 page_list = std::move(page_list_);
28 owner_permission = owner_permission_; 25 owner_permission = owner_permission_;
29 user_permission = user_permission_; 26 user_permission = user_permission_;
30 physical_address = physical_address_; 27 size = Common::AlignUp(size_, PageSize);
31 size = size_;
32 name = std::move(name_); 28 name = std::move(name_);
33 29
30 const size_t num_pages = Common::DivideUp(size, PageSize);
31
34 // Get the resource limit. 32 // Get the resource limit.
35 KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); 33 KResourceLimit* reslimit = kernel.GetSystemResourceLimit();
36 34
@@ -39,6 +37,18 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
39 size_); 37 size_);
40 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 38 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
41 39
40 // Allocate the memory.
41
42 //! HACK: Open continuous mapping from sysmodule pool.
43 auto option = KMemoryManager::EncodeOption(KMemoryManager::Pool::Secure,
44 KMemoryManager::Direction::FromBack);
45 physical_address = kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, option);
46 R_UNLESS(physical_address != 0, ResultOutOfMemory);
47
48 //! Insert the result into our page group.
49 page_group.emplace(kernel, &kernel.GetSystemSystemResource().GetBlockInfoManager());
50 page_group->AddBlock(physical_address, num_pages);
51
42 // Commit our reservation. 52 // Commit our reservation.
43 memory_reservation.Commit(); 53 memory_reservation.Commit();
44 54
@@ -50,12 +60,18 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
50 is_initialized = true; 60 is_initialized = true;
51 61
52 // Clear all pages in the memory. 62 // Clear all pages in the memory.
53 std::memset(device_memory_.GetPointer<void>(physical_address_), 0, size_); 63 for (const auto& block : *page_group) {
64 std::memset(device_memory_.GetPointer<void>(block.GetAddress()), 0, block.GetSize());
65 }
54 66
55 return ResultSuccess; 67 return ResultSuccess;
56} 68}
57 69
58void KSharedMemory::Finalize() { 70void KSharedMemory::Finalize() {
71 // Close and finalize the page group.
72 page_group->Close();
73 page_group->Finalize();
74
59 // Release the memory reservation. 75 // Release the memory reservation.
60 resource_limit->Release(LimitableResource::PhysicalMemoryMax, size); 76 resource_limit->Release(LimitableResource::PhysicalMemoryMax, size);
61 resource_limit->Close(); 77 resource_limit->Close();
@@ -65,32 +81,28 @@ void KSharedMemory::Finalize() {
65} 81}
66 82
67Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size, 83Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size,
68 Svc::MemoryPermission permissions) { 84 Svc::MemoryPermission map_perm) {
69 const u64 page_count{(map_size + PageSize - 1) / PageSize}; 85 // Validate the size.
70 86 R_UNLESS(size == map_size, ResultInvalidSize);
71 if (page_list.GetNumPages() != page_count) {
72 UNIMPLEMENTED_MSG("Page count does not match");
73 }
74 87
75 const Svc::MemoryPermission expected = 88 // Validate the permission.
89 const Svc::MemoryPermission test_perm =
76 &target_process == owner_process ? owner_permission : user_permission; 90 &target_process == owner_process ? owner_permission : user_permission;
77 91 if (test_perm == Svc::MemoryPermission::DontCare) {
78 if (permissions != expected) { 92 ASSERT(map_perm == Svc::MemoryPermission::Read || map_perm == Svc::MemoryPermission::Write);
79 UNIMPLEMENTED_MSG("Permission does not match"); 93 } else {
94 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission);
80 } 95 }
81 96
82 return target_process.PageTable().MapPages(address, page_list, KMemoryState::Shared, 97 return target_process.PageTable().MapPages(address, *page_group, KMemoryState::Shared,
83 ConvertToKMemoryPermission(permissions)); 98 ConvertToKMemoryPermission(map_perm));
84} 99}
85 100
86Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { 101Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
87 const u64 page_count{(unmap_size + PageSize - 1) / PageSize}; 102 // Validate the size.
88 103 R_UNLESS(size == unmap_size, ResultInvalidSize);
89 if (page_list.GetNumPages() != page_count) {
90 UNIMPLEMENTED_MSG("Page count does not match");
91 }
92 104
93 return target_process.PageTable().UnmapPages(address, page_list, KMemoryState::Shared); 105 return target_process.PageTable().UnmapPages(address, *page_group, KMemoryState::Shared);
94} 106}
95 107
96} // namespace Kernel 108} // namespace Kernel
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h
index a96c55a3e..8b29f0b4a 100644
--- a/src/core/hle/kernel/k_shared_memory.h
+++ b/src/core/hle/kernel/k_shared_memory.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <optional>
6#include <string> 7#include <string>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
@@ -26,9 +27,8 @@ public:
26 ~KSharedMemory() override; 27 ~KSharedMemory() override;
27 28
28 Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, 29 Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
29 KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_, 30 Svc::MemoryPermission owner_permission_,
30 Svc::MemoryPermission user_permission_, PAddr physical_address_, 31 Svc::MemoryPermission user_permission_, std::size_t size_, std::string name_);
31 std::size_t size_, std::string name_);
32 32
33 /** 33 /**
34 * Maps a shared memory block to an address in the target process' address space 34 * Maps a shared memory block to an address in the target process' address space
@@ -76,7 +76,7 @@ public:
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 std::optional<KPageGroup> page_group{};
80 Svc::MemoryPermission owner_permission{}; 80 Svc::MemoryPermission owner_permission{};
81 Svc::MemoryPermission user_permission{}; 81 Svc::MemoryPermission user_permission{};
82 PAddr physical_address{}; 82 PAddr physical_address{};
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index dc52b4ed3..7cd94a340 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -22,6 +22,7 @@
22#include "core/hle/kernel/k_light_lock.h" 22#include "core/hle/kernel/k_light_lock.h"
23#include "core/hle/kernel/k_spin_lock.h" 23#include "core/hle/kernel/k_spin_lock.h"
24#include "core/hle/kernel/k_synchronization_object.h" 24#include "core/hle/kernel/k_synchronization_object.h"
25#include "core/hle/kernel/k_timer_task.h"
25#include "core/hle/kernel/k_worker_task.h" 26#include "core/hle/kernel/k_worker_task.h"
26#include "core/hle/kernel/slab_helpers.h" 27#include "core/hle/kernel/slab_helpers.h"
27#include "core/hle/kernel/svc_common.h" 28#include "core/hle/kernel/svc_common.h"
@@ -112,7 +113,8 @@ void SetCurrentThread(KernelCore& kernel, KThread* thread);
112[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); 113[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
113 114
114class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, 115class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
115 public boost::intrusive::list_base_hook<> { 116 public boost::intrusive::list_base_hook<>,
117 public KTimerTask {
116 KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); 118 KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
117 119
118private: 120private:
@@ -840,4 +842,8 @@ private:
840 KernelCore& kernel; 842 KernelCore& kernel;
841}; 843};
842 844
845inline void KTimerTask::OnTimer() {
846 static_cast<KThread*>(this)->OnTimer();
847}
848
843} // namespace Kernel 849} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp
index 9f4e081ba..5f1dc97eb 100644
--- a/src/core/hle/kernel/k_thread_queue.cpp
+++ b/src/core/hle/kernel/k_thread_queue.cpp
@@ -1,9 +1,9 @@
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 "core/hle/kernel/k_hardware_timer.h"
4#include "core/hle/kernel/k_thread_queue.h" 5#include "core/hle/kernel/k_thread_queue.h"
5#include "core/hle/kernel/kernel.h" 6#include "core/hle/kernel/kernel.h"
6#include "core/hle/kernel/time_manager.h"
7 7
8namespace Kernel { 8namespace Kernel {
9 9
@@ -22,7 +22,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) {
22 waiting_thread->ClearWaitQueue(); 22 waiting_thread->ClearWaitQueue();
23 23
24 // Cancel the thread task. 24 // Cancel the thread task.
25 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); 25 kernel.HardwareTimer().CancelTask(waiting_thread);
26} 26}
27 27
28void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { 28void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) {
@@ -37,7 +37,7 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool
37 37
38 // Cancel the thread task. 38 // Cancel the thread task.
39 if (cancel_timer_task) { 39 if (cancel_timer_task) {
40 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); 40 kernel.HardwareTimer().CancelTask(waiting_thread);
41 } 41 }
42} 42}
43 43
diff --git a/src/core/hle/kernel/k_timer_task.h b/src/core/hle/kernel/k_timer_task.h
new file mode 100644
index 000000000..66f0a5a90
--- /dev/null
+++ b/src/core/hle/kernel/k_timer_task.h
@@ -0,0 +1,40 @@
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/intrusive_red_black_tree.h"
7
8namespace Kernel {
9
10class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode<KTimerTask> {
11public:
12 static constexpr int Compare(const KTimerTask& lhs, const KTimerTask& rhs) {
13 if (lhs.GetTime() < rhs.GetTime()) {
14 return -1;
15 } else {
16 return 1;
17 }
18 }
19
20 constexpr explicit KTimerTask() = default;
21
22 constexpr void SetTime(s64 t) {
23 m_time = t;
24 }
25
26 constexpr s64 GetTime() const {
27 return m_time;
28 }
29
30 // NOTE: This is virtual in Nintendo's kernel. Prior to 13.0.0, KWaitObject was also a
31 // TimerTask; this is no longer the case. Since this is now KThread exclusive, we have
32 // devirtualized (see inline declaration for this inside k_thread.h).
33 void OnTimer();
34
35private:
36 // Absolute time in nanoseconds
37 s64 m_time{};
38};
39
40} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 288f97df5..1fb25f221 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -26,6 +26,7 @@
26#include "core/hle/kernel/k_client_port.h" 26#include "core/hle/kernel/k_client_port.h"
27#include "core/hle/kernel/k_dynamic_resource_manager.h" 27#include "core/hle/kernel/k_dynamic_resource_manager.h"
28#include "core/hle/kernel/k_handle_table.h" 28#include "core/hle/kernel/k_handle_table.h"
29#include "core/hle/kernel/k_hardware_timer.h"
29#include "core/hle/kernel/k_memory_layout.h" 30#include "core/hle/kernel/k_memory_layout.h"
30#include "core/hle/kernel/k_memory_manager.h" 31#include "core/hle/kernel/k_memory_manager.h"
31#include "core/hle/kernel/k_page_buffer.h" 32#include "core/hle/kernel/k_page_buffer.h"
@@ -39,7 +40,6 @@
39#include "core/hle/kernel/kernel.h" 40#include "core/hle/kernel/kernel.h"
40#include "core/hle/kernel/physical_core.h" 41#include "core/hle/kernel/physical_core.h"
41#include "core/hle/kernel/service_thread.h" 42#include "core/hle/kernel/service_thread.h"
42#include "core/hle/kernel/time_manager.h"
43#include "core/hle/result.h" 43#include "core/hle/result.h"
44#include "core/hle/service/sm/sm.h" 44#include "core/hle/service/sm/sm.h"
45#include "core/memory.h" 45#include "core/memory.h"
@@ -55,7 +55,7 @@ struct KernelCore::Impl {
55 static constexpr size_t ReservedDynamicPageCount = 64; 55 static constexpr size_t ReservedDynamicPageCount = 64;
56 56
57 explicit Impl(Core::System& system_, KernelCore& kernel_) 57 explicit Impl(Core::System& system_, KernelCore& kernel_)
58 : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"}, 58 : service_threads_manager{1, "ServiceThreadsManager"},
59 service_thread_barrier{2}, system{system_} {} 59 service_thread_barrier{2}, system{system_} {}
60 60
61 void SetMulticore(bool is_multi) { 61 void SetMulticore(bool is_multi) {
@@ -63,6 +63,9 @@ struct KernelCore::Impl {
63 } 63 }
64 64
65 void Initialize(KernelCore& kernel) { 65 void Initialize(KernelCore& kernel) {
66 hardware_timer = std::make_unique<Kernel::KHardwareTimer>(kernel);
67 hardware_timer->Initialize();
68
66 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); 69 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
67 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 70 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
68 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); 71 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
@@ -91,6 +94,7 @@ struct KernelCore::Impl {
91 pt_heap_region.GetSize()); 94 pt_heap_region.GetSize());
92 } 95 }
93 96
97 InitializeHackSharedMemory();
94 RegisterHostThread(nullptr); 98 RegisterHostThread(nullptr);
95 99
96 default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread"); 100 default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread");
@@ -104,12 +108,16 @@ struct KernelCore::Impl {
104 } 108 }
105 109
106 void CloseCurrentProcess() { 110 void CloseCurrentProcess() {
107 (*current_process).Finalize(); 111 KProcess* old_process = current_process.exchange(nullptr);
108 // current_process->Close(); 112 if (old_process == nullptr) {
109 // TODO: The current process should be destroyed based on accurate ref counting after 113 return;
114 }
115
116 // old_process->Close();
117 // TODO: The process should be destroyed based on accurate ref counting after
110 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. 118 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
111 (*current_process).Destroy(); 119 old_process->Finalize();
112 current_process = nullptr; 120 old_process->Destroy();
113 } 121 }
114 122
115 void Shutdown() { 123 void Shutdown() {
@@ -189,6 +197,9 @@ struct KernelCore::Impl {
189 // Ensure that the object list container is finalized and properly shutdown. 197 // Ensure that the object list container is finalized and properly shutdown.
190 global_object_list_container->Finalize(); 198 global_object_list_container->Finalize();
191 global_object_list_container.reset(); 199 global_object_list_container.reset();
200
201 hardware_timer->Finalize();
202 hardware_timer.reset();
192 } 203 }
193 204
194 void CloseServices() { 205 void CloseServices() {
@@ -716,14 +727,14 @@ struct KernelCore::Impl {
716 } 727 }
717 728
718 void InitializeMemoryLayout() { 729 void InitializeMemoryLayout() {
719 const auto system_pool = memory_layout->GetKernelSystemPoolRegionPhysicalExtents();
720
721 // Initialize the memory manager. 730 // Initialize the memory manager.
722 memory_manager = std::make_unique<KMemoryManager>(system); 731 memory_manager = std::make_unique<KMemoryManager>(system);
723 const auto& management_region = memory_layout->GetPoolManagementRegion(); 732 const auto& management_region = memory_layout->GetPoolManagementRegion();
724 ASSERT(management_region.GetEndAddress() != 0); 733 ASSERT(management_region.GetEndAddress() != 0);
725 memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize()); 734 memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize());
735 }
726 736
737 void InitializeHackSharedMemory() {
727 // Setup memory regions for emulated processes 738 // Setup memory regions for emulated processes
728 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel 739 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel
729 constexpr std::size_t hid_size{0x40000}; 740 constexpr std::size_t hid_size{0x40000};
@@ -732,39 +743,23 @@ struct KernelCore::Impl {
732 constexpr std::size_t time_size{0x1000}; 743 constexpr std::size_t time_size{0x1000};
733 constexpr std::size_t hidbus_size{0x1000}; 744 constexpr std::size_t hidbus_size{0x1000};
734 745
735 const PAddr hid_phys_addr{system_pool.GetAddress()};
736 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};
737 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
738 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
739 const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size +
740 time_size};
741
742 hid_shared_mem = KSharedMemory::Create(system.Kernel()); 746 hid_shared_mem = KSharedMemory::Create(system.Kernel());
743 font_shared_mem = KSharedMemory::Create(system.Kernel()); 747 font_shared_mem = KSharedMemory::Create(system.Kernel());
744 irs_shared_mem = KSharedMemory::Create(system.Kernel()); 748 irs_shared_mem = KSharedMemory::Create(system.Kernel());
745 time_shared_mem = KSharedMemory::Create(system.Kernel()); 749 time_shared_mem = KSharedMemory::Create(system.Kernel());
746 hidbus_shared_mem = KSharedMemory::Create(system.Kernel()); 750 hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
747 751
748 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, 752 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
749 {hid_phys_addr, hid_size / PageSize}, 753 Svc::MemoryPermission::Read, hid_size, "HID:SharedMemory");
750 Svc::MemoryPermission::None, Svc::MemoryPermission::Read, 754 font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
751 hid_phys_addr, hid_size, "HID:SharedMemory"); 755 Svc::MemoryPermission::Read, font_size, "Font:SharedMemory");
752 font_shared_mem->Initialize(system.DeviceMemory(), nullptr, 756 irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
753 {font_phys_addr, font_size / PageSize}, 757 Svc::MemoryPermission::Read, irs_size, "IRS:SharedMemory");
754 Svc::MemoryPermission::None, Svc::MemoryPermission::Read, 758 time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
755 font_phys_addr, font_size, "Font:SharedMemory"); 759 Svc::MemoryPermission::Read, time_size, "Time:SharedMemory");
756 irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, 760 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
757 {irs_phys_addr, irs_size / PageSize}, 761 Svc::MemoryPermission::Read, hidbus_size,
758 Svc::MemoryPermission::None, Svc::MemoryPermission::Read, 762 "HidBus:SharedMemory");
759 irs_phys_addr, irs_size, "IRS:SharedMemory");
760 time_shared_mem->Initialize(system.DeviceMemory(), nullptr,
761 {time_phys_addr, time_size / PageSize},
762 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
763 time_phys_addr, time_size, "Time:SharedMemory");
764 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr,
765 {hidbus_phys_addr, hidbus_size / PageSize},
766 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
767 hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory");
768 } 763 }
769 764
770 KClientPort* CreateNamedServicePort(std::string name) { 765 KClientPort* CreateNamedServicePort(std::string name) {
@@ -828,7 +823,7 @@ struct KernelCore::Impl {
828 std::vector<KProcess*> process_list; 823 std::vector<KProcess*> process_list;
829 std::atomic<KProcess*> current_process{}; 824 std::atomic<KProcess*> current_process{};
830 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 825 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
831 Kernel::TimeManager time_manager; 826 std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;
832 827
833 Init::KSlabResourceCounts slab_resource_counts{}; 828 Init::KSlabResourceCounts slab_resource_counts{};
834 KResourceLimit* system_resource_limit{}; 829 KResourceLimit* system_resource_limit{};
@@ -1015,12 +1010,8 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {
1015 return impl->schedulers[core_id].get(); 1010 return impl->schedulers[core_id].get();
1016} 1011}
1017 1012
1018Kernel::TimeManager& KernelCore::TimeManager() { 1013Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
1019 return impl->time_manager; 1014 return *impl->hardware_timer;
1020}
1021
1022const Kernel::TimeManager& KernelCore::TimeManager() const {
1023 return impl->time_manager;
1024} 1015}
1025 1016
1026Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { 1017Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 2e22fe0f6..8d22f8d2c 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -39,6 +39,7 @@ class KDynamicPageManager;
39class KEvent; 39class KEvent;
40class KEventInfo; 40class KEventInfo;
41class KHandleTable; 41class KHandleTable;
42class KHardwareTimer;
42class KLinkedListNode; 43class KLinkedListNode;
43class KMemoryLayout; 44class KMemoryLayout;
44class KMemoryManager; 45class KMemoryManager;
@@ -63,7 +64,6 @@ class KCodeMemory;
63class PhysicalCore; 64class PhysicalCore;
64class ServiceThread; 65class ServiceThread;
65class Synchronization; 66class Synchronization;
66class TimeManager;
67 67
68using ServiceInterfaceFactory = 68using ServiceInterfaceFactory =
69 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; 69 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
@@ -175,11 +175,8 @@ public:
175 /// Gets the an instance of the current physical CPU core. 175 /// Gets the an instance of the current physical CPU core.
176 const Kernel::PhysicalCore& CurrentPhysicalCore() const; 176 const Kernel::PhysicalCore& CurrentPhysicalCore() const;
177 177
178 /// Gets the an instance of the TimeManager Interface. 178 /// Gets the an instance of the hardware timer.
179 Kernel::TimeManager& TimeManager(); 179 Kernel::KHardwareTimer& HardwareTimer();
180
181 /// Gets the an instance of the TimeManager Interface.
182 const Kernel::TimeManager& TimeManager() const;
183 180
184 /// Stops execution of 'id' core, in order to reschedule a new thread. 181 /// Stops execution of 'id' core, in order to reschedule a new thread.
185 void PrepareReschedule(std::size_t id); 182 void PrepareReschedule(std::size_t id);
diff --git a/src/core/hle/kernel/memory_types.h b/src/core/hle/kernel/memory_types.h
index 3975507bd..92b8b37ac 100644
--- a/src/core/hle/kernel/memory_types.h
+++ b/src/core/hle/kernel/memory_types.h
@@ -14,4 +14,7 @@ constexpr std::size_t PageSize{1 << PageBits};
14 14
15using Page = std::array<u8, PageSize>; 15using Page = std::array<u8, PageSize>;
16 16
17using KPhysicalAddress = PAddr;
18using KProcessAddress = VAddr;
19
17} // namespace Kernel 20} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index e520cab47..aca442196 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -784,63 +784,29 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
784 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 784 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
785 info_sub_id, handle); 785 info_sub_id, handle);
786 786
787 enum class GetInfoType : u64 { 787 const auto info_id_type = static_cast<InfoType>(info_id);
788 // 1.0.0+
789 AllowedCPUCoreMask = 0,
790 AllowedThreadPriorityMask = 1,
791 MapRegionBaseAddr = 2,
792 MapRegionSize = 3,
793 HeapRegionBaseAddr = 4,
794 HeapRegionSize = 5,
795 TotalPhysicalMemoryAvailable = 6,
796 TotalPhysicalMemoryUsed = 7,
797 IsCurrentProcessBeingDebugged = 8,
798 RegisterResourceLimit = 9,
799 IdleTickCount = 10,
800 RandomEntropy = 11,
801 ThreadTickCount = 0xF0000002,
802 // 2.0.0+
803 ASLRRegionBaseAddr = 12,
804 ASLRRegionSize = 13,
805 StackRegionBaseAddr = 14,
806 StackRegionSize = 15,
807 // 3.0.0+
808 SystemResourceSize = 16,
809 SystemResourceUsage = 17,
810 TitleId = 18,
811 // 4.0.0+
812 PrivilegedProcessId = 19,
813 // 5.0.0+
814 UserExceptionContextAddr = 20,
815 // 6.0.0+
816 TotalPhysicalMemoryAvailableWithoutSystemResource = 21,
817 TotalPhysicalMemoryUsedWithoutSystemResource = 22,
818
819 // Homebrew only
820 MesosphereCurrentProcess = 65001,
821 };
822
823 const auto info_id_type = static_cast<GetInfoType>(info_id);
824 788
825 switch (info_id_type) { 789 switch (info_id_type) {
826 case GetInfoType::AllowedCPUCoreMask: 790 case InfoType::CoreMask:
827 case GetInfoType::AllowedThreadPriorityMask: 791 case InfoType::PriorityMask:
828 case GetInfoType::MapRegionBaseAddr: 792 case InfoType::AliasRegionAddress:
829 case GetInfoType::MapRegionSize: 793 case InfoType::AliasRegionSize:
830 case GetInfoType::HeapRegionBaseAddr: 794 case InfoType::HeapRegionAddress:
831 case GetInfoType::HeapRegionSize: 795 case InfoType::HeapRegionSize:
832 case GetInfoType::ASLRRegionBaseAddr: 796 case InfoType::AslrRegionAddress:
833 case GetInfoType::ASLRRegionSize: 797 case InfoType::AslrRegionSize:
834 case GetInfoType::StackRegionBaseAddr: 798 case InfoType::StackRegionAddress:
835 case GetInfoType::StackRegionSize: 799 case InfoType::StackRegionSize:
836 case GetInfoType::TotalPhysicalMemoryAvailable: 800 case InfoType::TotalMemorySize:
837 case GetInfoType::TotalPhysicalMemoryUsed: 801 case InfoType::UsedMemorySize:
838 case GetInfoType::SystemResourceSize: 802 case InfoType::SystemResourceSizeTotal:
839 case GetInfoType::SystemResourceUsage: 803 case InfoType::SystemResourceSizeUsed:
840 case GetInfoType::TitleId: 804 case InfoType::ProgramId:
841 case GetInfoType::UserExceptionContextAddr: 805 case InfoType::UserExceptionContextAddress:
842 case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: 806 case InfoType::TotalNonSystemMemorySize:
843 case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: { 807 case InfoType::UsedNonSystemMemorySize:
808 case InfoType::IsApplication:
809 case InfoType::FreeThreadCount: {
844 if (info_sub_id != 0) { 810 if (info_sub_id != 0) {
845 LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, 811 LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
846 info_sub_id); 812 info_sub_id);
@@ -856,79 +822,83 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
856 } 822 }
857 823
858 switch (info_id_type) { 824 switch (info_id_type) {
859 case GetInfoType::AllowedCPUCoreMask: 825 case InfoType::CoreMask:
860 *result = process->GetCoreMask(); 826 *result = process->GetCoreMask();
861 return ResultSuccess; 827 return ResultSuccess;
862 828
863 case GetInfoType::AllowedThreadPriorityMask: 829 case InfoType::PriorityMask:
864 *result = process->GetPriorityMask(); 830 *result = process->GetPriorityMask();
865 return ResultSuccess; 831 return ResultSuccess;
866 832
867 case GetInfoType::MapRegionBaseAddr: 833 case InfoType::AliasRegionAddress:
868 *result = process->PageTable().GetAliasRegionStart(); 834 *result = process->PageTable().GetAliasRegionStart();
869 return ResultSuccess; 835 return ResultSuccess;
870 836
871 case GetInfoType::MapRegionSize: 837 case InfoType::AliasRegionSize:
872 *result = process->PageTable().GetAliasRegionSize(); 838 *result = process->PageTable().GetAliasRegionSize();
873 return ResultSuccess; 839 return ResultSuccess;
874 840
875 case GetInfoType::HeapRegionBaseAddr: 841 case InfoType::HeapRegionAddress:
876 *result = process->PageTable().GetHeapRegionStart(); 842 *result = process->PageTable().GetHeapRegionStart();
877 return ResultSuccess; 843 return ResultSuccess;
878 844
879 case GetInfoType::HeapRegionSize: 845 case InfoType::HeapRegionSize:
880 *result = process->PageTable().GetHeapRegionSize(); 846 *result = process->PageTable().GetHeapRegionSize();
881 return ResultSuccess; 847 return ResultSuccess;
882 848
883 case GetInfoType::ASLRRegionBaseAddr: 849 case InfoType::AslrRegionAddress:
884 *result = process->PageTable().GetAliasCodeRegionStart(); 850 *result = process->PageTable().GetAliasCodeRegionStart();
885 return ResultSuccess; 851 return ResultSuccess;
886 852
887 case GetInfoType::ASLRRegionSize: 853 case InfoType::AslrRegionSize:
888 *result = process->PageTable().GetAliasCodeRegionSize(); 854 *result = process->PageTable().GetAliasCodeRegionSize();
889 return ResultSuccess; 855 return ResultSuccess;
890 856
891 case GetInfoType::StackRegionBaseAddr: 857 case InfoType::StackRegionAddress:
892 *result = process->PageTable().GetStackRegionStart(); 858 *result = process->PageTable().GetStackRegionStart();
893 return ResultSuccess; 859 return ResultSuccess;
894 860
895 case GetInfoType::StackRegionSize: 861 case InfoType::StackRegionSize:
896 *result = process->PageTable().GetStackRegionSize(); 862 *result = process->PageTable().GetStackRegionSize();
897 return ResultSuccess; 863 return ResultSuccess;
898 864
899 case GetInfoType::TotalPhysicalMemoryAvailable: 865 case InfoType::TotalMemorySize:
900 *result = process->GetTotalPhysicalMemoryAvailable(); 866 *result = process->GetTotalPhysicalMemoryAvailable();
901 return ResultSuccess; 867 return ResultSuccess;
902 868
903 case GetInfoType::TotalPhysicalMemoryUsed: 869 case InfoType::UsedMemorySize:
904 *result = process->GetTotalPhysicalMemoryUsed(); 870 *result = process->GetTotalPhysicalMemoryUsed();
905 return ResultSuccess; 871 return ResultSuccess;
906 872
907 case GetInfoType::SystemResourceSize: 873 case InfoType::SystemResourceSizeTotal:
908 *result = process->GetSystemResourceSize(); 874 *result = process->GetSystemResourceSize();
909 return ResultSuccess; 875 return ResultSuccess;
910 876
911 case GetInfoType::SystemResourceUsage: 877 case InfoType::SystemResourceSizeUsed:
912 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); 878 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
913 *result = process->GetSystemResourceUsage(); 879 *result = process->GetSystemResourceUsage();
914 return ResultSuccess; 880 return ResultSuccess;
915 881
916 case GetInfoType::TitleId: 882 case InfoType::ProgramId:
917 *result = process->GetProgramID(); 883 *result = process->GetProgramID();
918 return ResultSuccess; 884 return ResultSuccess;
919 885
920 case GetInfoType::UserExceptionContextAddr: 886 case InfoType::UserExceptionContextAddress:
921 *result = process->GetProcessLocalRegionAddress(); 887 *result = process->GetProcessLocalRegionAddress();
922 return ResultSuccess; 888 return ResultSuccess;
923 889
924 case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: 890 case InfoType::TotalNonSystemMemorySize:
925 *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); 891 *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
926 return ResultSuccess; 892 return ResultSuccess;
927 893
928 case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: 894 case InfoType::UsedNonSystemMemorySize:
929 *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); 895 *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
930 return ResultSuccess; 896 return ResultSuccess;
931 897
898 case InfoType::FreeThreadCount:
899 *result = process->GetFreeThreadCount();
900 return ResultSuccess;
901
932 default: 902 default:
933 break; 903 break;
934 } 904 }
@@ -937,11 +907,11 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
937 return ResultInvalidEnumValue; 907 return ResultInvalidEnumValue;
938 } 908 }
939 909
940 case GetInfoType::IsCurrentProcessBeingDebugged: 910 case InfoType::DebuggerAttached:
941 *result = 0; 911 *result = 0;
942 return ResultSuccess; 912 return ResultSuccess;
943 913
944 case GetInfoType::RegisterResourceLimit: { 914 case InfoType::ResourceLimit: {
945 if (handle != 0) { 915 if (handle != 0) {
946 LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); 916 LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
947 return ResultInvalidHandle; 917 return ResultInvalidHandle;
@@ -969,7 +939,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
969 return ResultSuccess; 939 return ResultSuccess;
970 } 940 }
971 941
972 case GetInfoType::RandomEntropy: 942 case InfoType::RandomEntropy:
973 if (handle != 0) { 943 if (handle != 0) {
974 LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", 944 LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
975 handle); 945 handle);
@@ -985,13 +955,13 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
985 *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); 955 *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
986 return ResultSuccess; 956 return ResultSuccess;
987 957
988 case GetInfoType::PrivilegedProcessId: 958 case InfoType::InitialProcessIdRange:
989 LOG_WARNING(Kernel_SVC, 959 LOG_WARNING(Kernel_SVC,
990 "(STUBBED) Attempted to query privileged process id bounds, returned 0"); 960 "(STUBBED) Attempted to query privileged process id bounds, returned 0");
991 *result = 0; 961 *result = 0;
992 return ResultSuccess; 962 return ResultSuccess;
993 963
994 case GetInfoType::ThreadTickCount: { 964 case InfoType::ThreadTickCount: {
995 constexpr u64 num_cpus = 4; 965 constexpr u64 num_cpus = 4;
996 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { 966 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
997 LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, 967 LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
@@ -1026,7 +996,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
1026 *result = out_ticks; 996 *result = out_ticks;
1027 return ResultSuccess; 997 return ResultSuccess;
1028 } 998 }
1029 case GetInfoType::IdleTickCount: { 999 case InfoType::IdleTickCount: {
1030 // Verify the input handle is invalid. 1000 // Verify the input handle is invalid.
1031 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); 1001 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
1032 1002
@@ -1040,7 +1010,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
1040 *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); 1010 *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
1041 return ResultSuccess; 1011 return ResultSuccess;
1042 } 1012 }
1043 case GetInfoType::MesosphereCurrentProcess: { 1013 case InfoType::MesosphereCurrentProcess: {
1044 // Verify the input handle is invalid. 1014 // Verify the input handle is invalid.
1045 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); 1015 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
1046 1016
@@ -1515,7 +1485,7 @@ static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle p
1515 ResultInvalidMemoryRegion); 1485 ResultInvalidMemoryRegion);
1516 1486
1517 // Create a new page group. 1487 // Create a new page group.
1518 KPageGroup pg; 1488 KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()};
1519 R_TRY(src_pt.MakeAndOpenPageGroup( 1489 R_TRY(src_pt.MakeAndOpenPageGroup(
1520 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, 1490 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1521 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, 1491 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
deleted file mode 100644
index 5ee72c432..000000000
--- a/src/core/hle/kernel/time_manager.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/k_thread.h"
9#include "core/hle/kernel/time_manager.h"
10
11namespace Kernel {
12
13TimeManager::TimeManager(Core::System& system_) : system{system_} {
14 time_manager_event_type = Core::Timing::CreateEvent(
15 "Kernel::TimeManagerCallback",
16 [this](std::uintptr_t thread_handle, s64 time,
17 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
18 KThread* thread = reinterpret_cast<KThread*>(thread_handle);
19 {
20 KScopedSchedulerLock sl(system.Kernel());
21 thread->OnTimer();
22 }
23 return std::nullopt;
24 });
25}
26
27void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
28 std::scoped_lock lock{mutex};
29 if (nanoseconds > 0) {
30 ASSERT(thread);
31 ASSERT(thread->GetState() != ThreadState::Runnable);
32 system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
33 time_manager_event_type,
34 reinterpret_cast<uintptr_t>(thread));
35 }
36}
37
38void TimeManager::UnscheduleTimeEvent(KThread* thread) {
39 std::scoped_lock lock{mutex};
40 system.CoreTiming().UnscheduleEvent(time_manager_event_type,
41 reinterpret_cast<uintptr_t>(thread));
42}
43
44} // namespace Kernel
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h
deleted file mode 100644
index 94d16b3b4..000000000
--- a/src/core/hle/kernel/time_manager.h
+++ /dev/null
@@ -1,41 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8
9namespace Core {
10class System;
11} // namespace Core
12
13namespace Core::Timing {
14struct EventType;
15} // namespace Core::Timing
16
17namespace Kernel {
18
19class KThread;
20
21/**
22 * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
23 * method when the event is triggered.
24 */
25class TimeManager {
26public:
27 explicit TimeManager(Core::System& system);
28
29 /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
30 void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds);
31
32 /// Unschedule an existing time event
33 void UnscheduleTimeEvent(KThread* thread);
34
35private:
36 Core::System& system;
37 std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
38 std::mutex mutex;
39};
40
41} // namespace Kernel
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 26dec7147..053e8f9dd 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -203,8 +203,9 @@ private:
203}; 203};
204 204
205AudInU::AudInU(Core::System& system_) 205AudInU::AudInU(Core::System& system_)
206 : ServiceFramework{system_, "audin:u"}, service_context{system_, "AudInU"}, 206 : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew},
207 impl{std::make_unique<AudioCore::AudioIn::Manager>(system_)} { 207 service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
208 system_)} {
208 // clang-format off 209 // clang-format off
209 static const FunctionInfo functions[] = { 210 static const FunctionInfo functions[] = {
210 {0, &AudInU::ListAudioIns, "ListAudioIns"}, 211 {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 991e30ba1..29751f075 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -26,8 +26,9 @@ 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"}, service_context{system_, "IAudioOut"}, 29 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
30 event{service_context.CreateEvent("AudioOutEvent")}, 30 service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
31 "AudioOutEvent")},
31 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { 32 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
32 33
33 // clang-format off 34 // clang-format off
@@ -220,8 +221,9 @@ private:
220}; 221};
221 222
222AudOutU::AudOutU(Core::System& system_) 223AudOutU::AudOutU(Core::System& system_)
223 : ServiceFramework{system_, "audout:u"}, service_context{system_, "AudOutU"}, 224 : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew},
224 impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} { 225 service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(
226 system_)} {
225 // clang-format off 227 // clang-format off
226 static const FunctionInfo functions[] = { 228 static const FunctionInfo functions[] = {
227 {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, 229 {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 ead16c321..3a1c231b6 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -35,9 +35,10 @@ public:
35 AudioCore::AudioRendererParameterInternal& params, 35 AudioCore::AudioRendererParameterInternal& params,
36 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, 36 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
37 u32 process_handle, u64 applet_resource_user_id, s32 session_id) 37 u32 process_handle, u64 applet_resource_user_id, s32 session_id)
38 : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, 38 : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
39 rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, 39 service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent(
40 impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { 40 "IAudioRendererEvent")},
41 manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
41 // clang-format off 42 // clang-format off
42 static const FunctionInfo functions[] = { 43 static const FunctionInfo functions[] = {
43 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, 44 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
@@ -242,8 +243,10 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> {
242public: 243public:
243 explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, 244 explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
244 u32 device_num) 245 u32 device_num)
245 : ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"}, 246 : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew},
246 impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)}, 247 service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>(
248 system_, applet_resource_user_id,
249 revision)},
247 event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { 250 event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
248 static const FunctionInfo functions[] = { 251 static const FunctionInfo functions[] = {
249 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, 252 {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
@@ -418,7 +421,7 @@ private:
418}; 421};
419 422
420AudRenU::AudRenU(Core::System& system_) 423AudRenU::AudRenU(Core::System& system_)
421 : ServiceFramework{system_, "audren:u"}, 424 : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew},
422 service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { 425 service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
423 // clang-format off 426 // clang-format off
424 static const FunctionInfo functions[] = { 427 static const FunctionInfo functions[] = {
diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp
index 4615697e2..89aa6b3f5 100644
--- a/src/core/hle/service/nfc/nfc_user.cpp
+++ b/src/core/hle/service/nfc/nfc_user.cpp
@@ -97,7 +97,7 @@ void IUser::IsNfcEnabled(Kernel::HLERequestContext& ctx) {
97} 97}
98 98
99void IUser::ListDevices(Kernel::HLERequestContext& ctx) { 99void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
100 LOG_INFO(Service_NFC, "called"); 100 LOG_DEBUG(Service_NFC, "called");
101 101
102 if (state == State::NonInitialized) { 102 if (state == State::NonInitialized) {
103 IPC::ResponseBuilder rb{ctx, 2}; 103 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 49816b4c7..a4d3d1bc7 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -83,7 +83,7 @@ void IUser::Finalize(Kernel::HLERequestContext& ctx) {
83} 83}
84 84
85void IUser::ListDevices(Kernel::HLERequestContext& ctx) { 85void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
86 LOG_INFO(Service_NFP, "called"); 86 LOG_DEBUG(Service_NFP, "called");
87 87
88 if (state == State::NonInitialized) { 88 if (state == State::NonInitialized) {
89 IPC::ResponseBuilder rb{ctx, 2}; 89 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 4f1a8d6b7..16c5eaf75 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -191,6 +191,13 @@ void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) {
191 GetKeyCodeMapImpl(ctx); 191 GetKeyCodeMapImpl(ctx);
192} 192}
193 193
194void SET::GetDeviceNickName(Kernel::HLERequestContext& ctx) {
195 LOG_DEBUG(Service_SET, "called");
196 IPC::ResponseBuilder rb{ctx, 2};
197 rb.Push(ResultSuccess);
198 ctx.WriteBuffer(Settings::values.device_name.GetValue());
199}
200
194SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { 201SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} {
195 // clang-format off 202 // clang-format off
196 static const FunctionInfo functions[] = { 203 static const FunctionInfo functions[] = {
@@ -205,7 +212,7 @@ SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} {
205 {8, &SET::GetQuestFlag, "GetQuestFlag"}, 212 {8, &SET::GetQuestFlag, "GetQuestFlag"},
206 {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, 213 {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
207 {10, nullptr, "GetFirmwareVersionForDebug"}, 214 {10, nullptr, "GetFirmwareVersionForDebug"},
208 {11, nullptr, "GetDeviceNickName"}, 215 {11, &SET::GetDeviceNickName, "GetDeviceNickName"},
209 }; 216 };
210 // clang-format on 217 // clang-format on
211 218
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index 60cad3e6f..375975711 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -50,6 +50,7 @@ private:
50 void GetRegionCode(Kernel::HLERequestContext& ctx); 50 void GetRegionCode(Kernel::HLERequestContext& ctx);
51 void GetKeyCodeMap(Kernel::HLERequestContext& ctx); 51 void GetKeyCodeMap(Kernel::HLERequestContext& ctx);
52 void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); 52 void GetKeyCodeMap2(Kernel::HLERequestContext& ctx);
53 void GetDeviceNickName(Kernel::HLERequestContext& ctx);
53}; 54};
54 55
55} // namespace Service::Set 56} // namespace Service::Set
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index d7cea6aac..94c20edda 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -3,6 +3,7 @@
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/settings.h"
6#include "core/file_sys/errors.h" 7#include "core/file_sys/errors.h"
7#include "core/file_sys/system_archive/system_version.h" 8#include "core/file_sys/system_archive/system_version.h"
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
@@ -176,6 +177,13 @@ void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) {
176 rb.Push(response); 177 rb.Push(response);
177} 178}
178 179
180void SET_SYS::GetDeviceNickName(Kernel::HLERequestContext& ctx) {
181 LOG_DEBUG(Service_SET, "called");
182 IPC::ResponseBuilder rb{ctx, 2};
183 rb.Push(ResultSuccess);
184 ctx.WriteBuffer(::Settings::values.device_name.GetValue());
185}
186
179SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { 187SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
180 // clang-format off 188 // clang-format off
181 static const FunctionInfo functions[] = { 189 static const FunctionInfo functions[] = {
@@ -253,7 +261,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
253 {74, nullptr, "SetWirelessLanEnableFlag"}, 261 {74, nullptr, "SetWirelessLanEnableFlag"},
254 {75, nullptr, "GetInitialLaunchSettings"}, 262 {75, nullptr, "GetInitialLaunchSettings"},
255 {76, nullptr, "SetInitialLaunchSettings"}, 263 {76, nullptr, "SetInitialLaunchSettings"},
256 {77, nullptr, "GetDeviceNickName"}, 264 {77, &SET_SYS::GetDeviceNickName, "GetDeviceNickName"},
257 {78, nullptr, "SetDeviceNickName"}, 265 {78, nullptr, "SetDeviceNickName"},
258 {79, nullptr, "GetProductModel"}, 266 {79, nullptr, "GetProductModel"},
259 {80, nullptr, "GetLdnChannel"}, 267 {80, nullptr, "GetLdnChannel"},
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index 258ef8c57..464ac3da1 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -29,6 +29,7 @@ private:
29 void GetFirmwareVersion2(Kernel::HLERequestContext& ctx); 29 void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
30 void GetColorSetId(Kernel::HLERequestContext& ctx); 30 void GetColorSetId(Kernel::HLERequestContext& ctx);
31 void SetColorSetId(Kernel::HLERequestContext& ctx); 31 void SetColorSetId(Kernel::HLERequestContext& ctx);
32 void GetDeviceNickName(Kernel::HLERequestContext& ctx);
32 33
33 ColorSet color_set = ColorSet::BasicWhite; 34 ColorSet color_set = ColorSet::BasicWhite;
34}; 35};
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index ef070f32f..ed1eb5b2d 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -49,6 +49,7 @@ struct SteadyClockContext {
49static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size"); 49static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
50static_assert(std::is_trivially_copyable_v<SteadyClockContext>, 50static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
51 "SteadyClockContext must be trivially copyable"); 51 "SteadyClockContext must be trivially copyable");
52using StandardSteadyClockTimePointType = SteadyClockContext;
52 53
53struct SystemClockContext { 54struct SystemClockContext {
54 s64 offset; 55 s64 offset;
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index a3aa0e77f..ff53a7d6f 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -26,23 +26,24 @@ void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
26 const Clock::SteadyClockContext context{ 26 const Clock::SteadyClockContext context{
27 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), 27 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
28 clock_source_id}; 28 clock_source_id};
29 shared_memory_format.standard_steady_clock_timepoint.StoreData( 29 StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context);
30 system.Kernel().GetTimeSharedMem().GetPointer(), context);
31} 30}
32 31
33void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) { 32void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
34 shared_memory_format.standard_local_system_clock_context.StoreData( 33 StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context);
35 system.Kernel().GetTimeSharedMem().GetPointer(), context);
36} 34}
37 35
38void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) { 36void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
39 shared_memory_format.standard_network_system_clock_context.StoreData( 37 StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context);
40 system.Kernel().GetTimeSharedMem().GetPointer(), context);
41} 38}
42 39
43void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) { 40void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
44 shared_memory_format.standard_user_system_clock_automatic_correction.StoreData( 41 StoreToLockFreeAtomicType(
45 system.Kernel().GetTimeSharedMem().GetPointer(), is_enabled); 42 &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled);
43}
44
45SharedMemory::Format* SharedMemory::GetFormat() {
46 return reinterpret_cast<SharedMemory::Format*>(system.Kernel().GetTimeSharedMem().GetPointer());
46} 47}
47 48
48} // namespace Service::Time 49} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
index 561685acd..044a4d24e 100644
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -10,45 +10,68 @@
10 10
11namespace Service::Time { 11namespace Service::Time {
12 12
13// Note: this type is not safe for concurrent writes.
14template <typename T>
15struct LockFreeAtomicType {
16 u32 counter_;
17 std::array<T, 2> value_;
18};
19
20template <typename T>
21static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) {
22 // Get the current counter.
23 auto counter = p->counter_;
24
25 // Increment the counter.
26 ++counter;
27
28 // Store the updated value.
29 p->value_[counter % 2] = value;
30
31 // Fence memory.
32 std::atomic_thread_fence(std::memory_order_release);
33
34 // Set the updated counter.
35 p->counter_ = counter;
36}
37
38template <typename T>
39static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) {
40 while (true) {
41 // Get the counter.
42 auto counter = p->counter_;
43
44 // Get the value.
45 auto value = p->value_[counter % 2];
46
47 // Fence memory.
48 std::atomic_thread_fence(std::memory_order_acquire);
49
50 // Check that the counter matches.
51 if (counter == p->counter_) {
52 return value;
53 }
54 }
55}
56
13class SharedMemory final { 57class SharedMemory final {
14public: 58public:
15 explicit SharedMemory(Core::System& system_); 59 explicit SharedMemory(Core::System& system_);
16 ~SharedMemory(); 60 ~SharedMemory();
17 61
18 // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
19 template <typename T, std::size_t Offset>
20 struct MemoryBarrier {
21 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
22 u32_le read_attempt{};
23 std::array<T, 2> data{};
24
25 // These are not actually memory barriers at the moment as we don't have multicore and all
26 // HLE is mutexed. This will need to properly be implemented when we start updating the time
27 // points on threads. As of right now, we'll be updated both values synchronously and just
28 // incrementing the read_attempt to indicate that we waited.
29 void StoreData(u8* shared_memory, T data_to_store) {
30 std::memcpy(this, shared_memory + Offset, sizeof(*this));
31 read_attempt++;
32 data[read_attempt & 1] = data_to_store;
33 std::memcpy(shared_memory + Offset, this, sizeof(*this));
34 }
35
36 // For reading we're just going to read the last stored value. If there was no value stored
37 // it will just end up reading an empty value as intended.
38 T ReadData(u8* shared_memory) {
39 std::memcpy(this, shared_memory + Offset, sizeof(*this));
40 return data[(read_attempt - 1) & 1];
41 }
42 };
43
44 // Shared memory format 62 // Shared memory format
45 struct Format { 63 struct Format {
46 MemoryBarrier<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint; 64 LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint;
47 MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context; 65 LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context;
48 MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context; 66 LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context;
49 MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction; 67 LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled;
50 u32_le format_version; 68 u32 format_version;
51 }; 69 };
70 static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0);
71 static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38);
72 static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80);
73 static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) ==
74 0xc8);
52 static_assert(sizeof(Format) == 0xd8, "Format is an invalid size"); 75 static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
53 76
54 void SetupStandardSteadyClock(const Common::UUID& clock_source_id, 77 void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
@@ -56,10 +79,10 @@ public:
56 void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context); 79 void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
57 void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context); 80 void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
58 void SetAutomaticCorrectionEnabled(bool is_enabled); 81 void SetAutomaticCorrectionEnabled(bool is_enabled);
82 Format* GetFormat();
59 83
60private: 84private:
61 Core::System& system; 85 Core::System& system;
62 Format shared_memory_format{};
63}; 86};
64 87
65} // namespace Service::Time 88} // namespace Service::Time
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 7932aaab0..f24c89b04 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -20,6 +20,8 @@ add_library(input_common STATIC
20 drivers/udp_client.h 20 drivers/udp_client.h
21 drivers/virtual_amiibo.cpp 21 drivers/virtual_amiibo.cpp
22 drivers/virtual_amiibo.h 22 drivers/virtual_amiibo.h
23 drivers/virtual_gamepad.cpp
24 drivers/virtual_gamepad.h
23 helpers/stick_from_buttons.cpp 25 helpers/stick_from_buttons.cpp
24 helpers/stick_from_buttons.h 26 helpers/stick_from_buttons.h
25 helpers/touch_from_buttons.cpp 27 helpers/touch_from_buttons.cpp
diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp
index dceea67e0..fad9177dc 100644
--- a/src/input_common/drivers/camera.cpp
+++ b/src/input_common/drivers/camera.cpp
@@ -17,7 +17,7 @@ Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)
17 PreSetController(identifier); 17 PreSetController(identifier);
18} 18}
19 19
20void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) { 20void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) {
21 const std::size_t desired_width = getImageWidth(); 21 const std::size_t desired_width = getImageWidth();
22 const std::size_t desired_height = getImageHeight(); 22 const std::size_t desired_height = getImageHeight();
23 status.data.resize(desired_width * desired_height); 23 status.data.resize(desired_width * desired_height);
diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h
index b8a7c75e5..ead3e0fde 100644
--- a/src/input_common/drivers/camera.h
+++ b/src/input_common/drivers/camera.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <span>
7
6#include "input_common/input_engine.h" 8#include "input_common/input_engine.h"
7 9
8namespace InputCommon { 10namespace InputCommon {
@@ -15,7 +17,7 @@ class Camera final : public InputEngine {
15public: 17public:
16 explicit Camera(std::string input_engine_); 18 explicit Camera(std::string input_engine_);
17 19
18 void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data); 20 void SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data);
19 21
20 std::size_t getImageWidth() const; 22 std::size_t getImageWidth() const;
21 std::size_t getImageHeight() const; 23 std::size_t getImageHeight() const;
@@ -23,6 +25,7 @@ public:
23 Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, 25 Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
24 Common::Input::CameraFormat camera_format) override; 26 Common::Input::CameraFormat camera_format) override;
25 27
28private:
26 Common::Input::CameraStatus status{}; 29 Common::Input::CameraStatus status{};
27}; 30};
28 31
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 8de86b61e..4818bb744 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -16,6 +16,8 @@ Common::UUID GetGUID(SDL_Joystick* joystick) {
16 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); 16 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
17 std::array<u8, 16> data{}; 17 std::array<u8, 16> data{};
18 std::memcpy(data.data(), guid.data, sizeof(data)); 18 std::memcpy(data.data(), guid.data, sizeof(data));
19 // Clear controller name crc
20 std::memset(data.data() + 2, 0, sizeof(u16));
19 return Common::UUID{data}; 21 return Common::UUID{data};
20} 22}
21} // Anonymous namespace 23} // Anonymous namespace
diff --git a/src/input_common/drivers/virtual_gamepad.cpp b/src/input_common/drivers/virtual_gamepad.cpp
new file mode 100644
index 000000000..7db945aa6
--- /dev/null
+++ b/src/input_common/drivers/virtual_gamepad.cpp
@@ -0,0 +1,78 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "input_common/drivers/virtual_gamepad.h"
5
6namespace InputCommon {
7constexpr std::size_t PlayerIndexCount = 10;
8
9VirtualGamepad::VirtualGamepad(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
10 for (std::size_t i = 0; i < PlayerIndexCount; i++) {
11 PreSetController(GetIdentifier(i));
12 }
13}
14
15void VirtualGamepad::SetButtonState(std::size_t player_index, int button_id, bool value) {
16 if (player_index > PlayerIndexCount) {
17 return;
18 }
19 const auto identifier = GetIdentifier(player_index);
20 SetButton(identifier, button_id, value);
21}
22
23void VirtualGamepad::SetButtonState(std::size_t player_index, VirtualButton button_id, bool value) {
24 SetButtonState(player_index, static_cast<int>(button_id), value);
25}
26
27void VirtualGamepad::SetStickPosition(std::size_t player_index, int axis_id, float x_value,
28 float y_value) {
29 if (player_index > PlayerIndexCount) {
30 return;
31 }
32 const auto identifier = GetIdentifier(player_index);
33 SetAxis(identifier, axis_id * 2, x_value);
34 SetAxis(identifier, (axis_id * 2) + 1, y_value);
35}
36
37void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value,
38 float y_value) {
39 SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value);
40}
41
42void VirtualGamepad::ResetControllers() {
43 for (std::size_t i = 0; i < PlayerIndexCount; i++) {
44 SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f);
45 SetStickPosition(i, VirtualStick::Right, 0.0f, 0.0f);
46
47 SetButtonState(i, VirtualButton::ButtonA, false);
48 SetButtonState(i, VirtualButton::ButtonB, false);
49 SetButtonState(i, VirtualButton::ButtonX, false);
50 SetButtonState(i, VirtualButton::ButtonY, false);
51 SetButtonState(i, VirtualButton::StickL, false);
52 SetButtonState(i, VirtualButton::StickR, false);
53 SetButtonState(i, VirtualButton::TriggerL, false);
54 SetButtonState(i, VirtualButton::TriggerR, false);
55 SetButtonState(i, VirtualButton::TriggerZL, false);
56 SetButtonState(i, VirtualButton::TriggerZR, false);
57 SetButtonState(i, VirtualButton::ButtonPlus, false);
58 SetButtonState(i, VirtualButton::ButtonMinus, false);
59 SetButtonState(i, VirtualButton::ButtonLeft, false);
60 SetButtonState(i, VirtualButton::ButtonUp, false);
61 SetButtonState(i, VirtualButton::ButtonRight, false);
62 SetButtonState(i, VirtualButton::ButtonDown, false);
63 SetButtonState(i, VirtualButton::ButtonSL, false);
64 SetButtonState(i, VirtualButton::ButtonSR, false);
65 SetButtonState(i, VirtualButton::ButtonHome, false);
66 SetButtonState(i, VirtualButton::ButtonCapture, false);
67 }
68}
69
70PadIdentifier VirtualGamepad::GetIdentifier(std::size_t player_index) const {
71 return {
72 .guid = Common::UUID{},
73 .port = player_index,
74 .pad = 0,
75 };
76}
77
78} // namespace InputCommon
diff --git a/src/input_common/drivers/virtual_gamepad.h b/src/input_common/drivers/virtual_gamepad.h
new file mode 100644
index 000000000..3df91cc6f
--- /dev/null
+++ b/src/input_common/drivers/virtual_gamepad.h
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "input_common/input_engine.h"
7
8namespace InputCommon {
9
10/**
11 * A virtual controller that is always assigned to the game input
12 */
13class VirtualGamepad final : public InputEngine {
14public:
15 enum class VirtualButton {
16 ButtonA,
17 ButtonB,
18 ButtonX,
19 ButtonY,
20 StickL,
21 StickR,
22 TriggerL,
23 TriggerR,
24 TriggerZL,
25 TriggerZR,
26 ButtonPlus,
27 ButtonMinus,
28 ButtonLeft,
29 ButtonUp,
30 ButtonRight,
31 ButtonDown,
32 ButtonSL,
33 ButtonSR,
34 ButtonHome,
35 ButtonCapture,
36 };
37
38 enum class VirtualStick {
39 Left = 0,
40 Right = 1,
41 };
42
43 explicit VirtualGamepad(std::string input_engine_);
44
45 /**
46 * Sets the status of all buttons bound with the key to pressed
47 * @param player_index the player number that will take this action
48 * @param button_id the id of the button
49 * @param value indicates if the button is pressed or not
50 */
51 void SetButtonState(std::size_t player_index, int button_id, bool value);
52 void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value);
53
54 /**
55 * Sets the status of all buttons bound with the key to released
56 * @param player_index the player number that will take this action
57 * @param axis_id the id of the axis to move
58 * @param x_value the position of the stick in the x axis
59 * @param y_value the position of the stick in the y axis
60 */
61 void SetStickPosition(std::size_t player_index, int axis_id, float x_value, float y_value);
62 void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value,
63 float y_value);
64
65 /// Restores all inputs into the neutral position
66 void ResetControllers();
67
68private:
69 /// Returns the correct identifier corresponding to the player index
70 PadIdentifier GetIdentifier(std::size_t player_index) const;
71};
72
73} // namespace InputCommon
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
index 0fa4b1ddb..edd5287c1 100644
--- a/src/input_common/input_mapping.cpp
+++ b/src/input_common/input_mapping.cpp
@@ -200,12 +200,6 @@ bool MappingFactory::IsDriverValid(const MappingData& data) const {
200 return false; 200 return false;
201 } 201 }
202 // The following drivers don't need to be mapped 202 // The following drivers don't need to be mapped
203 if (data.engine == "tas") {
204 return false;
205 }
206 if (data.engine == "touch") {
207 return false;
208 }
209 if (data.engine == "touch_from_button") { 203 if (data.engine == "touch_from_button") {
210 return false; 204 return false;
211 } 205 }
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 942a13535..86deb4c7c 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -12,6 +12,7 @@
12#include "input_common/drivers/touch_screen.h" 12#include "input_common/drivers/touch_screen.h"
13#include "input_common/drivers/udp_client.h" 13#include "input_common/drivers/udp_client.h"
14#include "input_common/drivers/virtual_amiibo.h" 14#include "input_common/drivers/virtual_amiibo.h"
15#include "input_common/drivers/virtual_gamepad.h"
15#include "input_common/helpers/stick_from_buttons.h" 16#include "input_common/helpers/stick_from_buttons.h"
16#include "input_common/helpers/touch_from_buttons.h" 17#include "input_common/helpers/touch_from_buttons.h"
17#include "input_common/input_engine.h" 18#include "input_common/input_engine.h"
@@ -25,73 +26,33 @@
25namespace InputCommon { 26namespace InputCommon {
26 27
27struct InputSubsystem::Impl { 28struct InputSubsystem::Impl {
28 void Initialize() { 29 template <typename Engine>
29 mapping_factory = std::make_shared<MappingFactory>(); 30 void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) {
30 MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }}; 31 MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }};
31 32
32 keyboard = std::make_shared<Keyboard>("keyboard"); 33 engine = std::make_shared<Engine>(name);
33 keyboard->SetMappingCallback(mapping_callback); 34 engine->SetMappingCallback(mapping_callback);
34 keyboard_factory = std::make_shared<InputFactory>(keyboard); 35
35 keyboard_output_factory = std::make_shared<OutputFactory>(keyboard); 36 std::shared_ptr<InputFactory> input_factory = std::make_shared<InputFactory>(engine);
36 Common::Input::RegisterInputFactory(keyboard->GetEngineName(), keyboard_factory); 37 std::shared_ptr<OutputFactory> output_factory = std::make_shared<OutputFactory>(engine);
37 Common::Input::RegisterOutputFactory(keyboard->GetEngineName(), keyboard_output_factory); 38 Common::Input::RegisterInputFactory(engine->GetEngineName(), std::move(input_factory));
38 39 Common::Input::RegisterOutputFactory(engine->GetEngineName(), std::move(output_factory));
39 mouse = std::make_shared<Mouse>("mouse"); 40 }
40 mouse->SetMappingCallback(mapping_callback); 41
41 mouse_factory = std::make_shared<InputFactory>(mouse); 42 void Initialize() {
42 mouse_output_factory = std::make_shared<OutputFactory>(mouse); 43 mapping_factory = std::make_shared<MappingFactory>();
43 Common::Input::RegisterInputFactory(mouse->GetEngineName(), mouse_factory);
44 Common::Input::RegisterOutputFactory(mouse->GetEngineName(), mouse_output_factory);
45
46 touch_screen = std::make_shared<TouchScreen>("touch");
47 touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
48 Common::Input::RegisterInputFactory(touch_screen->GetEngineName(), touch_screen_factory);
49
50 gcadapter = std::make_shared<GCAdapter>("gcpad");
51 gcadapter->SetMappingCallback(mapping_callback);
52 gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
53 gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
54 Common::Input::RegisterInputFactory(gcadapter->GetEngineName(), gcadapter_input_factory);
55 Common::Input::RegisterOutputFactory(gcadapter->GetEngineName(), gcadapter_output_factory);
56
57 udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
58 udp_client->SetMappingCallback(mapping_callback);
59 udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
60 udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
61 Common::Input::RegisterInputFactory(udp_client->GetEngineName(), udp_client_input_factory);
62 Common::Input::RegisterOutputFactory(udp_client->GetEngineName(),
63 udp_client_output_factory);
64
65 tas_input = std::make_shared<TasInput::Tas>("tas");
66 tas_input->SetMappingCallback(mapping_callback);
67 tas_input_factory = std::make_shared<InputFactory>(tas_input);
68 tas_output_factory = std::make_shared<OutputFactory>(tas_input);
69 Common::Input::RegisterInputFactory(tas_input->GetEngineName(), tas_input_factory);
70 Common::Input::RegisterOutputFactory(tas_input->GetEngineName(), tas_output_factory);
71
72 camera = std::make_shared<Camera>("camera");
73 camera->SetMappingCallback(mapping_callback);
74 camera_input_factory = std::make_shared<InputFactory>(camera);
75 camera_output_factory = std::make_shared<OutputFactory>(camera);
76 Common::Input::RegisterInputFactory(camera->GetEngineName(), camera_input_factory);
77 Common::Input::RegisterOutputFactory(camera->GetEngineName(), camera_output_factory);
78
79 virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo");
80 virtual_amiibo->SetMappingCallback(mapping_callback);
81 virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo);
82 virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo);
83 Common::Input::RegisterInputFactory(virtual_amiibo->GetEngineName(),
84 virtual_amiibo_input_factory);
85 Common::Input::RegisterOutputFactory(virtual_amiibo->GetEngineName(),
86 virtual_amiibo_output_factory);
87 44
45 RegisterEngine("keyboard", keyboard);
46 RegisterEngine("mouse", mouse);
47 RegisterEngine("touch", touch_screen);
48 RegisterEngine("gcpad", gcadapter);
49 RegisterEngine("cemuhookudp", udp_client);
50 RegisterEngine("tas", tas_input);
51 RegisterEngine("camera", camera);
52 RegisterEngine("virtual_amiibo", virtual_amiibo);
53 RegisterEngine("virtual_gamepad", virtual_gamepad);
88#ifdef HAVE_SDL2 54#ifdef HAVE_SDL2
89 sdl = std::make_shared<SDLDriver>("sdl"); 55 RegisterEngine("sdl", sdl);
90 sdl->SetMappingCallback(mapping_callback);
91 sdl_input_factory = std::make_shared<InputFactory>(sdl);
92 sdl_output_factory = std::make_shared<OutputFactory>(sdl);
93 Common::Input::RegisterInputFactory(sdl->GetEngineName(), sdl_input_factory);
94 Common::Input::RegisterOutputFactory(sdl->GetEngineName(), sdl_output_factory);
95#endif 56#endif
96 57
97 Common::Input::RegisterInputFactory("touch_from_button", 58 Common::Input::RegisterInputFactory("touch_from_button",
@@ -100,42 +61,25 @@ struct InputSubsystem::Impl {
100 std::make_shared<StickFromButton>()); 61 std::make_shared<StickFromButton>());
101 } 62 }
102 63
103 void Shutdown() { 64 template <typename Engine>
104 Common::Input::UnregisterInputFactory(keyboard->GetEngineName()); 65 void UnregisterEngine(std::shared_ptr<Engine>& engine) {
105 Common::Input::UnregisterOutputFactory(keyboard->GetEngineName()); 66 Common::Input::UnregisterInputFactory(engine->GetEngineName());
106 keyboard.reset(); 67 Common::Input::UnregisterOutputFactory(engine->GetEngineName());
107 68 engine.reset();
108 Common::Input::UnregisterInputFactory(mouse->GetEngineName()); 69 }
109 Common::Input::UnregisterOutputFactory(mouse->GetEngineName());
110 mouse.reset();
111
112 Common::Input::UnregisterInputFactory(touch_screen->GetEngineName());
113 touch_screen.reset();
114
115 Common::Input::UnregisterInputFactory(gcadapter->GetEngineName());
116 Common::Input::UnregisterOutputFactory(gcadapter->GetEngineName());
117 gcadapter.reset();
118
119 Common::Input::UnregisterInputFactory(udp_client->GetEngineName());
120 Common::Input::UnregisterOutputFactory(udp_client->GetEngineName());
121 udp_client.reset();
122
123 Common::Input::UnregisterInputFactory(tas_input->GetEngineName());
124 Common::Input::UnregisterOutputFactory(tas_input->GetEngineName());
125 tas_input.reset();
126
127 Common::Input::UnregisterInputFactory(camera->GetEngineName());
128 Common::Input::UnregisterOutputFactory(camera->GetEngineName());
129 camera.reset();
130
131 Common::Input::UnregisterInputFactory(virtual_amiibo->GetEngineName());
132 Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName());
133 virtual_amiibo.reset();
134 70
71 void Shutdown() {
72 UnregisterEngine(keyboard);
73 UnregisterEngine(mouse);
74 UnregisterEngine(touch_screen);
75 UnregisterEngine(gcadapter);
76 UnregisterEngine(udp_client);
77 UnregisterEngine(tas_input);
78 UnregisterEngine(camera);
79 UnregisterEngine(virtual_amiibo);
80 UnregisterEngine(virtual_gamepad);
135#ifdef HAVE_SDL2 81#ifdef HAVE_SDL2
136 Common::Input::UnregisterInputFactory(sdl->GetEngineName()); 82 UnregisterEngine(sdl);
137 Common::Input::UnregisterOutputFactory(sdl->GetEngineName());
138 sdl.reset();
139#endif 83#endif
140 84
141 Common::Input::UnregisterInputFactory("touch_from_button"); 85 Common::Input::UnregisterInputFactory("touch_from_button");
@@ -163,117 +107,86 @@ struct InputSubsystem::Impl {
163 return devices; 107 return devices;
164 } 108 }
165 109
166 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice( 110 [[nodiscard]] std::shared_ptr<InputEngine> GetInputEngine(
167 const Common::ParamPackage& params) const { 111 const Common::ParamPackage& params) const {
168 if (!params.Has("engine") || params.Get("engine", "") == "any") { 112 if (!params.Has("engine") || params.Get("engine", "") == "any") {
169 return {}; 113 return nullptr;
170 } 114 }
171 const std::string engine = params.Get("engine", ""); 115 const std::string engine = params.Get("engine", "");
116 if (engine == keyboard->GetEngineName()) {
117 return keyboard;
118 }
172 if (engine == mouse->GetEngineName()) { 119 if (engine == mouse->GetEngineName()) {
173 return mouse->GetAnalogMappingForDevice(params); 120 return mouse;
174 } 121 }
175 if (engine == gcadapter->GetEngineName()) { 122 if (engine == gcadapter->GetEngineName()) {
176 return gcadapter->GetAnalogMappingForDevice(params); 123 return gcadapter;
177 } 124 }
178 if (engine == udp_client->GetEngineName()) { 125 if (engine == udp_client->GetEngineName()) {
179 return udp_client->GetAnalogMappingForDevice(params); 126 return udp_client;
180 }
181 if (engine == tas_input->GetEngineName()) {
182 return tas_input->GetAnalogMappingForDevice(params);
183 } 127 }
184#ifdef HAVE_SDL2 128#ifdef HAVE_SDL2
185 if (engine == sdl->GetEngineName()) { 129 if (engine == sdl->GetEngineName()) {
186 return sdl->GetAnalogMappingForDevice(params); 130 return sdl;
187 } 131 }
188#endif 132#endif
189 return {}; 133 return nullptr;
190 } 134 }
191 135
192 [[nodiscard]] ButtonMapping GetButtonMappingForDevice( 136 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
193 const Common::ParamPackage& params) const { 137 const Common::ParamPackage& params) const {
194 if (!params.Has("engine") || params.Get("engine", "") == "any") { 138 const auto input_engine = GetInputEngine(params);
139
140 if (input_engine == nullptr) {
195 return {}; 141 return {};
196 } 142 }
197 const std::string engine = params.Get("engine", ""); 143
198 if (engine == gcadapter->GetEngineName()) { 144 return input_engine->GetAnalogMappingForDevice(params);
199 return gcadapter->GetButtonMappingForDevice(params); 145 }
200 } 146
201 if (engine == udp_client->GetEngineName()) { 147 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
202 return udp_client->GetButtonMappingForDevice(params); 148 const Common::ParamPackage& params) const {
203 } 149 const auto input_engine = GetInputEngine(params);
204 if (engine == tas_input->GetEngineName()) { 150
205 return tas_input->GetButtonMappingForDevice(params); 151 if (input_engine == nullptr) {
206 } 152 return {};
207#ifdef HAVE_SDL2
208 if (engine == sdl->GetEngineName()) {
209 return sdl->GetButtonMappingForDevice(params);
210 } 153 }
211#endif 154
212 return {}; 155 return input_engine->GetButtonMappingForDevice(params);
213 } 156 }
214 157
215 [[nodiscard]] MotionMapping GetMotionMappingForDevice( 158 [[nodiscard]] MotionMapping GetMotionMappingForDevice(
216 const Common::ParamPackage& params) const { 159 const Common::ParamPackage& params) const {
217 if (!params.Has("engine") || params.Get("engine", "") == "any") { 160 const auto input_engine = GetInputEngine(params);
161
162 if (input_engine == nullptr) {
218 return {}; 163 return {};
219 } 164 }
220 const std::string engine = params.Get("engine", ""); 165
221 if (engine == udp_client->GetEngineName()) { 166 return input_engine->GetMotionMappingForDevice(params);
222 return udp_client->GetMotionMappingForDevice(params);
223 }
224#ifdef HAVE_SDL2
225 if (engine == sdl->GetEngineName()) {
226 return sdl->GetMotionMappingForDevice(params);
227 }
228#endif
229 return {};
230 } 167 }
231 168
232 Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const { 169 Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {
233 if (!params.Has("engine") || params.Get("engine", "") == "any") { 170 if (!params.Has("engine") || params.Get("engine", "") == "any") {
234 return Common::Input::ButtonNames::Undefined; 171 return Common::Input::ButtonNames::Undefined;
235 } 172 }
236 const std::string engine = params.Get("engine", ""); 173 const auto input_engine = GetInputEngine(params);
237 if (engine == mouse->GetEngineName()) { 174
238 return mouse->GetUIName(params); 175 if (input_engine == nullptr) {
239 } 176 return Common::Input::ButtonNames::Invalid;
240 if (engine == gcadapter->GetEngineName()) {
241 return gcadapter->GetUIName(params);
242 }
243 if (engine == udp_client->GetEngineName()) {
244 return udp_client->GetUIName(params);
245 }
246 if (engine == tas_input->GetEngineName()) {
247 return tas_input->GetUIName(params);
248 }
249#ifdef HAVE_SDL2
250 if (engine == sdl->GetEngineName()) {
251 return sdl->GetUIName(params);
252 } 177 }
253#endif 178
254 return Common::Input::ButtonNames::Invalid; 179 return input_engine->GetUIName(params);
255 } 180 }
256 181
257 bool IsStickInverted(const Common::ParamPackage& params) { 182 bool IsStickInverted(const Common::ParamPackage& params) {
258 const std::string engine = params.Get("engine", ""); 183 const auto input_engine = GetInputEngine(params);
259 if (engine == mouse->GetEngineName()) { 184
260 return mouse->IsStickInverted(params); 185 if (input_engine == nullptr) {
261 } 186 return false;
262 if (engine == gcadapter->GetEngineName()) {
263 return gcadapter->IsStickInverted(params);
264 }
265 if (engine == udp_client->GetEngineName()) {
266 return udp_client->IsStickInverted(params);
267 }
268 if (engine == tas_input->GetEngineName()) {
269 return tas_input->IsStickInverted(params);
270 }
271#ifdef HAVE_SDL2
272 if (engine == sdl->GetEngineName()) {
273 return sdl->IsStickInverted(params);
274 } 187 }
275#endif 188
276 return false; 189 return input_engine->IsStickInverted(params);
277 } 190 }
278 191
279 bool IsController(const Common::ParamPackage& params) { 192 bool IsController(const Common::ParamPackage& params) {
@@ -290,6 +203,9 @@ struct InputSubsystem::Impl {
290 if (engine == tas_input->GetEngineName()) { 203 if (engine == tas_input->GetEngineName()) {
291 return true; 204 return true;
292 } 205 }
206 if (engine == virtual_gamepad->GetEngineName()) {
207 return true;
208 }
293#ifdef HAVE_SDL2 209#ifdef HAVE_SDL2
294 if (engine == sdl->GetEngineName()) { 210 if (engine == sdl->GetEngineName()) {
295 return true; 211 return true;
@@ -338,28 +254,10 @@ struct InputSubsystem::Impl {
338 std::shared_ptr<CemuhookUDP::UDPClient> udp_client; 254 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
339 std::shared_ptr<Camera> camera; 255 std::shared_ptr<Camera> camera;
340 std::shared_ptr<VirtualAmiibo> virtual_amiibo; 256 std::shared_ptr<VirtualAmiibo> virtual_amiibo;
341 257 std::shared_ptr<VirtualGamepad> virtual_gamepad;
342 std::shared_ptr<InputFactory> keyboard_factory;
343 std::shared_ptr<InputFactory> mouse_factory;
344 std::shared_ptr<InputFactory> gcadapter_input_factory;
345 std::shared_ptr<InputFactory> touch_screen_factory;
346 std::shared_ptr<InputFactory> udp_client_input_factory;
347 std::shared_ptr<InputFactory> tas_input_factory;
348 std::shared_ptr<InputFactory> camera_input_factory;
349 std::shared_ptr<InputFactory> virtual_amiibo_input_factory;
350
351 std::shared_ptr<OutputFactory> keyboard_output_factory;
352 std::shared_ptr<OutputFactory> mouse_output_factory;
353 std::shared_ptr<OutputFactory> gcadapter_output_factory;
354 std::shared_ptr<OutputFactory> udp_client_output_factory;
355 std::shared_ptr<OutputFactory> tas_output_factory;
356 std::shared_ptr<OutputFactory> camera_output_factory;
357 std::shared_ptr<OutputFactory> virtual_amiibo_output_factory;
358 258
359#ifdef HAVE_SDL2 259#ifdef HAVE_SDL2
360 std::shared_ptr<SDLDriver> sdl; 260 std::shared_ptr<SDLDriver> sdl;
361 std::shared_ptr<InputFactory> sdl_input_factory;
362 std::shared_ptr<OutputFactory> sdl_output_factory;
363#endif 261#endif
364}; 262};
365 263
@@ -423,6 +321,14 @@ const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const {
423 return impl->virtual_amiibo.get(); 321 return impl->virtual_amiibo.get();
424} 322}
425 323
324VirtualGamepad* InputSubsystem::GetVirtualGamepad() {
325 return impl->virtual_gamepad.get();
326}
327
328const VirtualGamepad* InputSubsystem::GetVirtualGamepad() const {
329 return impl->virtual_gamepad.get();
330}
331
426std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { 332std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
427 return impl->GetInputDevices(); 333 return impl->GetInputDevices();
428} 334}
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 6218c37f6..1207d786c 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -34,6 +34,7 @@ class Keyboard;
34class Mouse; 34class Mouse;
35class TouchScreen; 35class TouchScreen;
36class VirtualAmiibo; 36class VirtualAmiibo;
37class VirtualGamepad;
37struct MappingData; 38struct MappingData;
38} // namespace InputCommon 39} // namespace InputCommon
39 40
@@ -108,6 +109,12 @@ public:
108 /// Retrieves the underlying virtual amiibo input device. 109 /// Retrieves the underlying virtual amiibo input device.
109 [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const; 110 [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const;
110 111
112 /// Retrieves the underlying virtual gamepad input device.
113 [[nodiscard]] VirtualGamepad* GetVirtualGamepad();
114
115 /// Retrieves the underlying virtual gamepad input device.
116 [[nodiscard]] const VirtualGamepad* GetVirtualGamepad() const;
117
111 /** 118 /**
112 * Returns all available input devices that this Factory can create a new device with. 119 * Returns all available input devices that this Factory can create a new device with.
113 * Each returned ParamPackage should have a `display` field used for display, a `engine` field 120 * Each returned ParamPackage should have a `display` field used for display, a `engine` field
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 01f6ec9b5..73b67f0af 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -461,7 +461,7 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) {
461} 461}
462 462
463void EmitSetFragDepth(EmitContext& ctx, Id value) { 463void EmitSetFragDepth(EmitContext& ctx, Id value) {
464 if (!ctx.runtime_info.convert_depth_mode) { 464 if (!ctx.runtime_info.convert_depth_mode || ctx.profile.support_native_ndc) {
465 ctx.OpStore(ctx.frag_depth, value); 465 ctx.OpStore(ctx.frag_depth, value);
466 return; 466 return;
467 } 467 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
index 00be1f127..9f7b6bb4b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
@@ -116,7 +116,8 @@ void EmitPrologue(EmitContext& ctx) {
116} 116}
117 117
118void EmitEpilogue(EmitContext& ctx) { 118void EmitEpilogue(EmitContext& ctx) {
119 if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) { 119 if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode &&
120 !ctx.profile.support_native_ndc) {
120 ConvertDepthMode(ctx); 121 ConvertDepthMode(ctx);
121 } 122 }
122 if (ctx.stage == Stage::Fragment) { 123 if (ctx.stage == Stage::Fragment) {
@@ -125,7 +126,7 @@ void EmitEpilogue(EmitContext& ctx) {
125} 126}
126 127
127void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { 128void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
128 if (ctx.runtime_info.convert_depth_mode) { 129 if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) {
129 ConvertDepthMode(ctx); 130 ConvertDepthMode(ctx);
130 } 131 }
131 if (stream.IsImmediate()) { 132 if (stream.IsImmediate()) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 8e3e40cd5..41dc6d031 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -1345,8 +1345,10 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1345 if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || 1345 if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
1346 (profile.warp_size_potentially_larger_than_guest && 1346 (profile.warp_size_potentially_larger_than_guest &&
1347 (info.uses_subgroup_vote || info.uses_subgroup_mask))) { 1347 (info.uses_subgroup_vote || info.uses_subgroup_mask))) {
1348 AddCapability(spv::Capability::GroupNonUniform);
1348 subgroup_local_invocation_id = 1349 subgroup_local_invocation_id =
1349 DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); 1350 DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
1351 Decorate(subgroup_local_invocation_id, spv::Decoration::Flat);
1350 } 1352 }
1351 if (info.uses_fswzadd) { 1353 if (info.uses_fswzadd) {
1352 const Id f32_one{Const(1.0f)}; 1354 const Id f32_one{Const(1.0f)};
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 21d3d236b..b8841a536 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -35,6 +35,7 @@ struct Profile {
35 bool support_int64_atomics{}; 35 bool support_int64_atomics{};
36 bool support_derivative_control{}; 36 bool support_derivative_control{};
37 bool support_geometry_shader_passthrough{}; 37 bool support_geometry_shader_passthrough{};
38 bool support_native_ndc{};
38 bool support_gl_nv_gpu_shader_5{}; 39 bool support_gl_nv_gpu_shader_5{};
39 bool support_gl_amd_gpu_shader_half_float{}; 40 bool support_gl_amd_gpu_shader_half_float{};
40 bool support_gl_texture_shadow_lod{}; 41 bool support_gl_texture_shadow_lod{};
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 348d1edf4..6a4022e45 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -8,6 +8,7 @@ add_executable(tests
8 common/host_memory.cpp 8 common/host_memory.cpp
9 common/param_package.cpp 9 common/param_package.cpp
10 common/ring_buffer.cpp 10 common/ring_buffer.cpp
11 common/scratch_buffer.cpp
11 common/unique_function.cpp 12 common/unique_function.cpp
12 core/core_timing.cpp 13 core/core_timing.cpp
13 core/internal_network/network.cpp 14 core/internal_network/network.cpp
diff --git a/src/tests/common/scratch_buffer.cpp b/src/tests/common/scratch_buffer.cpp
new file mode 100644
index 000000000..f6e50da4a
--- /dev/null
+++ b/src/tests/common/scratch_buffer.cpp
@@ -0,0 +1,200 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <array>
6#include <cstring>
7#include <span>
8#include <catch2/catch.hpp>
9#include "common/common_types.h"
10#include "common/scratch_buffer.h"
11
12namespace Common {
13
14TEST_CASE("ScratchBuffer: Basic Test", "[common]") {
15 ScratchBuffer<u8> buf;
16
17 REQUIRE(buf.size() == 0U);
18 REQUIRE(buf.capacity() == 0U);
19
20 std::array<u8, 10> payload;
21 payload.fill(66);
22
23 buf.resize(payload.size());
24 REQUIRE(buf.size() == payload.size());
25 REQUIRE(buf.capacity() == payload.size());
26
27 std::memcpy(buf.data(), payload.data(), payload.size());
28 for (size_t i = 0; i < payload.size(); ++i) {
29 REQUIRE(buf[i] == payload[i]);
30 }
31}
32
33TEST_CASE("ScratchBuffer: resize_destructive Grow", "[common]") {
34 std::array<u8, 10> payload;
35 payload.fill(66);
36
37 ScratchBuffer<u8> buf(payload.size());
38 REQUIRE(buf.size() == payload.size());
39 REQUIRE(buf.capacity() == payload.size());
40
41 // Increasing the size should reallocate the buffer
42 buf.resize_destructive(payload.size() * 2);
43 REQUIRE(buf.size() == payload.size() * 2);
44 REQUIRE(buf.capacity() == payload.size() * 2);
45
46 // Since the buffer is not value initialized, reading its data will be garbage
47}
48
49TEST_CASE("ScratchBuffer: resize_destructive Shrink", "[common]") {
50 std::array<u8, 10> payload;
51 payload.fill(66);
52
53 ScratchBuffer<u8> buf(payload.size());
54 REQUIRE(buf.size() == payload.size());
55 REQUIRE(buf.capacity() == payload.size());
56
57 std::memcpy(buf.data(), payload.data(), payload.size());
58 for (size_t i = 0; i < payload.size(); ++i) {
59 REQUIRE(buf[i] == payload[i]);
60 }
61
62 // Decreasing the size should not cause a buffer reallocation
63 // This can be tested by ensuring the buffer capacity and data has not changed,
64 buf.resize_destructive(1U);
65 REQUIRE(buf.size() == 1U);
66 REQUIRE(buf.capacity() == payload.size());
67
68 for (size_t i = 0; i < payload.size(); ++i) {
69 REQUIRE(buf[i] == payload[i]);
70 }
71}
72
73TEST_CASE("ScratchBuffer: resize Grow u8", "[common]") {
74 std::array<u8, 10> payload;
75 payload.fill(66);
76
77 ScratchBuffer<u8> buf(payload.size());
78 REQUIRE(buf.size() == payload.size());
79 REQUIRE(buf.capacity() == payload.size());
80
81 std::memcpy(buf.data(), payload.data(), payload.size());
82 for (size_t i = 0; i < payload.size(); ++i) {
83 REQUIRE(buf[i] == payload[i]);
84 }
85
86 // Increasing the size should reallocate the buffer
87 buf.resize(payload.size() * 2);
88 REQUIRE(buf.size() == payload.size() * 2);
89 REQUIRE(buf.capacity() == payload.size() * 2);
90
91 // resize() keeps the previous data intact
92 for (size_t i = 0; i < payload.size(); ++i) {
93 REQUIRE(buf[i] == payload[i]);
94 }
95}
96
97TEST_CASE("ScratchBuffer: resize Grow u64", "[common]") {
98 std::array<u64, 10> payload;
99 payload.fill(6666);
100
101 ScratchBuffer<u64> buf(payload.size());
102 REQUIRE(buf.size() == payload.size());
103 REQUIRE(buf.capacity() == payload.size());
104
105 std::memcpy(buf.data(), payload.data(), payload.size() * sizeof(u64));
106 for (size_t i = 0; i < payload.size(); ++i) {
107 REQUIRE(buf[i] == payload[i]);
108 }
109
110 // Increasing the size should reallocate the buffer
111 buf.resize(payload.size() * 2);
112 REQUIRE(buf.size() == payload.size() * 2);
113 REQUIRE(buf.capacity() == payload.size() * 2);
114
115 // resize() keeps the previous data intact
116 for (size_t i = 0; i < payload.size(); ++i) {
117 REQUIRE(buf[i] == payload[i]);
118 }
119}
120
121TEST_CASE("ScratchBuffer: resize Shrink", "[common]") {
122 std::array<u8, 10> payload;
123 payload.fill(66);
124
125 ScratchBuffer<u8> buf(payload.size());
126 REQUIRE(buf.size() == payload.size());
127 REQUIRE(buf.capacity() == payload.size());
128
129 std::memcpy(buf.data(), payload.data(), payload.size());
130 for (size_t i = 0; i < payload.size(); ++i) {
131 REQUIRE(buf[i] == payload[i]);
132 }
133
134 // Decreasing the size should not cause a buffer reallocation
135 // This can be tested by ensuring the buffer capacity and data has not changed,
136 buf.resize(1U);
137 REQUIRE(buf.size() == 1U);
138 REQUIRE(buf.capacity() == payload.size());
139
140 for (size_t i = 0; i < payload.size(); ++i) {
141 REQUIRE(buf[i] == payload[i]);
142 }
143}
144
145TEST_CASE("ScratchBuffer: Span Size", "[common]") {
146 std::array<u8, 10> payload;
147 payload.fill(66);
148
149 ScratchBuffer<u8> buf(payload.size());
150 REQUIRE(buf.size() == payload.size());
151 REQUIRE(buf.capacity() == payload.size());
152
153 std::memcpy(buf.data(), payload.data(), payload.size());
154 for (size_t i = 0; i < payload.size(); ++i) {
155 REQUIRE(buf[i] == payload[i]);
156 }
157
158 buf.resize(3U);
159 REQUIRE(buf.size() == 3U);
160 REQUIRE(buf.capacity() == payload.size());
161
162 const auto buf_span = std::span<u8>(buf);
163 // The span size is the last requested size of the buffer, not its capacity
164 REQUIRE(buf_span.size() == buf.size());
165
166 for (size_t i = 0; i < buf_span.size(); ++i) {
167 REQUIRE(buf_span[i] == buf[i]);
168 REQUIRE(buf_span[i] == payload[i]);
169 }
170}
171
172TEST_CASE("ScratchBuffer: Span Writes", "[common]") {
173 std::array<u8, 10> payload;
174 payload.fill(66);
175
176 ScratchBuffer<u8> buf(payload.size());
177 REQUIRE(buf.size() == payload.size());
178 REQUIRE(buf.capacity() == payload.size());
179
180 std::memcpy(buf.data(), payload.data(), payload.size());
181 for (size_t i = 0; i < payload.size(); ++i) {
182 REQUIRE(buf[i] == payload[i]);
183 }
184
185 buf.resize(3U);
186 REQUIRE(buf.size() == 3U);
187 REQUIRE(buf.capacity() == payload.size());
188
189 const auto buf_span = std::span<u8>(buf);
190 REQUIRE(buf_span.size() == buf.size());
191
192 for (size_t i = 0; i < buf_span.size(); ++i) {
193 const auto new_value = static_cast<u8>(i + 1U);
194 // Writes to a span of the scratch buffer will propogate to the buffer itself
195 buf_span[i] = new_value;
196 REQUIRE(buf[i] == new_value);
197 }
198}
199
200} // namespace Common
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 158360830..f1c60d1f3 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -20,6 +20,7 @@
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/polyfill_ranges.h"
23#include "common/scratch_buffer.h"
23#include "common/settings.h" 24#include "common/settings.h"
24#include "core/memory.h" 25#include "core/memory.h"
25#include "video_core/buffer_cache/buffer_base.h" 26#include "video_core/buffer_cache/buffer_base.h"
@@ -422,8 +423,7 @@ private:
422 IntervalSet common_ranges; 423 IntervalSet common_ranges;
423 std::deque<IntervalSet> committed_ranges; 424 std::deque<IntervalSet> committed_ranges;
424 425
425 size_t immediate_buffer_capacity = 0; 426 Common::ScratchBuffer<u8> immediate_buffer_alloc;
426 std::unique_ptr<u8[]> immediate_buffer_alloc;
427 427
428 struct LRUItemParams { 428 struct LRUItemParams {
429 using ObjectType = BufferId; 429 using ObjectType = BufferId;
@@ -1927,11 +1927,8 @@ std::span<const u8> BufferCache<P>::ImmediateBufferWithData(VAddr cpu_addr, size
1927 1927
1928template <class P> 1928template <class P>
1929std::span<u8> BufferCache<P>::ImmediateBuffer(size_t wanted_capacity) { 1929std::span<u8> BufferCache<P>::ImmediateBuffer(size_t wanted_capacity) {
1930 if (wanted_capacity > immediate_buffer_capacity) { 1930 immediate_buffer_alloc.resize_destructive(wanted_capacity);
1931 immediate_buffer_capacity = wanted_capacity; 1931 return std::span<u8>(immediate_buffer_alloc.data(), wanted_capacity);
1932 immediate_buffer_alloc = std::make_unique<u8[]>(wanted_capacity);
1933 }
1934 return std::span<u8>(immediate_buffer_alloc.get(), wanted_capacity);
1935} 1932}
1936 1933
1937template <class P> 1934template <class P>
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 9835e3ac1..322de2606 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -56,7 +56,7 @@ bool DmaPusher::Step() {
56 56
57 if (command_list.prefetch_command_list.size()) { 57 if (command_list.prefetch_command_list.size()) {
58 // Prefetched command list from nvdrv, used for things like synchronization 58 // Prefetched command list from nvdrv, used for things like synchronization
59 command_headers = std::move(command_list.prefetch_command_list); 59 ProcessCommands(command_list.prefetch_command_list);
60 dma_pushbuffer.pop(); 60 dma_pushbuffer.pop();
61 } else { 61 } else {
62 const CommandListHeader command_list_header{ 62 const CommandListHeader command_list_header{
@@ -74,7 +74,7 @@ bool DmaPusher::Step() {
74 } 74 }
75 75
76 // Push buffer non-empty, read a word 76 // Push buffer non-empty, read a word
77 command_headers.resize(command_list_header.size); 77 command_headers.resize_destructive(command_list_header.size);
78 if (Settings::IsGPULevelHigh()) { 78 if (Settings::IsGPULevelHigh()) {
79 memory_manager.ReadBlock(dma_get, command_headers.data(), 79 memory_manager.ReadBlock(dma_get, command_headers.data(),
80 command_list_header.size * sizeof(u32)); 80 command_list_header.size * sizeof(u32));
@@ -82,16 +82,21 @@ bool DmaPusher::Step() {
82 memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), 82 memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(),
83 command_list_header.size * sizeof(u32)); 83 command_list_header.size * sizeof(u32));
84 } 84 }
85 ProcessCommands(command_headers);
85 } 86 }
86 for (std::size_t index = 0; index < command_headers.size();) { 87
87 const CommandHeader& command_header = command_headers[index]; 88 return true;
89}
90
91void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
92 for (std::size_t index = 0; index < commands.size();) {
93 const CommandHeader& command_header = commands[index];
88 94
89 if (dma_state.method_count) { 95 if (dma_state.method_count) {
90 // Data word of methods command 96 // Data word of methods command
91 if (dma_state.non_incrementing) { 97 if (dma_state.non_incrementing) {
92 const u32 max_write = static_cast<u32>( 98 const u32 max_write = static_cast<u32>(
93 std::min<std::size_t>(index + dma_state.method_count, command_headers.size()) - 99 std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index);
94 index);
95 CallMultiMethod(&command_header.argument, max_write); 100 CallMultiMethod(&command_header.argument, max_write);
96 dma_state.method_count -= max_write; 101 dma_state.method_count -= max_write;
97 dma_state.is_last_call = true; 102 dma_state.is_last_call = true;
@@ -142,8 +147,6 @@ bool DmaPusher::Step() {
142 } 147 }
143 index++; 148 index++;
144 } 149 }
145
146 return true;
147} 150}
148 151
149void DmaPusher::SetState(const CommandHeader& command_header) { 152void DmaPusher::SetState(const CommandHeader& command_header) {
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 938f0f11c..6f00de937 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -4,11 +4,13 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <span>
7#include <vector> 8#include <vector>
8#include <queue> 9#include <queue>
9 10
10#include "common/bit_field.h" 11#include "common/bit_field.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/scratch_buffer.h"
12#include "video_core/engines/engine_interface.h" 14#include "video_core/engines/engine_interface.h"
13#include "video_core/engines/puller.h" 15#include "video_core/engines/puller.h"
14 16
@@ -136,13 +138,15 @@ private:
136 static constexpr u32 non_puller_methods = 0x40; 138 static constexpr u32 non_puller_methods = 0x40;
137 static constexpr u32 max_subchannels = 8; 139 static constexpr u32 max_subchannels = 8;
138 bool Step(); 140 bool Step();
141 void ProcessCommands(std::span<const CommandHeader> commands);
139 142
140 void SetState(const CommandHeader& command_header); 143 void SetState(const CommandHeader& command_header);
141 144
142 void CallMethod(u32 argument) const; 145 void CallMethod(u32 argument) const;
143 void CallMultiMethod(const u32* base_start, u32 num_methods) const; 146 void CallMultiMethod(const u32* base_start, u32 num_methods) const;
144 147
145 std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once 148 Common::ScratchBuffer<CommandHeader>
149 command_headers; ///< Buffer for list of commands fetched at once
146 150
147 std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed 151 std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed
148 std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer 152 std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer
@@ -159,7 +163,7 @@ private:
159 DmaState dma_state{}; 163 DmaState dma_state{};
160 bool dma_increment_once{}; 164 bool dma_increment_once{};
161 165
162 bool ib_enable{true}; ///< IB mode enabled 166 const bool ib_enable{true}; ///< IB mode enabled
163 167
164 std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; 168 std::array<Engines::EngineInterface*, max_subchannels> subchannels{};
165 169
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index b213c374f..3a78421f6 100644
--- a/src/video_core/engines/draw_manager.cpp
+++ b/src/video_core/engines/draw_manager.cpp
@@ -46,21 +46,26 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
46 SetInlineIndexBuffer(regs.inline_index_4x8.index2); 46 SetInlineIndexBuffer(regs.inline_index_4x8.index2);
47 SetInlineIndexBuffer(regs.inline_index_4x8.index3); 47 SetInlineIndexBuffer(regs.inline_index_4x8.index3);
48 break; 48 break;
49 case MAXWELL3D_REG_INDEX(topology_override): 49 case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
50 use_topology_override = true; 50 case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): {
51 LOG_WARNING(HW_GPU, "(STUBBED) called");
51 break; 52 break;
53 }
52 default: 54 default:
53 break; 55 break;
54 } 56 }
55} 57}
56 58
57void DrawManager::Clear(u32 layer_count) { 59void DrawManager::Clear(u32 layer_count) {
58 maxwell3d->rasterizer->Clear(layer_count); 60 if (maxwell3d->ShouldExecute()) {
61 maxwell3d->rasterizer->Clear(layer_count);
62 }
59} 63}
60 64
61void DrawManager::DrawDeferred() { 65void DrawManager::DrawDeferred() {
62 if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0) 66 if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0) {
63 return; 67 return;
68 }
64 DrawEnd(draw_state.instance_count + 1, true); 69 DrawEnd(draw_state.instance_count + 1, true);
65 draw_state.instance_count = 0; 70 draw_state.instance_count = 0;
66} 71}
@@ -115,8 +120,9 @@ void DrawManager::DrawEnd(u32 instance_count, bool force_draw) {
115 const auto& regs{maxwell3d->regs}; 120 const auto& regs{maxwell3d->regs};
116 switch (draw_state.draw_mode) { 121 switch (draw_state.draw_mode) {
117 case DrawMode::Instance: 122 case DrawMode::Instance:
118 if (!force_draw) 123 if (!force_draw) {
119 break; 124 break;
125 }
120 [[fallthrough]]; 126 [[fallthrough]];
121 case DrawMode::General: 127 case DrawMode::General:
122 draw_state.base_instance = regs.global_base_instance_index; 128 draw_state.base_instance = regs.global_base_instance_index;
@@ -156,25 +162,28 @@ void DrawManager::DrawIndexSmall(u32 argument) {
156 ProcessDraw(true, 1); 162 ProcessDraw(true, 1);
157} 163}
158 164
159void DrawManager::ProcessTopologyOverride() { 165void DrawManager::UpdateTopology() {
160 if (!use_topology_override)
161 return;
162
163 const auto& regs{maxwell3d->regs}; 166 const auto& regs{maxwell3d->regs};
164 switch (regs.topology_override) { 167 switch (regs.primitive_topology_control) {
165 case PrimitiveTopologyOverride::None: 168 case PrimitiveTopologyControl::UseInBeginMethods:
166 break;
167 case PrimitiveTopologyOverride::Points:
168 draw_state.topology = PrimitiveTopology::Points;
169 break;
170 case PrimitiveTopologyOverride::Lines:
171 draw_state.topology = PrimitiveTopology::Lines;
172 break;
173 case PrimitiveTopologyOverride::LineStrip:
174 draw_state.topology = PrimitiveTopology::LineStrip;
175 break; 169 break;
176 default: 170 case PrimitiveTopologyControl::UseSeparateState:
177 draw_state.topology = static_cast<PrimitiveTopology>(regs.topology_override); 171 switch (regs.topology_override) {
172 case PrimitiveTopologyOverride::None:
173 break;
174 case PrimitiveTopologyOverride::Points:
175 draw_state.topology = PrimitiveTopology::Points;
176 break;
177 case PrimitiveTopologyOverride::Lines:
178 draw_state.topology = PrimitiveTopology::Lines;
179 break;
180 case PrimitiveTopologyOverride::LineStrip:
181 draw_state.topology = PrimitiveTopology::LineStrip;
182 break;
183 default:
184 draw_state.topology = static_cast<PrimitiveTopology>(regs.topology_override);
185 break;
186 }
178 break; 187 break;
179 } 188 }
180} 189}
@@ -183,9 +192,10 @@ void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) {
183 LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology, 192 LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology,
184 draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count); 193 draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count);
185 194
186 ProcessTopologyOverride(); 195 UpdateTopology();
187 196
188 if (maxwell3d->ShouldExecute()) 197 if (maxwell3d->ShouldExecute()) {
189 maxwell3d->rasterizer->Draw(draw_indexed, instance_count); 198 maxwell3d->rasterizer->Draw(draw_indexed, instance_count);
199 }
190} 200}
191} // namespace Tegra::Engines 201} // namespace Tegra::Engines
diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h
index 4f67027ca..0e6930a9c 100644
--- a/src/video_core/engines/draw_manager.h
+++ b/src/video_core/engines/draw_manager.h
@@ -10,6 +10,7 @@ class RasterizerInterface;
10} 10}
11 11
12namespace Tegra::Engines { 12namespace Tegra::Engines {
13using PrimitiveTopologyControl = Maxwell3D::Regs::PrimitiveTopologyControl;
13using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; 14using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
14using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; 15using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
15using IndexBuffer = Maxwell3D::Regs::IndexBuffer; 16using IndexBuffer = Maxwell3D::Regs::IndexBuffer;
@@ -58,12 +59,11 @@ private:
58 59
59 void DrawIndexSmall(u32 argument); 60 void DrawIndexSmall(u32 argument);
60 61
61 void ProcessTopologyOverride(); 62 void UpdateTopology();
62 63
63 void ProcessDraw(bool draw_indexed, u32 instance_count); 64 void ProcessDraw(bool draw_indexed, u32 instance_count);
64 65
65 Maxwell3D* maxwell3d{}; 66 Maxwell3D* maxwell3d{};
66 State draw_state{}; 67 State draw_state{};
67 bool use_topology_override{};
68}; 68};
69} // namespace Tegra::Engines 69} // namespace Tegra::Engines
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp
index e4f8331ab..cea1dd8b0 100644
--- a/src/video_core/engines/engine_upload.cpp
+++ b/src/video_core/engines/engine_upload.cpp
@@ -24,7 +24,7 @@ void State::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
24void State::ProcessExec(const bool is_linear_) { 24void State::ProcessExec(const bool is_linear_) {
25 write_offset = 0; 25 write_offset = 0;
26 copy_size = regs.line_length_in * regs.line_count; 26 copy_size = regs.line_length_in * regs.line_count;
27 inner_buffer.resize(copy_size); 27 inner_buffer.resize_destructive(copy_size);
28 is_linear = is_linear_; 28 is_linear = is_linear_;
29} 29}
30 30
@@ -70,7 +70,7 @@ void State::ProcessData(std::span<const u8> read_buffer) {
70 const std::size_t dst_size = Tegra::Texture::CalculateSize( 70 const std::size_t dst_size = Tegra::Texture::CalculateSize(
71 true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth, 71 true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth,
72 regs.dest.BlockHeight(), regs.dest.BlockDepth()); 72 regs.dest.BlockHeight(), regs.dest.BlockDepth());
73 tmp_buffer.resize(dst_size); 73 tmp_buffer.resize_destructive(dst_size);
74 memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); 74 memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size);
75 Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width, 75 Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width,
76 regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, 76 regs.dest.height, regs.dest.depth, x_offset, regs.dest.y,
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h
index 94fafd9dc..7242d2529 100644
--- a/src/video_core/engines/engine_upload.h
+++ b/src/video_core/engines/engine_upload.h
@@ -4,9 +4,10 @@
4#pragma once 4#pragma once
5 5
6#include <span> 6#include <span>
7#include <vector> 7
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/scratch_buffer.h"
10 11
11namespace Tegra { 12namespace Tegra {
12class MemoryManager; 13class MemoryManager;
@@ -73,8 +74,8 @@ private:
73 74
74 u32 write_offset = 0; 75 u32 write_offset = 0;
75 u32 copy_size = 0; 76 u32 copy_size = 0;
76 std::vector<u8> inner_buffer; 77 Common::ScratchBuffer<u8> inner_buffer;
77 std::vector<u8> tmp_buffer; 78 Common::ScratchBuffer<u8> tmp_buffer;
78 bool is_linear = false; 79 bool is_linear = false;
79 Registers& regs; 80 Registers& regs;
80 MemoryManager& memory_manager; 81 MemoryManager& memory_manager;
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index a189e60ae..f73d7bf0f 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -184,12 +184,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
184 const size_t src_size = 184 const size_t src_size =
185 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); 185 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
186 186
187 if (read_buffer.size() < src_size) { 187 read_buffer.resize_destructive(src_size);
188 read_buffer.resize(src_size); 188 write_buffer.resize_destructive(dst_size);
189 }
190 if (write_buffer.size() < dst_size) {
191 write_buffer.resize(dst_size);
192 }
193 189
194 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 190 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
195 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); 191 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
@@ -235,12 +231,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
235 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); 231 CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
236 const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count; 232 const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
237 233
238 if (read_buffer.size() < src_size) { 234 read_buffer.resize_destructive(src_size);
239 read_buffer.resize(src_size); 235 write_buffer.resize_destructive(dst_size);
240 }
241 if (write_buffer.size() < dst_size) {
242 write_buffer.resize(dst_size);
243 }
244 236
245 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 237 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
246 if (Settings::IsGPULevelExtreme()) { 238 if (Settings::IsGPULevelExtreme()) {
@@ -269,12 +261,8 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
269 pos_x = pos_x % x_in_gob; 261 pos_x = pos_x % x_in_gob;
270 pos_y = pos_y % 8; 262 pos_y = pos_y % 8;
271 263
272 if (read_buffer.size() < src_size) { 264 read_buffer.resize_destructive(src_size);
273 read_buffer.resize(src_size); 265 write_buffer.resize_destructive(dst_size);
274 }
275 if (write_buffer.size() < dst_size) {
276 write_buffer.resize(dst_size);
277 }
278 266
279 if (Settings::IsGPULevelExtreme()) { 267 if (Settings::IsGPULevelExtreme()) {
280 memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size); 268 memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size);
@@ -333,14 +321,10 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() {
333 const u32 pitch = x_elements * bytes_per_pixel; 321 const u32 pitch = x_elements * bytes_per_pixel;
334 const size_t mid_buffer_size = pitch * regs.line_count; 322 const size_t mid_buffer_size = pitch * regs.line_count;
335 323
336 if (read_buffer.size() < src_size) { 324 read_buffer.resize_destructive(src_size);
337 read_buffer.resize(src_size); 325 write_buffer.resize_destructive(dst_size);
338 }
339 if (write_buffer.size() < dst_size) {
340 write_buffer.resize(dst_size);
341 }
342 326
343 intermediate_buffer.resize(mid_buffer_size); 327 intermediate_buffer.resize_destructive(mid_buffer_size);
344 328
345 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 329 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
346 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); 330 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index d40d3d302..c88191a61 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -6,8 +6,10 @@
6#include <array> 6#include <array>
7#include <cstddef> 7#include <cstddef>
8#include <vector> 8#include <vector>
9
9#include "common/bit_field.h" 10#include "common/bit_field.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/scratch_buffer.h"
11#include "video_core/engines/engine_interface.h" 13#include "video_core/engines/engine_interface.h"
12 14
13namespace Core { 15namespace Core {
@@ -234,9 +236,9 @@ private:
234 MemoryManager& memory_manager; 236 MemoryManager& memory_manager;
235 VideoCore::RasterizerInterface* rasterizer = nullptr; 237 VideoCore::RasterizerInterface* rasterizer = nullptr;
236 238
237 std::vector<u8> read_buffer; 239 Common::ScratchBuffer<u8> read_buffer;
238 std::vector<u8> write_buffer; 240 Common::ScratchBuffer<u8> write_buffer;
239 std::vector<u8> intermediate_buffer; 241 Common::ScratchBuffer<u8> intermediate_buffer;
240 242
241 static constexpr std::size_t NUM_REGS = 0x800; 243 static constexpr std::size_t NUM_REGS = 0x800;
242 struct Regs { 244 struct Regs {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 28b38273e..c6d54be63 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -223,8 +223,6 @@ struct GPU::Impl {
223 /// core timing events. 223 /// core timing events.
224 void Start() { 224 void Start() {
225 gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); 225 gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
226 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
227 cpu_context->MakeCurrent();
228 } 226 }
229 227
230 void NotifyShutdown() { 228 void NotifyShutdown() {
@@ -235,6 +233,9 @@ struct GPU::Impl {
235 233
236 /// Obtain the CPU Context 234 /// Obtain the CPU Context
237 void ObtainContext() { 235 void ObtainContext() {
236 if (!cpu_context) {
237 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
238 }
238 cpu_context->MakeCurrent(); 239 cpu_context->MakeCurrent();
239 } 240 }
240 241
diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp
index ac0b7d20e..36a04e4e0 100644
--- a/src/video_core/host1x/vic.cpp
+++ b/src/video_core/host1x/vic.cpp
@@ -155,7 +155,7 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
155 // swizzle pitch linear to block linear 155 // swizzle pitch linear to block linear
156 const u32 block_height = static_cast<u32>(config.block_linear_height_log2); 156 const u32 block_height = static_cast<u32>(config.block_linear_height_log2);
157 const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); 157 const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0);
158 luma_buffer.resize(size); 158 luma_buffer.resize_destructive(size);
159 std::span<const u8> frame_buff(converted_frame_buf_addr, 4 * width * height); 159 std::span<const u8> frame_buff(converted_frame_buf_addr, 4 * width * height);
160 Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, 0, 0, width, height, 160 Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, 0, 0, width, height,
161 block_height, 0, width * 4); 161 block_height, 0, width * 4);
@@ -181,8 +181,8 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
181 181
182 const auto stride = static_cast<size_t>(frame->linesize[0]); 182 const auto stride = static_cast<size_t>(frame->linesize[0]);
183 183
184 luma_buffer.resize(aligned_width * surface_height); 184 luma_buffer.resize_destructive(aligned_width * surface_height);
185 chroma_buffer.resize(aligned_width * surface_height / 2); 185 chroma_buffer.resize_destructive(aligned_width * surface_height / 2);
186 186
187 // Populate luma buffer 187 // Populate luma buffer
188 const u8* luma_src = frame->data[0]; 188 const u8* luma_src = frame->data[0];
diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h
index 2b78786e8..3d9753047 100644
--- a/src/video_core/host1x/vic.h
+++ b/src/video_core/host1x/vic.h
@@ -4,8 +4,9 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <vector> 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/scratch_buffer.h"
9 10
10struct SwsContext; 11struct SwsContext;
11 12
@@ -49,8 +50,8 @@ private:
49 /// size does not change during a stream 50 /// size does not change during a stream
50 using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>; 51 using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>;
51 AVMallocPtr converted_frame_buffer; 52 AVMallocPtr converted_frame_buffer;
52 std::vector<u8> luma_buffer; 53 Common::ScratchBuffer<u8> luma_buffer;
53 std::vector<u8> chroma_buffer; 54 Common::ScratchBuffer<u8> chroma_buffer;
54 55
55 GPUVAddr config_struct_address{}; 56 GPUVAddr config_struct_address{};
56 GPUVAddr output_surface_luma_address{}; 57 GPUVAddr output_surface_luma_address{};
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index e2e3dac34..cee5c3247 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -112,7 +112,7 @@ bool IsASTCSupported() {
112} 112}
113} // Anonymous namespace 113} // Anonymous namespace
114 114
115Device::Device() { 115Device::Device(Core::Frontend::EmuWindow& emu_window) {
116 if (!GLAD_GL_VERSION_4_6) { 116 if (!GLAD_GL_VERSION_4_6) {
117 LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); 117 LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available");
118 throw std::runtime_error{"Insufficient version"}; 118 throw std::runtime_error{"Insufficient version"};
@@ -126,9 +126,9 @@ Device::Device() {
126 const bool is_intel = vendor_name == "Intel"; 126 const bool is_intel = vendor_name == "Intel";
127 127
128#ifdef __unix__ 128#ifdef __unix__
129 const bool is_linux = true; 129 constexpr bool is_linux = true;
130#else 130#else
131 const bool is_linux = false; 131 constexpr bool is_linux = false;
132#endif 132#endif
133 133
134 bool disable_fast_buffer_sub_data = false; 134 bool disable_fast_buffer_sub_data = false;
@@ -193,9 +193,11 @@ Device::Device() {
193 } 193 }
194 } 194 }
195 195
196 strict_context_required = emu_window.StrictContextRequired();
196 // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. 197 // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation.
198 // Blocks EGL on Wayland from using asynchronous shader compilation.
197 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && 199 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() &&
198 !(is_amd || (is_intel && !is_linux)); 200 !(is_amd || (is_intel && !is_linux)) && !strict_context_required;
199 use_driver_cache = is_nvidia; 201 use_driver_cache = is_nvidia;
200 202
201 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); 203 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 5ef51ebcf..2a72d84be 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -5,6 +5,7 @@
5 5
6#include <cstddef> 6#include <cstddef>
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/frontend/emu_window.h"
8#include "shader_recompiler/stage.h" 9#include "shader_recompiler/stage.h"
9 10
10namespace Settings { 11namespace Settings {
@@ -15,7 +16,7 @@ namespace OpenGL {
15 16
16class Device { 17class Device {
17public: 18public:
18 explicit Device(); 19 explicit Device(Core::Frontend::EmuWindow& emu_window);
19 20
20 [[nodiscard]] std::string GetVendorName() const; 21 [[nodiscard]] std::string GetVendorName() const;
21 22
@@ -173,6 +174,10 @@ public:
173 return can_report_memory; 174 return can_report_memory;
174 } 175 }
175 176
177 bool StrictContextRequired() const {
178 return strict_context_required;
179 }
180
176private: 181private:
177 static bool TestVariableAoffi(); 182 static bool TestVariableAoffi();
178 static bool TestPreciseBug(); 183 static bool TestPreciseBug();
@@ -216,6 +221,7 @@ private:
216 bool has_cbuf_ftou_bug{}; 221 bool has_cbuf_ftou_bug{};
217 bool has_bool_ref_bug{}; 222 bool has_bool_ref_bug{};
218 bool can_report_memory{}; 223 bool can_report_memory{};
224 bool strict_context_required{};
219 225
220 std::string vendor_name; 226 std::string vendor_name;
221}; 227};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 64ed6f628..a44b8c454 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -138,9 +138,6 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load
138 138
139void RasterizerOpenGL::Clear(u32 layer_count) { 139void RasterizerOpenGL::Clear(u32 layer_count) {
140 MICROPROFILE_SCOPE(OpenGL_Clears); 140 MICROPROFILE_SCOPE(OpenGL_Clears);
141 if (!maxwell3d->ShouldExecute()) {
142 return;
143 }
144 141
145 const auto& regs = maxwell3d->regs; 142 const auto& regs = maxwell3d->regs;
146 bool use_color{}; 143 bool use_color{};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index a59d0d24e..f8868a012 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -174,6 +174,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
174 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, 174 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_},
175 state_tracker{state_tracker_}, shader_notify{shader_notify_}, 175 state_tracker{state_tracker_}, shader_notify{shader_notify_},
176 use_asynchronous_shaders{device.UseAsynchronousShaders()}, 176 use_asynchronous_shaders{device.UseAsynchronousShaders()},
177 strict_context_required{device.StrictContextRequired()},
177 profile{ 178 profile{
178 .supported_spirv = 0x00010000, 179 .supported_spirv = 0x00010000,
179 180
@@ -203,6 +204,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
203 .support_int64_atomics = false, 204 .support_int64_atomics = false,
204 .support_derivative_control = device.HasDerivativeControl(), 205 .support_derivative_control = device.HasDerivativeControl(),
205 .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), 206 .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
207 .support_native_ndc = true,
206 .support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(), 208 .support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(),
207 .support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(), 209 .support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(),
208 .support_gl_texture_shadow_lod = device.HasTextureShadowLod(), 210 .support_gl_texture_shadow_lod = device.HasTextureShadowLod(),
@@ -255,9 +257,14 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
255 } 257 }
256 shader_cache_filename = base_dir / "opengl.bin"; 258 shader_cache_filename = base_dir / "opengl.bin";
257 259
258 if (!workers) { 260 if (!workers && !strict_context_required) {
259 workers = CreateWorkers(); 261 workers = CreateWorkers();
260 } 262 }
263 std::optional<Context> strict_context;
264 if (strict_context_required) {
265 strict_context.emplace(emu_window);
266 }
267
261 struct { 268 struct {
262 std::mutex mutex; 269 std::mutex mutex;
263 size_t total{}; 270 size_t total{};
@@ -265,44 +272,49 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
265 bool has_loaded{}; 272 bool has_loaded{};
266 } state; 273 } state;
267 274
275 const auto queue_work{[&](Common::UniqueFunction<void, Context*>&& work) {
276 if (strict_context_required) {
277 work(&strict_context.value());
278 } else {
279 workers->QueueWork(std::move(work));
280 }
281 }};
268 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { 282 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
269 ComputePipelineKey key; 283 ComputePipelineKey key;
270 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 284 file.read(reinterpret_cast<char*>(&key), sizeof(key));
271 workers->QueueWork( 285 queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
272 [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { 286 ctx->pools.ReleaseContents();
273 ctx->pools.ReleaseContents(); 287 auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
274 auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; 288 std::scoped_lock lock{state.mutex};
275 std::scoped_lock lock{state.mutex}; 289 if (pipeline) {
276 if (pipeline) { 290 compute_cache.emplace(key, std::move(pipeline));
277 compute_cache.emplace(key, std::move(pipeline)); 291 }
278 } 292 ++state.built;
279 ++state.built; 293 if (state.has_loaded) {
280 if (state.has_loaded) { 294 callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
281 callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); 295 }
282 } 296 });
283 });
284 ++state.total; 297 ++state.total;
285 }}; 298 }};
286 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { 299 const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
287 GraphicsPipelineKey key; 300 GraphicsPipelineKey key;
288 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 301 file.read(reinterpret_cast<char*>(&key), sizeof(key));
289 workers->QueueWork( 302 queue_work([this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable {
290 [this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { 303 boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
291 boost::container::static_vector<Shader::Environment*, 5> env_ptrs; 304 for (auto& env : envs) {
292 for (auto& env : envs) { 305 env_ptrs.push_back(&env);
293 env_ptrs.push_back(&env); 306 }
294 } 307 ctx->pools.ReleaseContents();
295 ctx->pools.ReleaseContents(); 308 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
296 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; 309 std::scoped_lock lock{state.mutex};
297 std::scoped_lock lock{state.mutex}; 310 if (pipeline) {
298 if (pipeline) { 311 graphics_cache.emplace(key, std::move(pipeline));
299 graphics_cache.emplace(key, std::move(pipeline)); 312 }
300 } 313 ++state.built;
301 ++state.built; 314 if (state.has_loaded) {
302 if (state.has_loaded) { 315 callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
303 callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); 316 }
304 } 317 });
305 });
306 ++state.total; 318 ++state.total;
307 }}; 319 }};
308 LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); 320 LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics);
@@ -314,6 +326,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
314 state.has_loaded = true; 326 state.has_loaded = true;
315 lock.unlock(); 327 lock.unlock();
316 328
329 if (strict_context_required) {
330 return;
331 }
317 workers->WaitForRequests(stop_loading); 332 workers->WaitForRequests(stop_loading);
318 if (!use_asynchronous_shaders) { 333 if (!use_asynchronous_shaders) {
319 workers.reset(); 334 workers.reset();
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 53ffea904..f82420592 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -69,6 +69,7 @@ private:
69 StateTracker& state_tracker; 69 StateTracker& state_tracker;
70 VideoCore::ShaderNotify& shader_notify; 70 VideoCore::ShaderNotify& shader_notify;
71 const bool use_asynchronous_shaders; 71 const bool use_asynchronous_shaders;
72 const bool strict_context_required;
72 73
73 GraphicsPipelineKey graphics_key{}; 74 GraphicsPipelineKey graphics_key{};
74 GraphicsPipeline* current_pipeline{}; 75 GraphicsPipeline* current_pipeline{};
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 5b5e178ad..bc75680f0 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -140,8 +140,8 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
140 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, 140 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
141 std::unique_ptr<Core::Frontend::GraphicsContext> context_) 141 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
142 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, 142 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_},
143 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{}, 143 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, device{emu_window_},
144 program_manager{device}, 144 state_tracker{}, program_manager{device},
145 rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { 145 rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) {
146 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { 146 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
147 glEnable(GL_DEBUG_OUTPUT); 147 glEnable(GL_DEBUG_OUTPUT);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 18be54729..f502a7d09 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -139,23 +139,25 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
139 RenderScreenshot(*framebuffer, use_accelerated); 139 RenderScreenshot(*framebuffer, use_accelerated);
140 140
141 bool has_been_recreated = false; 141 bool has_been_recreated = false;
142 const auto recreate_swapchain = [&] { 142 const auto recreate_swapchain = [&](u32 width, u32 height) {
143 if (!has_been_recreated) { 143 if (!has_been_recreated) {
144 has_been_recreated = true; 144 has_been_recreated = true;
145 scheduler.Finish(); 145 scheduler.Finish();
146 } 146 }
147 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 147 swapchain.Create(width, height, is_srgb);
148 swapchain.Create(layout.width, layout.height, is_srgb);
149 }; 148 };
150 if (swapchain.NeedsRecreation(is_srgb)) { 149
151 recreate_swapchain(); 150 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
151 if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width ||
152 swapchain.GetHeight() != layout.height) {
153 recreate_swapchain(layout.width, layout.height);
152 } 154 }
153 bool is_outdated; 155 bool is_outdated;
154 do { 156 do {
155 swapchain.AcquireNextImage(); 157 swapchain.AcquireNextImage();
156 is_outdated = swapchain.IsOutDated(); 158 is_outdated = swapchain.IsOutDated();
157 if (is_outdated) { 159 if (is_outdated) {
158 recreate_swapchain(); 160 recreate_swapchain(layout.width, layout.height);
159 } 161 }
160 } while (is_outdated); 162 } while (is_outdated);
161 if (has_been_recreated) { 163 if (has_been_recreated) {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 54a12b35f..6b54d7111 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -461,6 +461,9 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir
461 461
462void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, 462void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size,
463 u32 stride) { 463 u32 stride) {
464 if (index >= device.GetMaxVertexInputBindings()) {
465 return;
466 }
464 if (device.IsExtExtendedDynamicStateSupported()) { 467 if (device.IsExtExtendedDynamicStateSupported()) {
465 scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { 468 scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
466 const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; 469 const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 006128638..515d8d869 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -529,7 +529,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
529 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; 529 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
530 static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; 530 static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
531 if (key.state.dynamic_vertex_input) { 531 if (key.state.dynamic_vertex_input) {
532 for (size_t index = 0; index < key.state.attributes.size(); ++index) { 532 const size_t num_vertex_arrays = std::min(
533 key.state.attributes.size(), static_cast<size_t>(device.GetMaxVertexInputBindings()));
534 for (size_t index = 0; index < num_vertex_arrays; ++index) {
533 const u32 type = key.state.DynamicAttributeType(index); 535 const u32 type = key.state.DynamicAttributeType(index);
534 if (!stage_infos[0].loads.Generic(index) || type == 0) { 536 if (!stage_infos[0].loads.Generic(index) || type == 0) {
535 continue; 537 continue;
@@ -551,7 +553,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
551 }); 553 });
552 } 554 }
553 } else { 555 } else {
554 for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 556 const size_t num_vertex_arrays = std::min(
557 Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
558 for (size_t index = 0; index < num_vertex_arrays; ++index) {
555 const bool instanced = key.state.binding_divisors[index] != 0; 559 const bool instanced = key.state.binding_divisors[index] != 0;
556 const auto rate = 560 const auto rate =
557 instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 561 instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
@@ -580,6 +584,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
580 }); 584 });
581 } 585 }
582 } 586 }
587 ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes());
588
583 VkPipelineVertexInputStateCreateInfo vertex_input_ci{ 589 VkPipelineVertexInputStateCreateInfo vertex_input_ci{
584 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 590 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
585 .pNext = nullptr, 591 .pNext = nullptr,
@@ -634,23 +640,33 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
634 }; 640 };
635 std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles; 641 std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
636 std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle); 642 std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
637 const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{ 643 VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
638 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV, 644 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV,
639 .pNext = nullptr, 645 .pNext = nullptr,
640 .flags = 0, 646 .flags = 0,
641 .viewportCount = Maxwell::NumViewports, 647 .viewportCount = Maxwell::NumViewports,
642 .pViewportSwizzles = swizzles.data(), 648 .pViewportSwizzles = swizzles.data(),
643 }; 649 };
644 const VkPipelineViewportStateCreateInfo viewport_ci{ 650 VkPipelineViewportDepthClipControlCreateInfoEXT ndc_info{
651 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT,
652 .pNext = nullptr,
653 .negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE,
654 };
655 VkPipelineViewportStateCreateInfo viewport_ci{
645 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, 656 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
646 .pNext = device.IsNvViewportSwizzleSupported() ? &swizzle_ci : nullptr, 657 .pNext = nullptr,
647 .flags = 0, 658 .flags = 0,
648 .viewportCount = Maxwell::NumViewports, 659 .viewportCount = Maxwell::NumViewports,
649 .pViewports = nullptr, 660 .pViewports = nullptr,
650 .scissorCount = Maxwell::NumViewports, 661 .scissorCount = Maxwell::NumViewports,
651 .pScissors = nullptr, 662 .pScissors = nullptr,
652 }; 663 };
653 664 if (device.IsNvViewportSwizzleSupported()) {
665 swizzle_ci.pNext = std::exchange(viewport_ci.pNext, &swizzle_ci);
666 }
667 if (device.IsExtDepthClipControlSupported()) {
668 ndc_info.pNext = std::exchange(viewport_ci.pNext, &ndc_info);
669 }
654 VkPipelineRasterizationStateCreateInfo rasterization_ci{ 670 VkPipelineRasterizationStateCreateInfo rasterization_ci{
655 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 671 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
656 .pNext = nullptr, 672 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 81f5f3e11..e7262420c 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -321,6 +321,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
321 .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), 321 .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
322 .support_derivative_control = true, 322 .support_derivative_control = true,
323 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), 323 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
324 .support_native_ndc = device.IsExtDepthClipControlSupported(),
324 325
325 .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), 326 .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
326 327
@@ -341,6 +342,15 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
341 .support_snorm_render_buffer = true, 342 .support_snorm_render_buffer = true,
342 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), 343 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
343 }; 344 };
345
346 if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) {
347 LOG_WARNING(Render_Vulkan, "maxVertexInputAttributes is too low: {} < {}",
348 device.GetMaxVertexInputAttributes(), Maxwell::NumVertexAttributes);
349 }
350 if (device.GetMaxVertexInputBindings() < Maxwell::NumVertexArrays) {
351 LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}",
352 device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
353 }
344} 354}
345 355
346PipelineCache::~PipelineCache() = default; 356PipelineCache::~PipelineCache() = default;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 3774f303a..ac1eb9895 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -220,9 +220,6 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
220void RasterizerVulkan::Clear(u32 layer_count) { 220void RasterizerVulkan::Clear(u32 layer_count) {
221 MICROPROFILE_SCOPE(Vulkan_Clearing); 221 MICROPROFILE_SCOPE(Vulkan_Clearing);
222 222
223 if (!maxwell3d->ShouldExecute()) {
224 return;
225 }
226 FlushWork(); 223 FlushWork();
227 224
228 query_cache.UpdateCounters(); 225 query_cache.UpdateCounters();
@@ -665,8 +662,7 @@ void RasterizerVulkan::BeginTransformFeedback() {
665 return; 662 return;
666 } 663 }
667 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || 664 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
668 regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) || 665 regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation));
669 regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
670 scheduler.Record( 666 scheduler.Record(
671 [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); 667 [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); });
672} 668}
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index d7be417f5..b6810eef9 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -67,17 +67,19 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi
67 67
68} // Anonymous namespace 68} // Anonymous namespace
69 69
70Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, u32 width, 70Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
71 u32 height, bool srgb) 71 u32 width_, u32 height_, bool srgb)
72 : surface{surface_}, device{device_}, scheduler{scheduler_} { 72 : surface{surface_}, device{device_}, scheduler{scheduler_} {
73 Create(width, height, srgb); 73 Create(width_, height_, srgb);
74} 74}
75 75
76Swapchain::~Swapchain() = default; 76Swapchain::~Swapchain() = default;
77 77
78void Swapchain::Create(u32 width, u32 height, bool srgb) { 78void Swapchain::Create(u32 width_, u32 height_, bool srgb) {
79 is_outdated = false; 79 is_outdated = false;
80 is_suboptimal = false; 80 is_suboptimal = false;
81 width = width_;
82 height = height_;
81 83
82 const auto physical_device = device.GetPhysical(); 84 const auto physical_device = device.GetPhysical();
83 const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; 85 const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)};
@@ -88,7 +90,7 @@ void Swapchain::Create(u32 width, u32 height, bool srgb) {
88 device.GetLogical().WaitIdle(); 90 device.GetLogical().WaitIdle();
89 Destroy(); 91 Destroy();
90 92
91 CreateSwapchain(capabilities, width, height, srgb); 93 CreateSwapchain(capabilities, srgb);
92 CreateSemaphores(); 94 CreateSemaphores();
93 CreateImageViews(); 95 CreateImageViews();
94 96
@@ -148,8 +150,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
148 } 150 }
149} 151}
150 152
151void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, 153void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) {
152 bool srgb) {
153 const auto physical_device{device.GetPhysical()}; 154 const auto physical_device{device.GetPhysical()};
154 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; 155 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
155 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; 156 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index 111b3902d..caf1ff32b 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -80,9 +80,16 @@ public:
80 return *present_semaphores[frame_index]; 80 return *present_semaphores[frame_index];
81 } 81 }
82 82
83 u32 GetWidth() const {
84 return width;
85 }
86
87 u32 GetHeight() const {
88 return height;
89 }
90
83private: 91private:
84 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, 92 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb);
85 bool srgb);
86 void CreateSemaphores(); 93 void CreateSemaphores();
87 void CreateImageViews(); 94 void CreateImageViews();
88 95
@@ -105,6 +112,9 @@ private:
105 std::vector<u64> resource_ticks; 112 std::vector<u64> resource_ticks;
106 std::vector<vk::Semaphore> present_semaphores; 113 std::vector<vk::Semaphore> present_semaphores;
107 114
115 u32 width;
116 u32 height;
117
108 u32 image_index{}; 118 u32 image_index{};
109 u32 frame_index{}; 119 u32 frame_index{};
110 120
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 8e68a2e53..27c82cd20 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -39,6 +39,12 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
39 sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear); 39 sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear);
40 sampler_descriptor.cubemap_anisotropy.Assign(1); 40 sampler_descriptor.cubemap_anisotropy.Assign(1);
41 41
42 // These values were chosen based on typical peak swizzle data sizes seen in some titles
43 static constexpr size_t SWIZZLE_DATA_BUFFER_INITIAL_CAPACITY = 8_MiB;
44 static constexpr size_t UNSWIZZLE_DATA_BUFFER_INITIAL_CAPACITY = 1_MiB;
45 swizzle_data_buffer.resize_destructive(SWIZZLE_DATA_BUFFER_INITIAL_CAPACITY);
46 unswizzle_data_buffer.resize_destructive(UNSWIZZLE_DATA_BUFFER_INITIAL_CAPACITY);
47
42 // Make sure the first index is reserved for the null resources 48 // Make sure the first index is reserved for the null resources
43 // This way the null resource becomes a compile time constant 49 // This way the null resource becomes a compile time constant
44 void(slot_images.insert(NullImageParams{})); 50 void(slot_images.insert(NullImageParams{}));
@@ -90,7 +96,8 @@ void TextureCache<P>::RunGarbageCollector() {
90 const auto copies = FullDownloadCopies(image.info); 96 const auto copies = FullDownloadCopies(image.info);
91 image.DownloadMemory(map, copies); 97 image.DownloadMemory(map, copies);
92 runtime.Finish(); 98 runtime.Finish();
93 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); 99 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
100 swizzle_data_buffer);
94 } 101 }
95 if (True(image.flags & ImageFlagBits::Tracked)) { 102 if (True(image.flags & ImageFlagBits::Tracked)) {
96 UntrackImage(image, image_id); 103 UntrackImage(image, image_id);
@@ -461,7 +468,8 @@ void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
461 const auto copies = FullDownloadCopies(image.info); 468 const auto copies = FullDownloadCopies(image.info);
462 image.DownloadMemory(map, copies); 469 image.DownloadMemory(map, copies);
463 runtime.Finish(); 470 runtime.Finish();
464 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); 471 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
472 swizzle_data_buffer);
465 } 473 }
466} 474}
467 475
@@ -672,7 +680,8 @@ void TextureCache<P>::PopAsyncFlushes() {
672 for (const ImageId image_id : download_ids) { 680 for (const ImageId image_id : download_ids) {
673 const ImageBase& image = slot_images[image_id]; 681 const ImageBase& image = slot_images[image_id];
674 const auto copies = FullDownloadCopies(image.info); 682 const auto copies = FullDownloadCopies(image.info);
675 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span); 683 SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span,
684 swizzle_data_buffer);
676 download_map.offset += image.unswizzled_size_bytes; 685 download_map.offset += image.unswizzled_size_bytes;
677 download_span = download_span.subspan(image.unswizzled_size_bytes); 686 download_span = download_span.subspan(image.unswizzled_size_bytes);
678 } 687 }
@@ -734,13 +743,21 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
734 gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); 743 gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes());
735 const auto uploads = FullUploadSwizzles(image.info); 744 const auto uploads = FullUploadSwizzles(image.info);
736 runtime.AccelerateImageUpload(image, staging, uploads); 745 runtime.AccelerateImageUpload(image, staging, uploads);
737 } else if (True(image.flags & ImageFlagBits::Converted)) { 746 return;
738 std::vector<u8> unswizzled_data(image.unswizzled_size_bytes); 747 }
739 auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, unswizzled_data); 748 const size_t guest_size_bytes = image.guest_size_bytes;
740 ConvertImage(unswizzled_data, image.info, mapped_span, copies); 749 swizzle_data_buffer.resize_destructive(guest_size_bytes);
750 gpu_memory->ReadBlockUnsafe(gpu_addr, swizzle_data_buffer.data(), guest_size_bytes);
751
752 if (True(image.flags & ImageFlagBits::Converted)) {
753 unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes);
754 auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer,
755 unswizzle_data_buffer);
756 ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies);
741 image.UploadMemory(staging, copies); 757 image.UploadMemory(staging, copies);
742 } else { 758 } else {
743 const auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, mapped_span); 759 const auto copies =
760 UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, mapped_span);
744 image.UploadMemory(staging, copies); 761 image.UploadMemory(staging, copies);
745 } 762 }
746} 763}
@@ -910,7 +927,7 @@ void TextureCache<P>::InvalidateScale(Image& image) {
910} 927}
911 928
912template <class P> 929template <class P>
913u64 TextureCache<P>::GetScaledImageSizeBytes(ImageBase& image) { 930u64 TextureCache<P>::GetScaledImageSizeBytes(const ImageBase& image) {
914 const u64 scale_up = static_cast<u64>(Settings::values.resolution_info.up_scale * 931 const u64 scale_up = static_cast<u64>(Settings::values.resolution_info.up_scale *
915 Settings::values.resolution_info.up_scale); 932 Settings::values.resolution_info.up_scale);
916 const u64 down_shift = static_cast<u64>(Settings::values.resolution_info.down_shift + 933 const u64 down_shift = static_cast<u64>(Settings::values.resolution_info.down_shift +
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 587339a31..4fd677a80 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -17,6 +17,7 @@
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 "common/polyfill_ranges.h"
20#include "common/scratch_buffer.h"
20#include "video_core/compatible_formats.h" 21#include "video_core/compatible_formats.h"
21#include "video_core/control/channel_state_cache.h" 22#include "video_core/control/channel_state_cache.h"
22#include "video_core/delayed_destruction_ring.h" 23#include "video_core/delayed_destruction_ring.h"
@@ -368,7 +369,7 @@ private:
368 void InvalidateScale(Image& image); 369 void InvalidateScale(Image& image);
369 bool ScaleUp(Image& image); 370 bool ScaleUp(Image& image);
370 bool ScaleDown(Image& image); 371 bool ScaleDown(Image& image);
371 u64 GetScaledImageSizeBytes(ImageBase& image); 372 u64 GetScaledImageSizeBytes(const ImageBase& image);
372 373
373 Runtime& runtime; 374 Runtime& runtime;
374 375
@@ -417,6 +418,9 @@ private:
417 418
418 std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table; 419 std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table;
419 420
421 Common::ScratchBuffer<u8> swizzle_data_buffer;
422 Common::ScratchBuffer<u8> unswizzle_data_buffer;
423
420 u64 modification_tick = 0; 424 u64 modification_tick = 0;
421 u64 frame_tick = 0; 425 u64 frame_tick = 0;
422}; 426};
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index e8c908b42..03acc68d9 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -505,7 +505,7 @@ void SwizzlePitchLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
505 505
506void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 506void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
507 const ImageInfo& info, const BufferImageCopy& copy, 507 const ImageInfo& info, const BufferImageCopy& copy,
508 std::span<const u8> input) { 508 std::span<const u8> input, Common::ScratchBuffer<u8>& tmp_buffer) {
509 const Extent3D size = info.size; 509 const Extent3D size = info.size;
510 const LevelInfo level_info = MakeLevelInfo(info); 510 const LevelInfo level_info = MakeLevelInfo(info);
511 const Extent2D tile_size = DefaultBlockSize(info.format); 511 const Extent2D tile_size = DefaultBlockSize(info.format);
@@ -534,8 +534,8 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
534 tile_size.height, info.tile_width_spacing); 534 tile_size.height, info.tile_width_spacing);
535 const size_t subresource_size = sizes[level]; 535 const size_t subresource_size = sizes[level];
536 536
537 const auto dst_data = std::make_unique<u8[]>(subresource_size); 537 tmp_buffer.resize_destructive(subresource_size);
538 const std::span<u8> dst(dst_data.get(), subresource_size); 538 const std::span<u8> dst(tmp_buffer);
539 539
540 for (s32 layer = 0; layer < info.resources.layers; ++layer) { 540 for (s32 layer = 0; layer < info.resources.layers; ++layer) {
541 const std::span<const u8> src = input.subspan(host_offset); 541 const std::span<const u8> src = input.subspan(host_offset);
@@ -765,8 +765,9 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config
765} 765}
766 766
767std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 767std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
768 const ImageInfo& info, std::span<u8> output) { 768 const ImageInfo& info, std::span<const u8> input,
769 const size_t guest_size_bytes = CalculateGuestSizeInBytes(info); 769 std::span<u8> output) {
770 const size_t guest_size_bytes = input.size_bytes();
770 const u32 bpp_log2 = BytesPerBlockLog2(info.format); 771 const u32 bpp_log2 = BytesPerBlockLog2(info.format);
771 const Extent3D size = info.size; 772 const Extent3D size = info.size;
772 773
@@ -789,10 +790,6 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP
789 .image_extent = size, 790 .image_extent = size,
790 }}; 791 }};
791 } 792 }
792 const auto input_data = std::make_unique<u8[]>(guest_size_bytes);
793 gpu_memory.ReadBlockUnsafe(gpu_addr, input_data.get(), guest_size_bytes);
794 const std::span<const u8> input(input_data.get(), guest_size_bytes);
795
796 const LevelInfo level_info = MakeLevelInfo(info); 793 const LevelInfo level_info = MakeLevelInfo(info);
797 const s32 num_layers = info.resources.layers; 794 const s32 num_layers = info.resources.layers;
798 const s32 num_levels = info.resources.levels; 795 const s32 num_levels = info.resources.levels;
@@ -980,13 +977,14 @@ std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) {
980} 977}
981 978
982void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, 979void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
983 std::span<const BufferImageCopy> copies, std::span<const u8> memory) { 980 std::span<const BufferImageCopy> copies, std::span<const u8> memory,
981 Common::ScratchBuffer<u8>& tmp_buffer) {
984 const bool is_pitch_linear = info.type == ImageType::Linear; 982 const bool is_pitch_linear = info.type == ImageType::Linear;
985 for (const BufferImageCopy& copy : copies) { 983 for (const BufferImageCopy& copy : copies) {
986 if (is_pitch_linear) { 984 if (is_pitch_linear) {
987 SwizzlePitchLinearImage(gpu_memory, gpu_addr, info, copy, memory); 985 SwizzlePitchLinearImage(gpu_memory, gpu_addr, info, copy, memory);
988 } else { 986 } else {
989 SwizzleBlockLinearImage(gpu_memory, gpu_addr, info, copy, memory); 987 SwizzleBlockLinearImage(gpu_memory, gpu_addr, info, copy, memory, tmp_buffer);
990 } 988 }
991 } 989 }
992} 990}
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index 5e28f4ab3..d103db8ae 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -7,6 +7,7 @@
7#include <span> 7#include <span>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/scratch_buffer.h"
10 11
11#include "video_core/surface.h" 12#include "video_core/surface.h"
12#include "video_core/texture_cache/image_base.h" 13#include "video_core/texture_cache/image_base.h"
@@ -59,6 +60,7 @@ struct OverlapResult {
59 60
60[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, 61[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory,
61 GPUVAddr gpu_addr, const ImageInfo& info, 62 GPUVAddr gpu_addr, const ImageInfo& info,
63 std::span<const u8> input,
62 std::span<u8> output); 64 std::span<u8> output);
63 65
64[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, 66[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
@@ -76,7 +78,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
76[[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info); 78[[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info);
77 79
78void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, 80void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
79 std::span<const BufferImageCopy> copies, std::span<const u8> memory); 81 std::span<const BufferImageCopy> copies, std::span<const u8> memory,
82 Common::ScratchBuffer<u8>& tmp_buffer);
80 83
81[[nodiscard]] bool IsBlockLinearSizeCompatible(const ImageInfo& new_info, 84[[nodiscard]] bool IsBlockLinearSizeCompatible(const ImageInfo& new_info,
82 const ImageInfo& overlap_info, u32 new_level, 85 const ImageInfo& overlap_info, u32 new_level,
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 6a2ad4b1d..c4d31681a 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -421,7 +421,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
421 VkPhysicalDevice8BitStorageFeatures bit8_storage{ 421 VkPhysicalDevice8BitStorageFeatures bit8_storage{
422 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, 422 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES,
423 .pNext = nullptr, 423 .pNext = nullptr,
424 .storageBuffer8BitAccess = false, 424 .storageBuffer8BitAccess = true,
425 .uniformAndStorageBuffer8BitAccess = true, 425 .uniformAndStorageBuffer8BitAccess = true,
426 .storagePushConstant8 = false, 426 .storagePushConstant8 = false,
427 }; 427 };
@@ -660,6 +660,16 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
660 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); 660 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
661 } 661 }
662 662
663 VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
664 if (ext_depth_clip_control) {
665 depth_clip_control_features = {
666 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT,
667 .pNext = nullptr,
668 .depthClipControl = VK_TRUE,
669 };
670 SetNext(next, depth_clip_control_features);
671 }
672
663 VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; 673 VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
664 if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) { 674 if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) {
665 nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>(); 675 nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>();
@@ -1044,6 +1054,7 @@ void Device::CheckSuitability(bool requires_swapchain) const {
1044 std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"), 1054 std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"),
1045 std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess, 1055 std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess,
1046 "uniformAndStorageBuffer16BitAccess"), 1056 "uniformAndStorageBuffer16BitAccess"),
1057 std::make_pair(bit8_storage.storageBuffer8BitAccess, "storageBuffer8BitAccess"),
1047 std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess, 1058 std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess,
1048 "uniformAndStorageBuffer8BitAccess"), 1059 "uniformAndStorageBuffer8BitAccess"),
1049 std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"), 1060 std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"),
@@ -1083,6 +1094,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1083 bool has_ext_vertex_input_dynamic_state{}; 1094 bool has_ext_vertex_input_dynamic_state{};
1084 bool has_ext_line_rasterization{}; 1095 bool has_ext_line_rasterization{};
1085 bool has_ext_primitive_topology_list_restart{}; 1096 bool has_ext_primitive_topology_list_restart{};
1097 bool has_ext_depth_clip_control{};
1086 for (const std::string& extension : supported_extensions) { 1098 for (const std::string& extension : supported_extensions) {
1087 const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name, 1099 const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
1088 bool push) { 1100 bool push) {
@@ -1116,6 +1128,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1116 test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true); 1128 test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true);
1117 test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME, 1129 test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
1118 true); 1130 true);
1131 test(has_ext_depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, false);
1119 test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false); 1132 test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
1120 test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); 1133 test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
1121 test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); 1134 test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
@@ -1279,6 +1292,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1279 ext_line_rasterization = true; 1292 ext_line_rasterization = true;
1280 } 1293 }
1281 } 1294 }
1295 if (has_ext_depth_clip_control) {
1296 VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
1297 depth_clip_control_features.sType =
1298 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT;
1299 depth_clip_control_features.pNext = nullptr;
1300 features.pNext = &depth_clip_control_features;
1301 physical.GetFeatures2(features);
1302
1303 if (depth_clip_control_features.depthClipControl) {
1304 extensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
1305 ext_depth_clip_control = true;
1306 }
1307 }
1282 if (has_khr_workgroup_memory_explicit_layout) { 1308 if (has_khr_workgroup_memory_explicit_layout) {
1283 VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout; 1309 VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout;
1284 layout.sType = 1310 layout.sType =
@@ -1380,6 +1406,10 @@ void Device::SetupFeatures() {
1380 is_shader_storage_image_multisample = features.shaderStorageImageMultisample; 1406 is_shader_storage_image_multisample = features.shaderStorageImageMultisample;
1381 is_blit_depth_stencil_supported = TestDepthStencilBlits(); 1407 is_blit_depth_stencil_supported = TestDepthStencilBlits();
1382 is_optimal_astc_supported = IsOptimalAstcSupported(features); 1408 is_optimal_astc_supported = IsOptimalAstcSupported(features);
1409
1410 const VkPhysicalDeviceLimits& limits{properties.limits};
1411 max_vertex_input_attributes = limits.maxVertexInputAttributes;
1412 max_vertex_input_bindings = limits.maxVertexInputBindings;
1383} 1413}
1384 1414
1385void Device::SetupProperties() { 1415void Device::SetupProperties() {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index db802437c..6a26c4e6e 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -256,6 +256,11 @@ public:
256 return ext_depth_range_unrestricted; 256 return ext_depth_range_unrestricted;
257 } 257 }
258 258
259 /// Returns true if the device supports VK_EXT_depth_clip_control.
260 bool IsExtDepthClipControlSupported() const {
261 return ext_depth_clip_control;
262 }
263
259 /// Returns true if the device supports VK_EXT_shader_viewport_index_layer. 264 /// Returns true if the device supports VK_EXT_shader_viewport_index_layer.
260 bool IsExtShaderViewportIndexLayerSupported() const { 265 bool IsExtShaderViewportIndexLayerSupported() const {
261 return ext_shader_viewport_index_layer; 266 return ext_shader_viewport_index_layer;
@@ -368,6 +373,14 @@ public:
368 return must_emulate_bgr565; 373 return must_emulate_bgr565;
369 } 374 }
370 375
376 u32 GetMaxVertexInputAttributes() const {
377 return max_vertex_input_attributes;
378 }
379
380 u32 GetMaxVertexInputBindings() const {
381 return max_vertex_input_bindings;
382 }
383
371private: 384private:
372 /// Checks if the physical device is suitable. 385 /// Checks if the physical device is suitable.
373 void CheckSuitability(bool requires_swapchain) const; 386 void CheckSuitability(bool requires_swapchain) const;
@@ -446,6 +459,7 @@ private:
446 bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format. 459 bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format.
447 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. 460 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
448 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. 461 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
462 bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control
449 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 463 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
450 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 464 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
451 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. 465 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
@@ -467,6 +481,8 @@ private:
467 bool supports_d24_depth{}; ///< Supports D24 depth buffers. 481 bool supports_d24_depth{}; ///< Supports D24 depth buffers.
468 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. 482 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
469 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. 483 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
484 u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline
485 u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline
470 486
471 // Telemetry parameters 487 // Telemetry parameters
472 std::string vendor_name; ///< Device's driver name. 488 std::string vendor_name; ///< Device's driver name.
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 483b534a0..7dca7341c 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -314,6 +314,18 @@ const char* ToString(VkResult result) noexcept {
314 return "VK_ERROR_VALIDATION_FAILED_EXT"; 314 return "VK_ERROR_VALIDATION_FAILED_EXT";
315 case VkResult::VK_ERROR_INVALID_SHADER_NV: 315 case VkResult::VK_ERROR_INVALID_SHADER_NV:
316 return "VK_ERROR_INVALID_SHADER_NV"; 316 return "VK_ERROR_INVALID_SHADER_NV";
317 case VkResult::VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR:
318 return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR";
319 case VkResult::VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR:
320 return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR";
321 case VkResult::VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR:
322 return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR";
323 case VkResult::VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR:
324 return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR";
325 case VkResult::VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR:
326 return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR";
327 case VkResult::VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR:
328 return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR";
317 case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: 329 case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
318 return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; 330 return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
319 case VkResult::VK_ERROR_FRAGMENTATION_EXT: 331 case VkResult::VK_ERROR_FRAGMENTATION_EXT:
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 5b5b6fed8..3d560f303 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -44,32 +44,30 @@
44#include "yuzu/bootmanager.h" 44#include "yuzu/bootmanager.h"
45#include "yuzu/main.h" 45#include "yuzu/main.h"
46 46
47EmuThread::EmuThread(Core::System& system_) : system{system_} {} 47static Core::Frontend::WindowSystemType GetWindowSystemType();
48
49EmuThread::EmuThread(Core::System& system) : m_system{system} {}
48 50
49EmuThread::~EmuThread() = default; 51EmuThread::~EmuThread() = default;
50 52
51void EmuThread::run() { 53void EmuThread::run() {
52 std::string name = "EmuControlThread"; 54 const char* name = "EmuControlThread";
53 MicroProfileOnThreadCreate(name.c_str()); 55 MicroProfileOnThreadCreate(name);
54 Common::SetCurrentThreadName(name.c_str()); 56 Common::SetCurrentThreadName(name);
55 57
56 auto& gpu = system.GPU(); 58 auto& gpu = m_system.GPU();
57 auto stop_token = stop_source.get_token(); 59 auto stop_token = m_stop_source.get_token();
58 bool debugger_should_start = system.DebuggerEnabled();
59 60
60 system.RegisterHostThread(); 61 m_system.RegisterHostThread();
61 62
62 // Main process has been loaded. Make the context current to this thread and begin GPU and CPU 63 // Main process has been loaded. Make the context current to this thread and begin GPU and CPU
63 // execution. 64 // execution.
64 gpu.Start();
65
66 gpu.ObtainContext(); 65 gpu.ObtainContext();
67 66
68 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 67 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
69
70 if (Settings::values.use_disk_shader_cache.GetValue()) { 68 if (Settings::values.use_disk_shader_cache.GetValue()) {
71 system.Renderer().ReadRasterizer()->LoadDiskResources( 69 m_system.Renderer().ReadRasterizer()->LoadDiskResources(
72 system.GetCurrentProcessProgramID(), stop_token, 70 m_system.GetCurrentProcessProgramID(), stop_token,
73 [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { 71 [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
74 emit LoadProgress(stage, value, total); 72 emit LoadProgress(stage, value, total);
75 }); 73 });
@@ -77,53 +75,36 @@ void EmuThread::run() {
77 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); 75 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
78 76
79 gpu.ReleaseContext(); 77 gpu.ReleaseContext();
78 gpu.Start();
80 79
81 system.GetCpuManager().OnGpuReady(); 80 m_system.GetCpuManager().OnGpuReady();
82
83 // Holds whether the cpu was running during the last iteration,
84 // so that the DebugModeLeft signal can be emitted before the
85 // next execution step
86 bool was_active = false;
87 while (!stop_token.stop_requested()) {
88 if (running) {
89 if (was_active) {
90 emit DebugModeLeft();
91 }
92
93 running_guard = true;
94 Core::SystemResultStatus result = system.Run();
95 if (result != Core::SystemResultStatus::Success) {
96 running_guard = false;
97 this->SetRunning(false);
98 emit ErrorThrown(result, system.GetStatusDetails());
99 }
100 81
101 if (debugger_should_start) { 82 if (m_system.DebuggerEnabled()) {
102 system.InitializeDebugger(); 83 m_system.InitializeDebugger();
103 debugger_should_start = false; 84 }
104 }
105 85
106 running_wait.Wait(); 86 while (!stop_token.stop_requested()) {
107 result = system.Pause(); 87 std::unique_lock lk{m_should_run_mutex};
108 if (result != Core::SystemResultStatus::Success) { 88 if (m_should_run) {
109 running_guard = false; 89 m_system.Run();
110 this->SetRunning(false); 90 m_is_running.store(true);
111 emit ErrorThrown(result, system.GetStatusDetails()); 91 m_is_running.notify_all();
112 }
113 running_guard = false;
114 92
115 if (!stop_token.stop_requested()) { 93 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; });
116 was_active = true;
117 emit DebugModeEntered();
118 }
119 } else { 94 } else {
120 std::unique_lock lock{running_mutex}; 95 m_system.Pause();
121 Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); }); 96 m_is_running.store(false);
97 m_is_running.notify_all();
98
99 emit DebugModeEntered();
100 Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; });
101 emit DebugModeLeft();
122 } 102 }
123 } 103 }
124 104
125 // Shutdown the main emulated process 105 // Shutdown the main emulated process
126 system.ShutdownMainProcess(); 106 m_system.DetachDebugger();
107 m_system.ShutdownMainProcess();
127 108
128#if MICROPROFILE_ENABLED 109#if MICROPROFILE_ENABLED
129 MicroProfileOnThreadExit(); 110 MicroProfileOnThreadExit();
@@ -225,6 +206,9 @@ public:
225 explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { 206 explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
226 setAttribute(Qt::WA_NativeWindow); 207 setAttribute(Qt::WA_NativeWindow);
227 setAttribute(Qt::WA_PaintOnScreen); 208 setAttribute(Qt::WA_PaintOnScreen);
209 if (GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) {
210 setAttribute(Qt::WA_DontCreateNativeAncestors);
211 }
228 } 212 }
229 213
230 virtual ~RenderWidget() = default; 214 virtual ~RenderWidget() = default;
@@ -269,12 +253,14 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() {
269 return Core::Frontend::WindowSystemType::X11; 253 return Core::Frontend::WindowSystemType::X11;
270 else if (platform_name == QStringLiteral("wayland")) 254 else if (platform_name == QStringLiteral("wayland"))
271 return Core::Frontend::WindowSystemType::Wayland; 255 return Core::Frontend::WindowSystemType::Wayland;
256 else if (platform_name == QStringLiteral("wayland-egl"))
257 return Core::Frontend::WindowSystemType::Wayland;
272 else if (platform_name == QStringLiteral("cocoa")) 258 else if (platform_name == QStringLiteral("cocoa"))
273 return Core::Frontend::WindowSystemType::Cocoa; 259 return Core::Frontend::WindowSystemType::Cocoa;
274 else if (platform_name == QStringLiteral("android")) 260 else if (platform_name == QStringLiteral("android"))
275 return Core::Frontend::WindowSystemType::Android; 261 return Core::Frontend::WindowSystemType::Android;
276 262
277 LOG_CRITICAL(Frontend, "Unknown Qt platform!"); 263 LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString());
278 return Core::Frontend::WindowSystemType::Windows; 264 return Core::Frontend::WindowSystemType::Windows;
279} 265}
280 266
@@ -314,6 +300,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
314 input_subsystem->Initialize(); 300 input_subsystem->Initialize();
315 this->setMouseTracking(true); 301 this->setMouseTracking(true);
316 302
303 strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") ||
304 QGuiApplication::platformName() == QStringLiteral("wayland-egl");
305
317 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 306 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
318 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, 307 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
319 Qt::QueuedConnection); 308 Qt::QueuedConnection);
@@ -750,6 +739,9 @@ void GRenderWindow::InitializeCamera() {
750 return; 739 return;
751 } 740 }
752 741
742 const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
743 const auto camera_height = input_subsystem->GetCamera()->getImageHeight();
744 camera_data.resize(camera_width * camera_height);
753 camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer); 745 camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);
754 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, 746 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
755 &GRenderWindow::OnCameraCapture); 747 &GRenderWindow::OnCameraCapture);
@@ -805,17 +797,22 @@ void GRenderWindow::RequestCameraCapture() {
805} 797}
806 798
807void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { 799void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
808 constexpr std::size_t camera_width = 320; 800#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
809 constexpr std::size_t camera_height = 240; 801 // TODO: Capture directly in the format and resolution needed
802 const auto camera_width = input_subsystem->GetCamera()->getImageWidth();
803 const auto camera_height = input_subsystem->GetCamera()->getImageHeight();
810 const auto converted = 804 const auto converted =
811 img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio, 805 img.scaled(static_cast<int>(camera_width), static_cast<int>(camera_height),
806 Qt::AspectRatioMode::IgnoreAspectRatio,
812 Qt::TransformationMode::SmoothTransformation) 807 Qt::TransformationMode::SmoothTransformation)
813 .mirrored(false, true); 808 .mirrored(false, true);
814 std::vector<u32> camera_data{}; 809 if (camera_data.size() != camera_width * camera_height) {
815 camera_data.resize(camera_width * camera_height); 810 camera_data.resize(camera_width * camera_height);
811 }
816 std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32)); 812 std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32));
817 input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data); 813 input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data);
818 pending_camera_snapshots = 0; 814 pending_camera_snapshots = 0;
815#endif
819} 816}
820 817
821bool GRenderWindow::event(QEvent* event) { 818bool GRenderWindow::event(QEvent* event) {
@@ -952,6 +949,12 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
952 949
953bool GRenderWindow::InitializeOpenGL() { 950bool GRenderWindow::InitializeOpenGL() {
954#ifdef HAS_OPENGL 951#ifdef HAS_OPENGL
952 if (!QOpenGLContext::supportsThreadedOpenGL()) {
953 QMessageBox::warning(this, tr("OpenGL not available!"),
954 tr("OpenGL shared contexts are not supported."));
955 return false;
956 }
957
955 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, 958 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
956 // WA_DontShowOnScreen, WA_DeleteOnClose 959 // WA_DontShowOnScreen, WA_DeleteOnClose
957 auto child = new OpenGLRenderWidget(this); 960 auto child = new OpenGLRenderWidget(this);
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index f4deae4ee..eca16b313 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -47,7 +47,7 @@ class EmuThread final : public QThread {
47 Q_OBJECT 47 Q_OBJECT
48 48
49public: 49public:
50 explicit EmuThread(Core::System& system_); 50 explicit EmuThread(Core::System& system);
51 ~EmuThread() override; 51 ~EmuThread() override;
52 52
53 /** 53 /**
@@ -57,48 +57,48 @@ public:
57 void run() override; 57 void run() override;
58 58
59 /** 59 /**
60 * Sets whether the emulation thread is running or not 60 * Sets whether the emulation thread should run or not
61 * @param running_ Boolean value, set the emulation thread to running if true 61 * @param should_run Boolean value, set the emulation thread to running if true
62 * @note This function is thread-safe
63 */ 62 */
64 void SetRunning(bool running_) { 63 void SetRunning(bool should_run) {
65 std::unique_lock lock{running_mutex}; 64 // TODO: Prevent other threads from modifying the state until we finish.
66 running = running_; 65 {
67 lock.unlock(); 66 // Notify the running thread to change state.
68 running_cv.notify_all(); 67 std::unique_lock run_lk{m_should_run_mutex};
69 if (!running) { 68 m_should_run = should_run;
70 running_wait.Set(); 69 m_should_run_cv.notify_one();
71 /// Wait until effectively paused 70 }
72 while (running_guard) 71
73 ; 72 // Wait until paused, if pausing.
73 if (!should_run) {
74 m_is_running.wait(true);
74 } 75 }
75 } 76 }
76 77
77 /** 78 /**
78 * Check if the emulation thread is running or not 79 * Check if the emulation thread is running or not
79 * @return True if the emulation thread is running, otherwise false 80 * @return True if the emulation thread is running, otherwise false
80 * @note This function is thread-safe
81 */ 81 */
82 bool IsRunning() const { 82 bool IsRunning() const {
83 return running; 83 return m_is_running.load() || m_should_run;
84 } 84 }
85 85
86 /** 86 /**
87 * Requests for the emulation thread to stop running 87 * Requests for the emulation thread to immediately stop running
88 */ 88 */
89 void RequestStop() { 89 void ForceStop() {
90 stop_source.request_stop(); 90 LOG_WARNING(Frontend, "Force stopping EmuThread");
91 SetRunning(false); 91 m_stop_source.request_stop();
92 } 92 }
93 93
94private: 94private:
95 bool running = false; 95 Core::System& m_system;
96 std::stop_source stop_source; 96
97 std::mutex running_mutex; 97 std::stop_source m_stop_source;
98 std::condition_variable_any running_cv; 98 std::mutex m_should_run_mutex;
99 Common::Event running_wait{}; 99 std::condition_variable_any m_should_run_cv;
100 std::atomic_bool running_guard{false}; 100 std::atomic<bool> m_is_running{false};
101 Core::System& system; 101 bool m_should_run{true};
102 102
103signals: 103signals:
104 /** 104 /**
@@ -119,8 +119,6 @@ signals:
119 */ 119 */
120 void DebugModeLeft(); 120 void DebugModeLeft();
121 121
122 void ErrorThrown(Core::SystemResultStatus, std::string);
123
124 void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); 122 void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
125}; 123};
126 124
@@ -241,13 +239,14 @@ private:
241 bool first_frame = false; 239 bool first_frame = false;
242 InputCommon::TasInput::TasState last_tas_state; 240 InputCommon::TasInput::TasState last_tas_state;
243 241
242#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
244 bool is_virtual_camera; 243 bool is_virtual_camera;
245 int pending_camera_snapshots; 244 int pending_camera_snapshots;
246#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA 245 std::vector<u32> camera_data;
247 std::unique_ptr<QCamera> camera; 246 std::unique_ptr<QCamera> camera;
248 std::unique_ptr<QCameraImageCapture> camera_capture; 247 std::unique_ptr<QCameraImageCapture> camera_capture;
249#endif
250 std::unique_ptr<QTimer> camera_timer; 248 std::unique_ptr<QTimer> camera_timer;
249#endif
251 250
252 Core::System& system; 251 Core::System& system;
253 252
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 722fc708e..2ea4f367b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -697,7 +697,6 @@ void Config::ReadRendererValues() {
697 ReadGlobalSetting(Settings::values.fsr_sharpening_slider); 697 ReadGlobalSetting(Settings::values.fsr_sharpening_slider);
698 ReadGlobalSetting(Settings::values.anti_aliasing); 698 ReadGlobalSetting(Settings::values.anti_aliasing);
699 ReadGlobalSetting(Settings::values.max_anisotropy); 699 ReadGlobalSetting(Settings::values.max_anisotropy);
700 ReadGlobalSetting(Settings::values.use_speed_limit);
701 ReadGlobalSetting(Settings::values.speed_limit); 700 ReadGlobalSetting(Settings::values.speed_limit);
702 ReadGlobalSetting(Settings::values.use_disk_shader_cache); 701 ReadGlobalSetting(Settings::values.use_disk_shader_cache);
703 ReadGlobalSetting(Settings::values.gpu_accuracy); 702 ReadGlobalSetting(Settings::values.gpu_accuracy);
@@ -796,6 +795,7 @@ void Config::ReadSystemValues() {
796 } else { 795 } else {
797 Settings::values.custom_rtc = std::nullopt; 796 Settings::values.custom_rtc = std::nullopt;
798 } 797 }
798 ReadBasicSetting(Settings::values.device_name);
799 } 799 }
800 800
801 ReadGlobalSetting(Settings::values.sound_index); 801 ReadGlobalSetting(Settings::values.sound_index);
@@ -1328,7 +1328,6 @@ void Config::SaveRendererValues() {
1328 static_cast<u32>(Settings::values.anti_aliasing.GetDefault()), 1328 static_cast<u32>(Settings::values.anti_aliasing.GetDefault()),
1329 Settings::values.anti_aliasing.UsingGlobal()); 1329 Settings::values.anti_aliasing.UsingGlobal());
1330 WriteGlobalSetting(Settings::values.max_anisotropy); 1330 WriteGlobalSetting(Settings::values.max_anisotropy);
1331 WriteGlobalSetting(Settings::values.use_speed_limit);
1332 WriteGlobalSetting(Settings::values.speed_limit); 1331 WriteGlobalSetting(Settings::values.speed_limit);
1333 WriteGlobalSetting(Settings::values.use_disk_shader_cache); 1332 WriteGlobalSetting(Settings::values.use_disk_shader_cache);
1334 WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), 1333 WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
@@ -1415,6 +1414,7 @@ void Config::SaveSystemValues() {
1415 false); 1414 false);
1416 WriteSetting(QStringLiteral("custom_rtc"), 1415 WriteSetting(QStringLiteral("custom_rtc"),
1417 QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0); 1416 QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0);
1417 WriteBasicSetting(Settings::values.device_name);
1418 } 1418 }
1419 1419
1420 WriteGlobalSetting(Settings::values.sound_index); 1420 WriteGlobalSetting(Settings::values.sound_index);
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index b1575b0d3..183cbe562 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -738,13 +738,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
738 738
739 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, 739 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
740 &ConfigureInputPlayer::UpdateMappingWithDefaults); 740 &ConfigureInputPlayer::UpdateMappingWithDefaults);
741 ui->comboDevices->installEventFilter(this);
741 742
742 ui->comboDevices->setCurrentIndex(-1); 743 ui->comboDevices->setCurrentIndex(-1);
743 744
744 ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
745 connect(ui->buttonRefreshDevices, &QPushButton::clicked,
746 [this] { emit RefreshInputDevices(); });
747
748 timeout_timer->setSingleShot(true); 745 timeout_timer->setSingleShot(true);
749 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 746 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
750 747
@@ -1479,6 +1476,13 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1479 } 1476 }
1480} 1477}
1481 1478
1479bool ConfigureInputPlayer::eventFilter(QObject* object, QEvent* event) {
1480 if (object == ui->comboDevices && event->type() == QEvent::MouseButtonPress) {
1481 RefreshInputDevices();
1482 }
1483 return object->eventFilter(object, event);
1484}
1485
1482void ConfigureInputPlayer::CreateProfile() { 1486void ConfigureInputPlayer::CreateProfile() {
1483 const auto profile_name = 1487 const auto profile_name =
1484 LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 30, 1488 LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 30,
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 26f60d121..6d1876f2b 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -119,6 +119,9 @@ private:
119 /// Handle key press events. 119 /// Handle key press events.
120 void keyPressEvent(QKeyEvent* event) override; 120 void keyPressEvent(QKeyEvent* event) override;
121 121
122 /// Handle combobox list refresh
123 bool eventFilter(QObject* object, QEvent* event) override;
124
122 /// Update UI to reflect current configuration. 125 /// Update UI to reflect current configuration.
123 void UpdateUI(); 126 void UpdateUI();
124 127
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index a62b57501..a9567c6ee 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -122,25 +122,6 @@
122 </property> 122 </property>
123 </widget> 123 </widget>
124 </item> 124 </item>
125 <item>
126 <widget class="QPushButton" name="buttonRefreshDevices">
127 <property name="minimumSize">
128 <size>
129 <width>21</width>
130 <height>21</height>
131 </size>
132 </property>
133 <property name="maximumSize">
134 <size>
135 <width>21</width>
136 <height>21</height>
137 </size>
138 </property>
139 <property name="styleSheet">
140 <string notr="true"/>
141 </property>
142 </widget>
143 </item>
144 </layout> 125 </layout>
145 </widget> 126 </widget>
146 </item> 127 </item>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index bc9d9d77a..9b14e5903 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -72,6 +72,8 @@ void ConfigureSystem::SetConfiguration() {
72 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); 72 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value());
73 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); 73 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value());
74 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); 74 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time));
75 ui->device_name_edit->setText(
76 QString::fromUtf8(Settings::values.device_name.GetValue().c_str()));
75 77
76 if (Settings::IsConfiguringGlobal()) { 78 if (Settings::IsConfiguringGlobal()) {
77 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); 79 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
@@ -115,6 +117,8 @@ void ConfigureSystem::ApplyConfiguration() {
115 } 117 }
116 } 118 }
117 119
120 Settings::values.device_name = ui->device_name_edit->text().toStdString();
121
118 if (!enabled) { 122 if (!enabled) {
119 return; 123 return;
120 } 124 }
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index b234ea87b..46892f5c1 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -432,6 +432,13 @@
432 </property> 432 </property>
433 </widget> 433 </widget>
434 </item> 434 </item>
435 <item row="7" column="0">
436 <widget class="QLabel" name="device_name_label">
437 <property name="text">
438 <string>Device Name</string>
439 </property>
440 </widget>
441 </item>
435 <item row="3" column="1"> 442 <item row="3" column="1">
436 <widget class="QComboBox" name="combo_sound"> 443 <widget class="QComboBox" name="combo_sound">
437 <item> 444 <item>
@@ -476,6 +483,13 @@
476 </property> 483 </property>
477 </widget> 484 </widget>
478 </item> 485 </item>
486 <item row="7" column="1">
487 <widget class="QLineEdit" name="device_name_edit">
488 <property name="maxLength">
489 <number>128</number>
490 </property>
491 </widget>
492 </item>
479 <item row="6" column="1"> 493 <item row="6" column="1">
480 <widget class="QLineEdit" name="rng_seed_edit"> 494 <widget class="QLineEdit" name="rng_seed_edit">
481 <property name="sizePolicy"> 495 <property name="sizePolicy">
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 5c33c1b0f..22aa19c56 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -554,6 +554,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
554 QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); 554 QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
555 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 555 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
556 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 556 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
557#ifndef WIN32
558 QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut"));
559 QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop"));
560 QAction* create_applications_menu_shortcut =
561 shortcut_menu->addAction(tr("Add to Applications Menu"));
562#endif
557 context_menu.addSeparator(); 563 context_menu.addSeparator();
558 QAction* properties = context_menu.addAction(tr("Properties")); 564 QAction* properties = context_menu.addAction(tr("Properties"));
559 565
@@ -619,6 +625,14 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
619 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { 625 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
620 emit NavigateToGamedbEntryRequested(program_id, compatibility_list); 626 emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
621 }); 627 });
628#ifndef WIN32
629 connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() {
630 emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop);
631 });
632 connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() {
633 emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications);
634 });
635#endif
622 connect(properties, &QAction::triggered, 636 connect(properties, &QAction::triggered,
623 [this, path]() { emit OpenPerGameGeneralRequested(path); }); 637 [this, path]() { emit OpenPerGameGeneralRequested(path); });
624}; 638};
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index cdf085019..f7ff93ed9 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -52,6 +52,11 @@ enum class DumpRomFSTarget {
52 SDMC, 52 SDMC,
53}; 53};
54 54
55enum class GameListShortcutTarget {
56 Desktop,
57 Applications,
58};
59
55enum class InstalledEntryType { 60enum class InstalledEntryType {
56 Game, 61 Game,
57 Update, 62 Update,
@@ -108,6 +113,8 @@ signals:
108 const std::string& game_path); 113 const std::string& game_path);
109 void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); 114 void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
110 void CopyTIDRequested(u64 program_id); 115 void CopyTIDRequested(u64 program_id);
116 void CreateShortcut(u64 program_id, const std::string& game_path,
117 GameListShortcutTarget target);
111 void NavigateToGamedbEntryRequested(u64 program_id, 118 void NavigateToGamedbEntryRequested(u64 program_id,
112 const CompatibilityList& compatibility_list); 119 const CompatibilityList& compatibility_list);
113 void OpenPerGameGeneralRequested(const std::string& file); 120 void OpenPerGameGeneralRequested(const std::string& file);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b11b26f7b..524650144 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -4,6 +4,8 @@
4#include <cinttypes> 4#include <cinttypes>
5#include <clocale> 5#include <clocale>
6#include <cmath> 6#include <cmath>
7#include <fstream>
8#include <iostream>
7#include <memory> 9#include <memory>
8#include <thread> 10#include <thread>
9#ifdef __APPLE__ 11#ifdef __APPLE__
@@ -1249,6 +1251,7 @@ void GMainWindow::ConnectWidgetEvents() {
1249 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); 1251 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
1250 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, 1252 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
1251 &GMainWindow::OnGameListNavigateToGamedbEntry); 1253 &GMainWindow::OnGameListNavigateToGamedbEntry);
1254 connect(game_list, &GameList::CreateShortcut, this, &GMainWindow::OnGameListCreateShortcut);
1252 connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); 1255 connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory);
1253 connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, 1256 connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this,
1254 &GMainWindow::OnGameListAddDirectory); 1257 &GMainWindow::OnGameListAddDirectory);
@@ -1495,7 +1498,7 @@ void GMainWindow::SetupSigInterrupts() {
1495 1498
1496void GMainWindow::HandleSigInterrupt(int sig) { 1499void GMainWindow::HandleSigInterrupt(int sig) {
1497 if (sig == SIGINT) { 1500 if (sig == SIGINT) {
1498 exit(1); 1501 _exit(1);
1499 } 1502 }
1500 1503
1501 // Calling into Qt directly from a signal handler is not safe, 1504 // Calling into Qt directly from a signal handler is not safe,
@@ -1547,8 +1550,9 @@ void GMainWindow::AllowOSSleep() {
1547 1550
1548bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) { 1551bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) {
1549 // Shutdown previous session if the emu thread is still active... 1552 // Shutdown previous session if the emu thread is still active...
1550 if (emu_thread != nullptr) 1553 if (emu_thread != nullptr) {
1551 ShutdownGame(); 1554 ShutdownGame();
1555 }
1552 1556
1553 if (!render_window->InitRenderTarget()) { 1557 if (!render_window->InitRenderTarget()) {
1554 return false; 1558 return false;
@@ -1707,8 +1711,10 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1707 system->RegisterExecuteProgramCallback( 1711 system->RegisterExecuteProgramCallback(
1708 [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); 1712 [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
1709 1713
1710 // Register an Exit callback such that Core can exit the currently running application. 1714 system->RegisterExitCallback([this] {
1711 system->RegisterExitCallback([this]() { render_window->Exit(); }); 1715 emu_thread->ForceStop();
1716 render_window->Exit();
1717 });
1712 1718
1713 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 1719 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
1714 connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); 1720 connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
@@ -1779,9 +1785,9 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1779 OnStartGame(); 1785 OnStartGame();
1780} 1786}
1781 1787
1782void GMainWindow::ShutdownGame() { 1788bool GMainWindow::OnShutdownBegin() {
1783 if (!emulation_running) { 1789 if (!emulation_running) {
1784 return; 1790 return false;
1785 } 1791 }
1786 1792
1787 if (ui->action_Fullscreen->isChecked()) { 1793 if (ui->action_Fullscreen->isChecked()) {
@@ -1790,17 +1796,58 @@ void GMainWindow::ShutdownGame() {
1790 1796
1791 AllowOSSleep(); 1797 AllowOSSleep();
1792 1798
1799 // Disable unlimited frame rate
1800 Settings::values.use_speed_limit.SetValue(true);
1801
1802 if (system->IsShuttingDown()) {
1803 return false;
1804 }
1805
1793 system->SetShuttingDown(true); 1806 system->SetShuttingDown(true);
1794 system->DetachDebugger();
1795 discord_rpc->Pause(); 1807 discord_rpc->Pause();
1796 emu_thread->RequestStop(); 1808
1809 RequestGameExit();
1810 emu_thread->disconnect();
1811 emu_thread->SetRunning(true);
1797 1812
1798 emit EmulationStopping(); 1813 emit EmulationStopping();
1799 1814
1800 // Wait for emulation thread to complete and delete it 1815 shutdown_timer.setSingleShot(true);
1816 shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000);
1817 connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired);
1818 connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped);
1819
1820 // Disable everything to prevent anything from being triggered here
1821 ui->action_Pause->setEnabled(false);
1822 ui->action_Restart->setEnabled(false);
1823 ui->action_Stop->setEnabled(false);
1824
1825 return true;
1826}
1827
1828void GMainWindow::OnShutdownBeginDialog() {
1829 shutdown_dialog = new OverlayDialog(this, *system, QString{}, tr("Closing software..."),
1830 QString{}, QString{}, Qt::AlignHCenter | Qt::AlignVCenter);
1831 shutdown_dialog->open();
1832}
1833
1834void GMainWindow::OnEmulationStopTimeExpired() {
1835 if (emu_thread) {
1836 emu_thread->ForceStop();
1837 }
1838}
1839
1840void GMainWindow::OnEmulationStopped() {
1841 shutdown_timer.stop();
1842 emu_thread->disconnect();
1801 emu_thread->wait(); 1843 emu_thread->wait();
1802 emu_thread = nullptr; 1844 emu_thread = nullptr;
1803 1845
1846 if (shutdown_dialog) {
1847 shutdown_dialog->deleteLater();
1848 shutdown_dialog = nullptr;
1849 }
1850
1804 emulation_running = false; 1851 emulation_running = false;
1805 1852
1806 discord_rpc->Update(); 1853 discord_rpc->Update();
@@ -1846,6 +1893,20 @@ void GMainWindow::ShutdownGame() {
1846 1893
1847 // When closing the game, destroy the GLWindow to clear the context after the game is closed 1894 // When closing the game, destroy the GLWindow to clear the context after the game is closed
1848 render_window->ReleaseRenderTarget(); 1895 render_window->ReleaseRenderTarget();
1896
1897 Settings::RestoreGlobalState(system->IsPoweredOn());
1898 system->HIDCore().ReloadInputDevices();
1899 UpdateStatusButtons();
1900}
1901
1902void GMainWindow::ShutdownGame() {
1903 if (!emulation_running) {
1904 return;
1905 }
1906
1907 OnShutdownBegin();
1908 OnEmulationStopTimeExpired();
1909 OnEmulationStopped();
1849} 1910}
1850 1911
1851void GMainWindow::StoreRecentFile(const QString& filename) { 1912void GMainWindow::StoreRecentFile(const QString& filename) {
@@ -2375,6 +2436,152 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
2375 QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); 2436 QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory));
2376} 2437}
2377 2438
2439void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
2440 GameListShortcutTarget target) {
2441 // Get path to yuzu executable
2442 const QStringList args = QApplication::arguments();
2443 std::filesystem::path yuzu_command = args[0].toStdString();
2444
2445#if defined(__linux__) || defined(__FreeBSD__)
2446 // If relative path, make it an absolute path
2447 if (yuzu_command.c_str()[0] == '.') {
2448 yuzu_command = Common::FS::GetCurrentDir() / yuzu_command;
2449 }
2450
2451#if defined(__linux__)
2452 // Warn once if we are making a shortcut to a volatile AppImage
2453 const std::string appimage_ending =
2454 std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage");
2455 if (yuzu_command.string().ends_with(appimage_ending) &&
2456 !UISettings::values.shortcut_already_warned) {
2457 if (QMessageBox::warning(this, tr("Create Shortcut"),
2458 tr("This will create a shortcut to the current AppImage. This may "
2459 "not work well if you update. Continue?"),
2460 QMessageBox::StandardButton::Ok |
2461 QMessageBox::StandardButton::Cancel) ==
2462 QMessageBox::StandardButton::Cancel) {
2463 return;
2464 }
2465 UISettings::values.shortcut_already_warned = true;
2466 }
2467#endif // __linux__
2468#endif // __linux__ || __FreeBSD__
2469
2470 std::filesystem::path target_directory{};
2471 // Determine target directory for shortcut
2472#if defined(__linux__) || defined(__FreeBSD__)
2473 const char* home = std::getenv("HOME");
2474 const std::filesystem::path home_path = (home == nullptr ? "~" : home);
2475 const char* xdg_data_home = std::getenv("XDG_DATA_HOME");
2476
2477 if (target == GameListShortcutTarget::Desktop) {
2478 target_directory = home_path / "Desktop";
2479 if (!Common::FS::IsDir(target_directory)) {
2480 QMessageBox::critical(
2481 this, tr("Create Shortcut"),
2482 tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.")
2483 .arg(QString::fromStdString(target_directory)),
2484 QMessageBox::StandardButton::Ok);
2485 return;
2486 }
2487 } else if (target == GameListShortcutTarget::Applications) {
2488 target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) /
2489 "applications";
2490 if (!Common::FS::CreateDirs(target_directory)) {
2491 QMessageBox::critical(this, tr("Create Shortcut"),
2492 tr("Cannot create shortcut in applications menu. Path \"%1\" "
2493 "does not exist and cannot be created.")
2494 .arg(QString::fromStdString(target_directory)),
2495 QMessageBox::StandardButton::Ok);
2496 return;
2497 }
2498 }
2499#endif
2500
2501 const std::string game_file_name = std::filesystem::path(game_path).filename().string();
2502 // Determine full paths for icon and shortcut
2503#if defined(__linux__) || defined(__FreeBSD__)
2504 std::filesystem::path system_icons_path =
2505 (xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) /
2506 "icons/hicolor/256x256";
2507 if (!Common::FS::CreateDirs(system_icons_path)) {
2508 QMessageBox::critical(
2509 this, tr("Create Icon"),
2510 tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
2511 .arg(QString::fromStdString(system_icons_path)),
2512 QMessageBox::StandardButton::Ok);
2513 return;
2514 }
2515 std::filesystem::path icon_path =
2516 system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name)
2517 : fmt::format("yuzu-{:016X}.png", program_id));
2518 const std::filesystem::path shortcut_path =
2519 target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name)
2520 : fmt::format("yuzu-{:016X}.desktop", program_id));
2521#else
2522 const std::filesystem::path icon_path{};
2523 const std::filesystem::path shortcut_path{};
2524#endif
2525
2526 // Get title from game file
2527 const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
2528 system->GetContentProvider()};
2529 const auto control = pm.GetControlMetadata();
2530 const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
2531
2532 std::string title{fmt::format("{:016X}", program_id)};
2533
2534 if (control.first != nullptr) {
2535 title = control.first->GetApplicationName();
2536 } else {
2537 loader->ReadTitle(title);
2538 }
2539
2540 // Get icon from game file
2541 std::vector<u8> icon_image_file{};
2542 if (control.second != nullptr) {
2543 icon_image_file = control.second->ReadAllBytes();
2544 } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
2545 LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
2546 }
2547
2548 QImage icon_jpeg =
2549 QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
2550#if defined(__linux__) || defined(__FreeBSD__)
2551 // Convert and write the icon as a PNG
2552 if (!icon_jpeg.save(QString::fromStdString(icon_path.string()))) {
2553 LOG_ERROR(Frontend, "Could not write icon as PNG to file");
2554 } else {
2555 LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
2556 }
2557#endif // __linux__
2558
2559#if defined(__linux__) || defined(__FreeBSD__)
2560 const std::string comment =
2561 tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString();
2562 const std::string arguments = fmt::format("-g \"{:s}\"", game_path);
2563 const std::string categories = "Game;Emulator;Qt;";
2564 const std::string keywords = "Switch;Nintendo;";
2565#else
2566 const std::string comment{};
2567 const std::string arguments{};
2568 const std::string categories{};
2569 const std::string keywords{};
2570#endif
2571 if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(),
2572 yuzu_command.string(), arguments, categories, keywords)) {
2573 QMessageBox::critical(this, tr("Create Shortcut"),
2574 tr("Failed to create a shortcut at %1")
2575 .arg(QString::fromStdString(shortcut_path.string())));
2576 return;
2577 }
2578
2579 LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string());
2580 QMessageBox::information(
2581 this, tr("Create Shortcut"),
2582 tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title)));
2583}
2584
2378void GMainWindow::OnGameListOpenDirectory(const QString& directory) { 2585void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
2379 std::filesystem::path fs_path; 2586 std::filesystem::path fs_path;
2380 if (directory == QStringLiteral("SDMC")) { 2587 if (directory == QStringLiteral("SDMC")) {
@@ -2508,6 +2715,9 @@ void GMainWindow::OnMenuInstallToNAND() {
2508 return; 2715 return;
2509 } 2716 }
2510 2717
2718 // Save folder location of the first selected file
2719 UISettings::values.roms_path = QFileInfo(filenames[0]).path();
2720
2511 int remaining = filenames.size(); 2721 int remaining = filenames.size();
2512 2722
2513 // This would only overflow above 2^43 bytes (8.796 TB) 2723 // This would only overflow above 2^43 bytes (8.796 TB)
@@ -2763,8 +2973,6 @@ void GMainWindow::OnStartGame() {
2763 2973
2764 emu_thread->SetRunning(true); 2974 emu_thread->SetRunning(true);
2765 2975
2766 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
2767
2768 UpdateMenuState(); 2976 UpdateMenuState();
2769 OnTasStateChanged(); 2977 OnTasStateChanged();
2770 2978
@@ -2801,11 +3009,9 @@ void GMainWindow::OnStopGame() {
2801 return; 3009 return;
2802 } 3010 }
2803 3011
2804 ShutdownGame(); 3012 if (OnShutdownBegin()) {
2805 3013 OnShutdownBeginDialog();
2806 Settings::RestoreGlobalState(system->IsPoweredOn()); 3014 }
2807 system->HIDCore().ReloadInputDevices();
2808 UpdateStatusButtons();
2809} 3015}
2810 3016
2811void GMainWindow::OnLoadComplete() { 3017void GMainWindow::OnLoadComplete() {
@@ -2912,9 +3118,15 @@ static QScreen* GuessCurrentScreen(QWidget* window) {
2912 }); 3118 });
2913} 3119}
2914 3120
3121bool GMainWindow::UsingExclusiveFullscreen() {
3122 return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive ||
3123 QGuiApplication::platformName() == QStringLiteral("wayland") ||
3124 QGuiApplication::platformName() == QStringLiteral("wayland-egl");
3125}
3126
2915void GMainWindow::ShowFullscreen() { 3127void GMainWindow::ShowFullscreen() {
2916 const auto show_fullscreen = [](QWidget* window) { 3128 const auto show_fullscreen = [this](QWidget* window) {
2917 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { 3129 if (UsingExclusiveFullscreen()) {
2918 window->showFullScreen(); 3130 window->showFullScreen();
2919 return; 3131 return;
2920 } 3132 }
@@ -2942,7 +3154,7 @@ void GMainWindow::ShowFullscreen() {
2942 3154
2943void GMainWindow::HideFullscreen() { 3155void GMainWindow::HideFullscreen() {
2944 if (ui->action_Single_Window_Mode->isChecked()) { 3156 if (ui->action_Single_Window_Mode->isChecked()) {
2945 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { 3157 if (UsingExclusiveFullscreen()) {
2946 showNormal(); 3158 showNormal();
2947 restoreGeometry(UISettings::values.geometry); 3159 restoreGeometry(UISettings::values.geometry);
2948 } else { 3160 } else {
@@ -2956,7 +3168,7 @@ void GMainWindow::HideFullscreen() {
2956 statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); 3168 statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
2957 ui->menubar->show(); 3169 ui->menubar->show();
2958 } else { 3170 } else {
2959 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { 3171 if (UsingExclusiveFullscreen()) {
2960 render_window->showNormal(); 3172 render_window->showNormal();
2961 render_window->restoreGeometry(UISettings::values.renderwindow_geometry); 3173 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
2962 } else { 3174 } else {
@@ -3293,6 +3505,38 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
3293 } 3505 }
3294} 3506}
3295 3507
3508bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title,
3509 const std::string& comment, const std::string& icon_path,
3510 const std::string& command, const std::string& arguments,
3511 const std::string& categories, const std::string& keywords) {
3512#if defined(__linux__) || defined(__FreeBSD__)
3513 // This desktop file template was writting referencing
3514 // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html
3515 std::string shortcut_contents{};
3516 shortcut_contents.append("[Desktop Entry]\n");
3517 shortcut_contents.append("Type=Application\n");
3518 shortcut_contents.append("Version=1.0\n");
3519 shortcut_contents.append(fmt::format("Name={:s}\n", title));
3520 shortcut_contents.append(fmt::format("Comment={:s}\n", comment));
3521 shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path));
3522 shortcut_contents.append(fmt::format("TryExec={:s}\n", command));
3523 shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments));
3524 shortcut_contents.append(fmt::format("Categories={:s}\n", categories));
3525 shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords));
3526
3527 std::ofstream shortcut_stream(shortcut_path);
3528 if (!shortcut_stream.is_open()) {
3529 LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path);
3530 return false;
3531 }
3532 shortcut_stream << shortcut_contents;
3533 shortcut_stream.close();
3534
3535 return true;
3536#endif
3537 return false;
3538}
3539
3296void GMainWindow::OnLoadAmiibo() { 3540void GMainWindow::OnLoadAmiibo() {
3297 if (emu_thread == nullptr || !emu_thread->IsRunning()) { 3541 if (emu_thread == nullptr || !emu_thread->IsRunning()) {
3298 return; 3542 return;
@@ -3710,79 +3954,6 @@ void GMainWindow::OnMouseActivity() {
3710 mouse_center_timer.stop(); 3954 mouse_center_timer.stop();
3711} 3955}
3712 3956
3713void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) {
3714 QMessageBox::StandardButton answer;
3715 QString status_message;
3716 const QString common_message =
3717 tr("The game you are trying to load requires additional files from your Switch to be "
3718 "dumped "
3719 "before playing.<br/><br/>For more information on dumping these files, please see the "
3720 "following wiki page: <a "
3721 "href='https://yuzu-emu.org/wiki/"
3722 "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
3723 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
3724 "quit "
3725 "back to the game list? Continuing emulation may result in crashes, corrupted save "
3726 "data, or other bugs.");
3727 switch (result) {
3728 case Core::SystemResultStatus::ErrorSystemFiles: {
3729 QString message;
3730 if (details.empty()) {
3731 message =
3732 tr("yuzu was unable to locate a Switch system archive. %1").arg(common_message);
3733 } else {
3734 message = tr("yuzu was unable to locate a Switch system archive: %1. %2")
3735 .arg(QString::fromStdString(details), common_message);
3736 }
3737
3738 answer = QMessageBox::question(this, tr("System Archive Not Found"), message,
3739 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
3740 status_message = tr("System Archive Missing");
3741 break;
3742 }
3743
3744 case Core::SystemResultStatus::ErrorSharedFont: {
3745 const QString message =
3746 tr("yuzu was unable to locate the Switch shared fonts. %1").arg(common_message);
3747 answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message,
3748 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
3749 status_message = tr("Shared Font Missing");
3750 break;
3751 }
3752
3753 default:
3754 answer = QMessageBox::question(
3755 this, tr("Fatal Error"),
3756 tr("yuzu has encountered a fatal error, please see the log for more details. "
3757 "For more information on accessing the log, please see the following page: "
3758 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
3759 "to "
3760 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
3761 "list? "
3762 "Continuing emulation may result in crashes, corrupted save data, or other "
3763 "bugs."),
3764 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
3765 status_message = tr("Fatal Error encountered");
3766 break;
3767 }
3768
3769 if (answer == QMessageBox::Yes) {
3770 if (emu_thread) {
3771 ShutdownGame();
3772
3773 Settings::RestoreGlobalState(system->IsPoweredOn());
3774 system->HIDCore().ReloadInputDevices();
3775 UpdateStatusButtons();
3776 }
3777 } else {
3778 // Only show the message if the game is still running.
3779 if (emu_thread) {
3780 emu_thread->SetRunning(true);
3781 message_label->setText(status_message);
3782 }
3783 }
3784}
3785
3786void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { 3957void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
3787 if (behavior == ReinitializeKeyBehavior::Warning) { 3958 if (behavior == ReinitializeKeyBehavior::Warning) {
3788 const auto res = QMessageBox::information( 3959 const auto res = QMessageBox::information(
@@ -3927,10 +4098,6 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
3927 // Shutdown session if the emu thread is active... 4098 // Shutdown session if the emu thread is active...
3928 if (emu_thread != nullptr) { 4099 if (emu_thread != nullptr) {
3929 ShutdownGame(); 4100 ShutdownGame();
3930
3931 Settings::RestoreGlobalState(system->IsPoweredOn());
3932 system->HIDCore().ReloadInputDevices();
3933 UpdateStatusButtons();
3934 } 4101 }
3935 4102
3936 render_window->close(); 4103 render_window->close();
@@ -4023,6 +4190,10 @@ bool GMainWindow::ConfirmForceLockedExit() {
4023} 4190}
4024 4191
4025void GMainWindow::RequestGameExit() { 4192void GMainWindow::RequestGameExit() {
4193 if (!system->IsPoweredOn()) {
4194 return;
4195 }
4196
4026 auto& sm{system->ServiceManager()}; 4197 auto& sm{system->ServiceManager()};
4027 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE"); 4198 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
4028 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); 4199 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 62d629973..db318485d 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -29,6 +29,7 @@ class GImageInfo;
29class GRenderWindow; 29class GRenderWindow;
30class LoadingScreen; 30class LoadingScreen;
31class MicroProfileDialog; 31class MicroProfileDialog;
32class OverlayDialog;
32class ProfilerWidget; 33class ProfilerWidget;
33class ControllerDialog; 34class ControllerDialog;
34class QLabel; 35class QLabel;
@@ -38,6 +39,7 @@ class QProgressDialog;
38class WaitTreeWidget; 39class WaitTreeWidget;
39enum class GameListOpenTarget; 40enum class GameListOpenTarget;
40enum class GameListRemoveTarget; 41enum class GameListRemoveTarget;
42enum class GameListShortcutTarget;
41enum class DumpRomFSTarget; 43enum class DumpRomFSTarget;
42enum class InstalledEntryType; 44enum class InstalledEntryType;
43class GameListPlaceholder; 45class GameListPlaceholder;
@@ -293,6 +295,8 @@ private slots:
293 void OnGameListCopyTID(u64 program_id); 295 void OnGameListCopyTID(u64 program_id);
294 void OnGameListNavigateToGamedbEntry(u64 program_id, 296 void OnGameListNavigateToGamedbEntry(u64 program_id,
295 const CompatibilityList& compatibility_list); 297 const CompatibilityList& compatibility_list);
298 void OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
299 GameListShortcutTarget target);
296 void OnGameListOpenDirectory(const QString& directory); 300 void OnGameListOpenDirectory(const QString& directory);
297 void OnGameListAddDirectory(); 301 void OnGameListAddDirectory();
298 void OnGameListShowList(bool show); 302 void OnGameListShowList(bool show);
@@ -320,6 +324,7 @@ private slots:
320 void OnDisplayTitleBars(bool); 324 void OnDisplayTitleBars(bool);
321 void InitializeHotkeys(); 325 void InitializeHotkeys();
322 void ToggleFullscreen(); 326 void ToggleFullscreen();
327 bool UsingExclusiveFullscreen();
323 void ShowFullscreen(); 328 void ShowFullscreen();
324 void HideFullscreen(); 329 void HideFullscreen();
325 void ToggleWindowMode(); 330 void ToggleWindowMode();
@@ -328,10 +333,13 @@ private slots:
328 void ResetWindowSize900(); 333 void ResetWindowSize900();
329 void ResetWindowSize1080(); 334 void ResetWindowSize1080();
330 void OnCaptureScreenshot(); 335 void OnCaptureScreenshot();
331 void OnCoreError(Core::SystemResultStatus, std::string);
332 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 336 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
333 void OnLanguageChanged(const QString& locale); 337 void OnLanguageChanged(const QString& locale);
334 void OnMouseActivity(); 338 void OnMouseActivity();
339 bool OnShutdownBegin();
340 void OnShutdownBeginDialog();
341 void OnEmulationStopped();
342 void OnEmulationStopTimeExpired();
335 343
336private: 344private:
337 QString GetGameListErrorRemoving(InstalledEntryType type) const; 345 QString GetGameListErrorRemoving(InstalledEntryType type) const;
@@ -365,6 +373,10 @@ private:
365 bool CheckDarkMode(); 373 bool CheckDarkMode();
366 374
367 QString GetTasStateDescription() const; 375 QString GetTasStateDescription() const;
376 bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
377 const std::string& comment, const std::string& icon_path,
378 const std::string& command, const std::string& arguments,
379 const std::string& categories, const std::string& keywords);
368 380
369 std::unique_ptr<Ui::MainWindow> ui; 381 std::unique_ptr<Ui::MainWindow> ui;
370 382
@@ -377,6 +389,8 @@ private:
377 GRenderWindow* render_window; 389 GRenderWindow* render_window;
378 GameList* game_list; 390 GameList* game_list;
379 LoadingScreen* loading_screen; 391 LoadingScreen* loading_screen;
392 QTimer shutdown_timer;
393 OverlayDialog* shutdown_dialog{};
380 394
381 GameListPlaceholder* game_list_placeholder; 395 GameListPlaceholder* game_list_placeholder;
382 396
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index 563818362..9f702fe95 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -186,7 +186,7 @@ pid_t SpawnChild(const char* arg0) {
186 return pid; 186 return pid;
187 } else if (pid == 0) { 187 } else if (pid == 0) {
188 // child 188 // child
189 execl(arg0, arg0, nullptr); 189 execlp(arg0, arg0, nullptr);
190 const int err = errno; 190 const int err = errno;
191 fmt::print(stderr, "execl failed with error {}\n", err); 191 fmt::print(stderr, "execl failed with error {}\n", err);
192 _exit(0); 192 _exit(0);
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 452038cd9..2006b883e 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -138,6 +138,7 @@ struct Values {
138 138
139 bool configuration_applied; 139 bool configuration_applied;
140 bool reset_to_defaults; 140 bool reset_to_defaults;
141 bool shortcut_already_warned{false};
141 Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; 142 Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"};
142}; 143};
143 144
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
index b27954512..796f5bf41 100644
--- a/src/yuzu/util/overlay_dialog.cpp
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -3,6 +3,7 @@
3 3
4#include <QKeyEvent> 4#include <QKeyEvent>
5#include <QScreen> 5#include <QScreen>
6#include <QWindow>
6 7
7#include "core/core.h" 8#include "core/core.h"
8#include "core/hid/hid_types.h" 9#include "core/hid/hid_types.h"
@@ -42,7 +43,7 @@ OverlayDialog::OverlayDialog(QWidget* parent, Core::System& system, const QStrin
42 MoveAndResizeWindow(); 43 MoveAndResizeWindow();
43 44
44 // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend 45 // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
45 if (system.IsPoweredOn()) { 46 if (system.IsPoweredOn() && !ui->buttonsDialog->isHidden()) {
46 input_interpreter = std::make_unique<InputInterpreter>(system); 47 input_interpreter = std::make_unique<InputInterpreter>(system);
47 48
48 StartInputThread(); 49 StartInputThread();
@@ -83,6 +84,11 @@ void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const
83 ui->button_ok_label->setEnabled(false); 84 ui->button_ok_label->setEnabled(false);
84 } 85 }
85 86
87 if (ui->button_cancel->isHidden() && ui->button_ok_label->isHidden()) {
88 ui->buttonsDialog->hide();
89 return;
90 }
91
86 connect( 92 connect(
87 ui->button_cancel, &QPushButton::clicked, this, 93 ui->button_cancel, &QPushButton::clicked, this,
88 [this](bool) { 94 [this](bool) {
@@ -130,6 +136,11 @@ void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QS
130 ui->button_ok_rich->setEnabled(false); 136 ui->button_ok_rich->setEnabled(false);
131 } 137 }
132 138
139 if (ui->button_cancel_rich->isHidden() && ui->button_ok_rich->isHidden()) {
140 ui->buttonsRichDialog->hide();
141 return;
142 }
143
133 connect( 144 connect(
134 ui->button_cancel_rich, &QPushButton::clicked, this, 145 ui->button_cancel_rich, &QPushButton::clicked, this,
135 [this](bool) { 146 [this](bool) {
@@ -152,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() {
152 const auto height = static_cast<float>(parentWidget()->height()); 163 const auto height = static_cast<float>(parentWidget()->height());
153 164
154 // High DPI 165 // High DPI
155 const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; 166 const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f;
156 167
157 const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; 168 const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
158 const auto body_text_font_size = 169 const auto body_text_font_size =
@@ -249,3 +260,9 @@ void OverlayDialog::InputThread() {
249 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 260 std::this_thread::sleep_for(std::chrono::milliseconds(50));
250 } 261 }
251} 262}
263
264void OverlayDialog::keyPressEvent(QKeyEvent* e) {
265 if (!ui->buttonsDialog->isHidden() || e->key() != Qt::Key_Escape) {
266 QDialog::keyPressEvent(e);
267 }
268}
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h
index 39c44393c..872283d61 100644
--- a/src/yuzu/util/overlay_dialog.h
+++ b/src/yuzu/util/overlay_dialog.h
@@ -94,6 +94,7 @@ private:
94 94
95 /// The thread where input is being polled and processed. 95 /// The thread where input is being polled and processed.
96 void InputThread(); 96 void InputThread();
97 void keyPressEvent(QKeyEvent* e) override;
97 98
98 std::unique_ptr<Ui::OverlayDialog> ui; 99 std::unique_ptr<Ui::OverlayDialog> ui;
99 100
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index f6eeb9d8d..61b6cc4e0 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -49,6 +49,15 @@ if(UNIX AND NOT APPLE)
49 install(TARGETS yuzu-cmd) 49 install(TARGETS yuzu-cmd)
50endif() 50endif()
51 51
52if(WIN32)
53 # compile as a win32 gui application instead of a console application
54 if(MSVC)
55 set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
56 elseif(MINGW)
57 set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
58 endif()
59endif()
60
52if (MSVC) 61if (MSVC)
53 include(CopyYuzuSDLDeps) 62 include(CopyYuzuSDLDeps)
54 copy_yuzu_SDL_deps(yuzu-cmd) 63 copy_yuzu_SDL_deps(yuzu-cmd)
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 37dd1747c..31f28a507 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -115,7 +115,7 @@ bool EmuWindow_SDL2::IsShown() const {
115 115
116void EmuWindow_SDL2::OnResize() { 116void EmuWindow_SDL2::OnResize() {
117 int width, height; 117 int width, height;
118 SDL_GetWindowSize(render_window, &width, &height); 118 SDL_GL_GetDrawableSize(render_window, &width, &height);
119 UpdateCurrentFramebufferLayout(width, height); 119 UpdateCurrentFramebufferLayout(width, height);
120} 120}
121 121
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 9b660c13c..ddcb048d6 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -104,6 +104,8 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
104 exit(1); 104 exit(1);
105 } 105 }
106 106
107 strict_context_required = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0;
108
107 SetWindowIcon(); 109 SetWindowIcon();
108 110
109 if (fullscreen) { 111 if (fullscreen) {
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index a80649703..91133569d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -174,6 +174,13 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
174 174
175/// Application entry point 175/// Application entry point
176int main(int argc, char** argv) { 176int main(int argc, char** argv) {
177#ifdef _WIN32
178 if (AttachConsole(ATTACH_PARENT_PROCESS)) {
179 freopen("CONOUT$", "wb", stdout);
180 freopen("CONOUT$", "wb", stderr);
181 }
182#endif
183
177 Common::Log::Initialize(); 184 Common::Log::Initialize();
178 Common::Log::SetColorConsoleBackendEnabled(true); 185 Common::Log::SetColorConsoleBackendEnabled(true);
179 Common::Log::Start(); 186 Common::Log::Start();