summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.lgtm.yml13
-rw-r--r--src/common/settings.cpp5
-rw-r--r--src/common/settings.h5
-rw-r--r--src/core/core.cpp7
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp7
-rw-r--r--src/core/hle/kernel/k_auto_object.h4
-rw-r--r--src/core/hle/kernel/kernel.cpp31
-rw-r--r--src/core/hle/service/ipc_helpers.h1
-rw-r--r--src/core/hle/service/kernel_helpers.cpp3
-rw-r--r--src/core/hle/service/mutex.cpp3
-rw-r--r--src/core/hle/service/server_manager.cpp6
-rw-r--r--src/core/hle/service/sm/sm.cpp3
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp3
-rw-r--r--src/core/memory.cpp2
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h2
-rw-r--r--src/video_core/buffer_cache/memory_tracker_base.h6
-rw-r--r--src/video_core/compatible_formats.cpp20
-rw-r--r--src/video_core/fence_manager.h143
-rw-r--r--src/video_core/memory_manager.cpp13
-rw-r--r--src/video_core/memory_manager.h4
-rw-r--r--src/video_core/query_cache.h137
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h12
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.h6
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h3
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp3
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp51
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp224
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h34
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.cpp457
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.h83
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp49
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h31
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.h10
-rw-r--r--src/video_core/surface.cpp5
-rw-r--r--src/video_core/surface.h12
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp6
-rw-r--r--src/video_core/texture_cache/formatter.h6
-rw-r--r--src/video_core/texture_cache/texture_cache.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp8
-rw-r--r--src/yuzu/configuration/configure_general.cpp9
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_general.ui7
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp15
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui23
-rw-r--r--src/yuzu/configuration/configure_system.cpp10
-rw-r--r--src/yuzu/configuration/configure_system.h1
-rw-r--r--src/yuzu/configuration/configure_system.ui7
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h12
-rw-r--r--vcpkg.json2
65 files changed, 1205 insertions, 397 deletions
diff --git a/.lgtm.yml b/.lgtm.yml
deleted file mode 100644
index 7cd3f9926..000000000
--- a/.lgtm.yml
+++ /dev/null
@@ -1,13 +0,0 @@
1# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4path_classifiers:
5 library: "externals"
6extraction:
7 cpp:
8 prepare:
9 packages:
10 - "libsdl2-dev"
11 - "qtmultimedia5-dev"
12 - "libtbb-dev"
13 - "libjack-jackd2-dev"
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 84955030b..174460c5e 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -45,6 +45,7 @@ void LogSettings() {
45 log_setting("System_LanguageIndex", values.language_index.GetValue()); 45 log_setting("System_LanguageIndex", values.language_index.GetValue());
46 log_setting("System_RegionIndex", values.region_index.GetValue()); 46 log_setting("System_RegionIndex", values.region_index.GetValue());
47 log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); 47 log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
48 log_setting("System_UnsafeMemoryLayout", values.use_unsafe_extended_memory_layout.GetValue());
48 log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); 49 log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
49 log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); 50 log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
50 log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); 51 log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
@@ -191,7 +192,7 @@ void RestoreGlobalState(bool is_powered_on) {
191 192
192 // Core 193 // Core
193 values.use_multi_core.SetGlobal(true); 194 values.use_multi_core.SetGlobal(true);
194 values.use_extended_memory_layout.SetGlobal(true); 195 values.use_unsafe_extended_memory_layout.SetGlobal(true);
195 196
196 // CPU 197 // CPU
197 values.cpu_accuracy.SetGlobal(true); 198 values.cpu_accuracy.SetGlobal(true);
@@ -205,6 +206,7 @@ void RestoreGlobalState(bool is_powered_on) {
205 // Renderer 206 // Renderer
206 values.fsr_sharpening_slider.SetGlobal(true); 207 values.fsr_sharpening_slider.SetGlobal(true);
207 values.renderer_backend.SetGlobal(true); 208 values.renderer_backend.SetGlobal(true);
209 values.async_presentation.SetGlobal(true);
208 values.renderer_force_max_clock.SetGlobal(true); 210 values.renderer_force_max_clock.SetGlobal(true);
209 values.vulkan_device.SetGlobal(true); 211 values.vulkan_device.SetGlobal(true);
210 values.fullscreen_mode.SetGlobal(true); 212 values.fullscreen_mode.SetGlobal(true);
@@ -225,7 +227,6 @@ void RestoreGlobalState(bool is_powered_on) {
225 values.shader_backend.SetGlobal(true); 227 values.shader_backend.SetGlobal(true);
226 values.use_asynchronous_shaders.SetGlobal(true); 228 values.use_asynchronous_shaders.SetGlobal(true);
227 values.use_fast_gpu_time.SetGlobal(true); 229 values.use_fast_gpu_time.SetGlobal(true);
228 values.use_pessimistic_flushes.SetGlobal(true);
229 values.use_vulkan_driver_pipeline_cache.SetGlobal(true); 230 values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
230 values.bg_red.SetGlobal(true); 231 values.bg_red.SetGlobal(true);
231 values.bg_green.SetGlobal(true); 232 values.bg_green.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index b77a1580a..55200c36f 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -388,7 +388,8 @@ struct Values {
388 388
389 // Core 389 // Core
390 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; 390 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"};
391 SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; 391 SwitchableSetting<bool> use_unsafe_extended_memory_layout{false,
392 "use_unsafe_extended_memory_layout"};
392 393
393 // Cpu 394 // Cpu
394 SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 395 SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
@@ -422,6 +423,7 @@ struct Values {
422 // Renderer 423 // Renderer
423 SwitchableSetting<RendererBackend, true> renderer_backend{ 424 SwitchableSetting<RendererBackend, true> renderer_backend{
424 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; 425 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
426 SwitchableSetting<bool> async_presentation{false, "async_presentation"};
425 SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"}; 427 SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"};
426 Setting<bool> renderer_debug{false, "debug"}; 428 Setting<bool> renderer_debug{false, "debug"};
427 Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; 429 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
@@ -459,7 +461,6 @@ struct Values {
459 ShaderBackend::SPIRV, "shader_backend"}; 461 ShaderBackend::SPIRV, "shader_backend"};
460 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 462 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
461 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 463 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
462 SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"};
463 SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true, 464 SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
464 "use_vulkan_driver_pipeline_cache"}; 465 "use_vulkan_driver_pipeline_cache"};
465 466
diff --git a/src/core/core.cpp b/src/core/core.cpp
index caa6a77be..06fba4ce5 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -137,7 +137,7 @@ struct System::Impl {
137 device_memory = std::make_unique<Core::DeviceMemory>(); 137 device_memory = std::make_unique<Core::DeviceMemory>();
138 138
139 is_multicore = Settings::values.use_multi_core.GetValue(); 139 is_multicore = Settings::values.use_multi_core.GetValue();
140 extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); 140 extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue();
141 141
142 core_timing.SetMulticore(is_multicore); 142 core_timing.SetMulticore(is_multicore);
143 core_timing.Initialize([&system]() { system.RegisterHostThread(); }); 143 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
@@ -169,7 +169,7 @@ struct System::Impl {
169 void ReinitializeIfNecessary(System& system) { 169 void ReinitializeIfNecessary(System& system) {
170 const bool must_reinitialize = 170 const bool must_reinitialize =
171 is_multicore != Settings::values.use_multi_core.GetValue() || 171 is_multicore != Settings::values.use_multi_core.GetValue() ||
172 extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue(); 172 extended_memory_layout != Settings::values.use_unsafe_extended_memory_layout.GetValue();
173 173
174 if (!must_reinitialize) { 174 if (!must_reinitialize) {
175 return; 175 return;
@@ -178,7 +178,7 @@ struct System::Impl {
178 LOG_DEBUG(Kernel, "Re-initializing"); 178 LOG_DEBUG(Kernel, "Re-initializing");
179 179
180 is_multicore = Settings::values.use_multi_core.GetValue(); 180 is_multicore = Settings::values.use_multi_core.GetValue();
181 extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); 181 extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue();
182 182
183 Initialize(system); 183 Initialize(system);
184 } 184 }
@@ -293,6 +293,7 @@ struct System::Impl {
293 ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", 293 ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
294 Kernel::KProcess::ProcessType::Userland, resource_limit) 294 Kernel::KProcess::ProcessType::Userland, resource_limit)
295 .IsSuccess()); 295 .IsSuccess());
296 Kernel::KProcess::Register(system.Kernel(), main_process);
296 kernel.MakeApplicationProcess(main_process); 297 kernel.MakeApplicationProcess(main_process);
297 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); 298 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
298 if (load_result != Loader::ResultStatus::Success) { 299 if (load_result != Loader::ResultStatus::Success) {
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 36d0d20d2..49bdc671e 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -35,12 +35,13 @@ namespace {
35using namespace Common::Literals; 35using namespace Common::Literals;
36 36
37u32 GetMemorySizeForInit() { 37u32 GetMemorySizeForInit() {
38 return Settings::values.use_extended_memory_layout ? Smc::MemorySize_8GB : Smc::MemorySize_4GB; 38 return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemorySize_8GB
39 : Smc::MemorySize_4GB;
39} 40}
40 41
41Smc::MemoryArrangement GetMemoryArrangeForInit() { 42Smc::MemoryArrangement GetMemoryArrangeForInit() {
42 return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_8GB 43 return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemoryArrangement_8GB
43 : Smc::MemoryArrangement_4GB; 44 : Smc::MemoryArrangement_4GB;
44} 45}
45} // namespace 46} // namespace
46 47
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 9b71fe371..f384b1568 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -182,8 +182,8 @@ public:
182 explicit KAutoObjectWithList(KernelCore& kernel) : KAutoObject(kernel) {} 182 explicit KAutoObjectWithList(KernelCore& kernel) : KAutoObject(kernel) {}
183 183
184 static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) { 184 static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) {
185 const u64 lid = lhs.GetId(); 185 const uintptr_t lid = reinterpret_cast<uintptr_t>(std::addressof(lhs));
186 const u64 rid = rhs.GetId(); 186 const uintptr_t rid = reinterpret_cast<uintptr_t>(std::addressof(rhs));
187 187
188 if (lid < rid) { 188 if (lid < rid) {
189 return -1; 189 return -1;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 4f3366c9d..f33600ca5 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -95,7 +95,7 @@ struct KernelCore::Impl {
95 pt_heap_region.GetSize()); 95 pt_heap_region.GetSize());
96 } 96 }
97 97
98 InitializeHackSharedMemory(); 98 InitializeHackSharedMemory(kernel);
99 RegisterHostThread(nullptr); 99 RegisterHostThread(nullptr);
100 } 100 }
101 101
@@ -216,10 +216,12 @@ struct KernelCore::Impl {
216 auto* main_thread{Kernel::KThread::Create(system.Kernel())}; 216 auto* main_thread{Kernel::KThread::Create(system.Kernel())};
217 main_thread->SetCurrentCore(core); 217 main_thread->SetCurrentCore(core);
218 ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess()); 218 ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess());
219 KThread::Register(system.Kernel(), main_thread);
219 220
220 auto* idle_thread{Kernel::KThread::Create(system.Kernel())}; 221 auto* idle_thread{Kernel::KThread::Create(system.Kernel())};
221 idle_thread->SetCurrentCore(core); 222 idle_thread->SetCurrentCore(core);
222 ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess()); 223 ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess());
224 KThread::Register(system.Kernel(), idle_thread);
223 225
224 schedulers[i]->Initialize(main_thread, idle_thread, core); 226 schedulers[i]->Initialize(main_thread, idle_thread, core);
225 } 227 }
@@ -230,6 +232,7 @@ struct KernelCore::Impl {
230 const Core::Timing::CoreTiming& core_timing) { 232 const Core::Timing::CoreTiming& core_timing) {
231 system_resource_limit = KResourceLimit::Create(system.Kernel()); 233 system_resource_limit = KResourceLimit::Create(system.Kernel());
232 system_resource_limit->Initialize(&core_timing); 234 system_resource_limit->Initialize(&core_timing);
235 KResourceLimit::Register(kernel, system_resource_limit);
233 236
234 const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()}; 237 const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
235 const auto total_size{sizes.first}; 238 const auto total_size{sizes.first};
@@ -355,6 +358,7 @@ struct KernelCore::Impl {
355 ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {}, 358 ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {},
356 core_id) 359 core_id)
357 .IsSuccess()); 360 .IsSuccess());
361 KThread::Register(system.Kernel(), shutdown_threads[core_id]);
358 } 362 }
359 } 363 }
360 364
@@ -729,7 +733,7 @@ struct KernelCore::Impl {
729 memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize()); 733 memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize());
730 } 734 }
731 735
732 void InitializeHackSharedMemory() { 736 void InitializeHackSharedMemory(KernelCore& kernel) {
733 // Setup memory regions for emulated processes 737 // Setup memory regions for emulated processes
734 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel 738 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel
735 constexpr std::size_t hid_size{0x40000}; 739 constexpr std::size_t hid_size{0x40000};
@@ -746,14 +750,23 @@ struct KernelCore::Impl {
746 750
747 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 751 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
748 Svc::MemoryPermission::Read, hid_size); 752 Svc::MemoryPermission::Read, hid_size);
753 KSharedMemory::Register(kernel, hid_shared_mem);
754
749 font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 755 font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
750 Svc::MemoryPermission::Read, font_size); 756 Svc::MemoryPermission::Read, font_size);
757 KSharedMemory::Register(kernel, font_shared_mem);
758
751 irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 759 irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
752 Svc::MemoryPermission::Read, irs_size); 760 Svc::MemoryPermission::Read, irs_size);
761 KSharedMemory::Register(kernel, irs_shared_mem);
762
753 time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 763 time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
754 Svc::MemoryPermission::Read, time_size); 764 Svc::MemoryPermission::Read, time_size);
765 KSharedMemory::Register(kernel, time_shared_mem);
766
755 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, 767 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
756 Svc::MemoryPermission::Read, hidbus_size); 768 Svc::MemoryPermission::Read, hidbus_size);
769 KSharedMemory::Register(kernel, hidbus_shared_mem);
757 } 770 }
758 771
759 std::mutex registered_objects_lock; 772 std::mutex registered_objects_lock;
@@ -1072,12 +1085,15 @@ static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process,
1072 // Commit the thread reservation. 1085 // Commit the thread reservation.
1073 thread_reservation.Commit(); 1086 thread_reservation.Commit();
1074 1087
1088 // Register the thread.
1089 KThread::Register(kernel, thread);
1090
1075 return std::jthread( 1091 return std::jthread(
1076 [&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] { 1092 [&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] {
1077 // Set the thread name. 1093 // Set the thread name.
1078 Common::SetCurrentThreadName(thread_name.c_str()); 1094 Common::SetCurrentThreadName(thread_name.c_str());
1079 1095
1080 // Register the thread. 1096 // Set the thread as current.
1081 kernel.RegisterHostThread(thread); 1097 kernel.RegisterHostThread(thread);
1082 1098
1083 // Run the callback. 1099 // Run the callback.
@@ -1099,6 +1115,9 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
1099 // Ensure that we don't hold onto any extra references. 1115 // Ensure that we don't hold onto any extra references.
1100 SCOPE_EXIT({ process->Close(); }); 1116 SCOPE_EXIT({ process->Close(); });
1101 1117
1118 // Register the new process.
1119 KProcess::Register(*this, process);
1120
1102 // Run the host thread. 1121 // Run the host thread.
1103 return RunHostThreadFunc(*this, process, std::move(process_name), std::move(func)); 1122 return RunHostThreadFunc(*this, process, std::move(process_name), std::move(func));
1104} 1123}
@@ -1124,6 +1143,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
1124 // Ensure that we don't hold onto any extra references. 1143 // Ensure that we don't hold onto any extra references.
1125 SCOPE_EXIT({ process->Close(); }); 1144 SCOPE_EXIT({ process->Close(); });
1126 1145
1146 // Register the new process.
1147 KProcess::Register(*this, process);
1148
1127 // Reserve a new thread from the process resource limit. 1149 // Reserve a new thread from the process resource limit.
1128 KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax); 1150 KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax);
1129 ASSERT(thread_reservation.Succeeded()); 1151 ASSERT(thread_reservation.Succeeded());
@@ -1136,6 +1158,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
1136 // Commit the thread reservation. 1158 // Commit the thread reservation.
1137 thread_reservation.Commit(); 1159 thread_reservation.Commit();
1138 1160
1161 // Register the new thread.
1162 KThread::Register(*this, thread);
1163
1139 // Begin running the thread. 1164 // Begin running the thread.
1140 ASSERT(R_SUCCEEDED(thread->Run())); 1165 ASSERT(R_SUCCEEDED(thread->Run()));
1141} 1166}
diff --git a/src/core/hle/service/ipc_helpers.h b/src/core/hle/service/ipc_helpers.h
index e4cb4e1f2..0e222362e 100644
--- a/src/core/hle/service/ipc_helpers.h
+++ b/src/core/hle/service/ipc_helpers.h
@@ -156,6 +156,7 @@ public:
156 156
157 auto* session = Kernel::KSession::Create(kernel); 157 auto* session = Kernel::KSession::Create(kernel);
158 session->Initialize(nullptr, 0); 158 session->Initialize(nullptr, 0);
159 Kernel::KSession::Register(kernel, session);
159 160
160 auto next_manager = std::make_shared<Service::SessionRequestManager>( 161 auto next_manager = std::make_shared<Service::SessionRequestManager>(
161 kernel, manager->GetServerManager()); 162 kernel, manager->GetServerManager());
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index a39ce5212..6a313a03b 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -25,6 +25,9 @@ ServiceContext::ServiceContext(Core::System& system_, std::string name_)
25 Kernel::KProcess::ProcessType::KernelInternal, 25 Kernel::KProcess::ProcessType::KernelInternal,
26 kernel.GetSystemResourceLimit()) 26 kernel.GetSystemResourceLimit())
27 .IsSuccess()); 27 .IsSuccess());
28
29 // Register the process.
30 Kernel::KProcess::Register(kernel, process);
28 process_created = true; 31 process_created = true;
29} 32}
30 33
diff --git a/src/core/hle/service/mutex.cpp b/src/core/hle/service/mutex.cpp
index 07589a0f0..b0ff71d1b 100644
--- a/src/core/hle/service/mutex.cpp
+++ b/src/core/hle/service/mutex.cpp
@@ -12,6 +12,9 @@ Mutex::Mutex(Core::System& system) : m_system(system) {
12 m_event = Kernel::KEvent::Create(system.Kernel()); 12 m_event = Kernel::KEvent::Create(system.Kernel());
13 m_event->Initialize(nullptr); 13 m_event->Initialize(nullptr);
14 14
15 // Register the event.
16 Kernel::KEvent::Register(system.Kernel(), m_event);
17
15 ASSERT(R_SUCCEEDED(m_event->Signal())); 18 ASSERT(R_SUCCEEDED(m_event->Signal()));
16} 19}
17 20
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 6b4a1291e..156bc27d8 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -33,6 +33,9 @@ ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_m
33 // Initialize event. 33 // Initialize event.
34 m_event = Kernel::KEvent::Create(system.Kernel()); 34 m_event = Kernel::KEvent::Create(system.Kernel());
35 m_event->Initialize(nullptr); 35 m_event->Initialize(nullptr);
36
37 // Register event.
38 Kernel::KEvent::Register(system.Kernel(), m_event);
36} 39}
37 40
38ServerManager::~ServerManager() { 41ServerManager::~ServerManager() {
@@ -160,6 +163,9 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
160 // Initialize the event. 163 // Initialize the event.
161 m_deferral_event->Initialize(nullptr); 164 m_deferral_event->Initialize(nullptr);
162 165
166 // Register the event.
167 Kernel::KEvent::Register(m_system.Kernel(), m_deferral_event);
168
163 // Set the output. 169 // Set the output.
164 *out_event = m_deferral_event; 170 *out_event = m_deferral_event;
165 171
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index c45be5726..1608fa24c 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -64,6 +64,9 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
64 auto* port = Kernel::KPort::Create(kernel); 64 auto* port = Kernel::KPort::Create(kernel);
65 port->Initialize(ServerSessionCountMax, false, 0); 65 port->Initialize(ServerSessionCountMax, false, 0);
66 66
67 // Register the port.
68 Kernel::KPort::Register(kernel, port);
69
67 service_ports.emplace(name, port); 70 service_ports.emplace(name, port);
68 registered_services.emplace(name, handler); 71 registered_services.emplace(name, handler);
69 if (deferral_event) { 72 if (deferral_event) {
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index 419c1df2b..7dce28fe0 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -49,6 +49,9 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
49 // Commit the session reservation. 49 // Commit the session reservation.
50 session_reservation.Commit(); 50 session_reservation.Commit();
51 51
52 // Register the session.
53 Kernel::KSession::Register(system.Kernel(), session);
54
52 // Register with server manager. 55 // Register with server manager.
53 session_manager->GetServerManager().RegisterSession(&session->GetServerSession(), 56 session_manager->GetServerManager().RegisterSession(&session->GetServerSession(),
54 session_manager); 57 session_manager);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 432310632..a9667463f 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -462,7 +462,7 @@ struct Memory::Impl {
462 } 462 }
463 463
464 if (Settings::IsFastmemEnabled()) { 464 if (Settings::IsFastmemEnabled()) {
465 const bool is_read_enable = Settings::IsGPULevelHigh() || !cached; 465 const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached;
466 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 466 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
467 } 467 }
468 468
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 92cab93f3..a0009a36f 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -179,6 +179,8 @@ add_library(video_core STATIC
179 renderer_vulkan/vk_master_semaphore.h 179 renderer_vulkan/vk_master_semaphore.h
180 renderer_vulkan/vk_pipeline_cache.cpp 180 renderer_vulkan/vk_pipeline_cache.cpp
181 renderer_vulkan/vk_pipeline_cache.h 181 renderer_vulkan/vk_pipeline_cache.h
182 renderer_vulkan/vk_present_manager.cpp
183 renderer_vulkan/vk_present_manager.h
182 renderer_vulkan/vk_query_cache.cpp 184 renderer_vulkan/vk_query_cache.cpp
183 renderer_vulkan/vk_query_cache.h 185 renderer_vulkan/vk_query_cache.h
184 renderer_vulkan/vk_rasterizer.cpp 186 renderer_vulkan/vk_rasterizer.cpp
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 7975564b5..e534e1e9c 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -1426,7 +1426,7 @@ bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr,
1426 .size = sub_size, 1426 .size = sub_size,
1427 }); 1427 });
1428 total_size_bytes += sub_size; 1428 total_size_bytes += sub_size;
1429 largest_copy = std::max(largest_copy, sub_size); 1429 largest_copy = std::max<u64>(largest_copy, sub_size);
1430 } 1430 }
1431 const std::span<BufferCopy> copies_span(copies.data(), copies.size()); 1431 const std::span<BufferCopy> copies_span(copies.data(), copies.size());
1432 UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); 1432 UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h
index 4bc59017f..dc4ebfcaa 100644
--- a/src/video_core/buffer_cache/memory_tracker_base.h
+++ b/src/video_core/buffer_cache/memory_tracker_base.h
@@ -170,7 +170,8 @@ private:
170 std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS}; 170 std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS};
171 u64 page_offset{cpu_address & HIGHER_PAGE_MASK}; 171 u64 page_offset{cpu_address & HIGHER_PAGE_MASK};
172 while (remaining_size > 0) { 172 while (remaining_size > 0) {
173 const std::size_t copy_amount{std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; 173 const std::size_t copy_amount{
174 std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
174 auto* manager{top_tier[page_index]}; 175 auto* manager{top_tier[page_index]};
175 if (manager) { 176 if (manager) {
176 if constexpr (BOOL_BREAK) { 177 if constexpr (BOOL_BREAK) {
@@ -206,7 +207,8 @@ private:
206 u64 begin = std::numeric_limits<u64>::max(); 207 u64 begin = std::numeric_limits<u64>::max();
207 u64 end = 0; 208 u64 end = 0;
208 while (remaining_size > 0) { 209 while (remaining_size > 0) {
209 const std::size_t copy_amount{std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; 210 const std::size_t copy_amount{
211 std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
210 auto* manager{top_tier[page_index]}; 212 auto* manager{top_tier[page_index]};
211 const auto execute = [&] { 213 const auto execute = [&] {
212 auto [new_begin, new_end] = func(manager, page_offset, copy_amount); 214 auto [new_begin, new_end] = func(manager, page_offset, copy_amount);
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp
index 4e75f33ca..ab4f4d407 100644
--- a/src/video_core/compatible_formats.cpp
+++ b/src/video_core/compatible_formats.cpp
@@ -126,15 +126,14 @@ constexpr std::array VIEW_CLASS_ASTC_8x8_RGBA{
126 PixelFormat::ASTC_2D_8X8_SRGB, 126 PixelFormat::ASTC_2D_8X8_SRGB,
127}; 127};
128 128
129// Missing formats: 129constexpr std::array VIEW_CLASS_ASTC_10x5_RGBA{
130// PixelFormat::ASTC_2D_10X5_UNORM 130 PixelFormat::ASTC_2D_10X5_UNORM,
131// PixelFormat::ASTC_2D_10X5_SRGB 131 PixelFormat::ASTC_2D_10X5_SRGB,
132 132};
133// Missing formats:
134// PixelFormat::ASTC_2D_10X6_SRGB
135 133
136constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{ 134constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{
137 PixelFormat::ASTC_2D_10X6_UNORM, 135 PixelFormat::ASTC_2D_10X6_UNORM,
136 PixelFormat::ASTC_2D_10X6_SRGB,
138}; 137};
139 138
140constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{ 139constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{
@@ -147,9 +146,10 @@ constexpr std::array VIEW_CLASS_ASTC_10x10_RGBA{
147 PixelFormat::ASTC_2D_10X10_SRGB, 146 PixelFormat::ASTC_2D_10X10_SRGB,
148}; 147};
149 148
150// Missing formats 149constexpr std::array VIEW_CLASS_ASTC_12x10_RGBA{
151// ASTC_2D_12X10_UNORM, 150 PixelFormat::ASTC_2D_12X10_UNORM,
152// ASTC_2D_12X10_SRGB, 151 PixelFormat::ASTC_2D_12X10_SRGB,
152};
153 153
154constexpr std::array VIEW_CLASS_ASTC_12x12_RGBA{ 154constexpr std::array VIEW_CLASS_ASTC_12x12_RGBA{
155 PixelFormat::ASTC_2D_12X12_UNORM, 155 PixelFormat::ASTC_2D_12X12_UNORM,
@@ -229,9 +229,11 @@ constexpr Table MakeViewTable() {
229 EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA); 229 EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA);
230 EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA); 230 EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA);
231 EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA); 231 EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA);
232 EnableRange(view, VIEW_CLASS_ASTC_10x5_RGBA);
232 EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA); 233 EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA);
233 EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA); 234 EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA);
234 EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA); 235 EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA);
236 EnableRange(view, VIEW_CLASS_ASTC_12x10_RGBA);
235 EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA); 237 EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA);
236 return view; 238 return view;
237} 239}
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index c390ac91b..3b2f6aab6 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -4,13 +4,20 @@
4#pragma once 4#pragma once
5 5
6#include <algorithm> 6#include <algorithm>
7#include <condition_variable>
7#include <cstring> 8#include <cstring>
8#include <deque> 9#include <deque>
9#include <functional> 10#include <functional>
10#include <memory> 11#include <memory>
12#include <mutex>
13#include <thread>
11#include <queue> 14#include <queue>
12 15
13#include "common/common_types.h" 16#include "common/common_types.h"
17#include "common/microprofile.h"
18#include "common/scope_exit.h"
19#include "common/settings.h"
20#include "common/thread.h"
14#include "video_core/delayed_destruction_ring.h" 21#include "video_core/delayed_destruction_ring.h"
15#include "video_core/gpu.h" 22#include "video_core/gpu.h"
16#include "video_core/host1x/host1x.h" 23#include "video_core/host1x/host1x.h"
@@ -23,15 +30,26 @@ class FenceBase {
23public: 30public:
24 explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {} 31 explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {}
25 32
33 bool IsStubbed() const {
34 return is_stubbed;
35 }
36
26protected: 37protected:
27 bool is_stubbed; 38 bool is_stubbed;
28}; 39};
29 40
30template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache> 41template <typename Traits>
31class FenceManager { 42class FenceManager {
43 using TFence = typename Traits::FenceType;
44 using TTextureCache = typename Traits::TextureCacheType;
45 using TBufferCache = typename Traits::BufferCacheType;
46 using TQueryCache = typename Traits::QueryCacheType;
47 static constexpr bool can_async_check = Traits::HAS_ASYNC_CHECK;
48
32public: 49public:
33 /// Notify the fence manager about a new frame 50 /// Notify the fence manager about a new frame
34 void TickFrame() { 51 void TickFrame() {
52 std::unique_lock lock(ring_guard);
35 delayed_destruction_ring.Tick(); 53 delayed_destruction_ring.Tick();
36 } 54 }
37 55
@@ -46,17 +64,33 @@ public:
46 } 64 }
47 65
48 void SignalFence(std::function<void()>&& func) { 66 void SignalFence(std::function<void()>&& func) {
49 TryReleasePendingFences(); 67 rasterizer.InvalidateGPUCache();
68 bool delay_fence = Settings::IsGPULevelHigh();
69 if constexpr (!can_async_check) {
70 TryReleasePendingFences<false>();
71 }
50 const bool should_flush = ShouldFlush(); 72 const bool should_flush = ShouldFlush();
51 CommitAsyncFlushes(); 73 CommitAsyncFlushes();
52 uncommitted_operations.emplace_back(std::move(func));
53 CommitOperations();
54 TFence new_fence = CreateFence(!should_flush); 74 TFence new_fence = CreateFence(!should_flush);
55 fences.push(new_fence); 75 if constexpr (can_async_check) {
76 guard.lock();
77 }
78 if (delay_fence) {
79 uncommitted_operations.emplace_back(std::move(func));
80 }
81 pending_operations.emplace_back(std::move(uncommitted_operations));
56 QueueFence(new_fence); 82 QueueFence(new_fence);
83 if (!delay_fence) {
84 func();
85 }
86 fences.push(std::move(new_fence));
57 if (should_flush) { 87 if (should_flush) {
58 rasterizer.FlushCommands(); 88 rasterizer.FlushCommands();
59 } 89 }
90 if constexpr (can_async_check) {
91 guard.unlock();
92 cv.notify_all();
93 }
60 } 94 }
61 95
62 void SignalSyncPoint(u32 value) { 96 void SignalSyncPoint(u32 value) {
@@ -66,29 +100,30 @@ public:
66 } 100 }
67 101
68 void WaitPendingFences() { 102 void WaitPendingFences() {
69 while (!fences.empty()) { 103 if constexpr (!can_async_check) {
70 TFence& current_fence = fences.front(); 104 TryReleasePendingFences<true>();
71 if (ShouldWait()) {
72 WaitFence(current_fence);
73 }
74 PopAsyncFlushes();
75 auto operations = std::move(pending_operations.front());
76 pending_operations.pop_front();
77 for (auto& operation : operations) {
78 operation();
79 }
80 PopFence();
81 } 105 }
82 } 106 }
83 107
84protected: 108protected:
85 explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, 109 explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
86 TTextureCache& texture_cache_, TTBufferCache& buffer_cache_, 110 TTextureCache& texture_cache_, TBufferCache& buffer_cache_,
87 TQueryCache& query_cache_) 111 TQueryCache& query_cache_)
88 : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()}, 112 : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()},
89 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {} 113 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {
114 if constexpr (can_async_check) {
115 fence_thread =
116 std::jthread([this](std::stop_token token) { ReleaseThreadFunc(token); });
117 }
118 }
90 119
91 virtual ~FenceManager() = default; 120 virtual ~FenceManager() {
121 if constexpr (can_async_check) {
122 fence_thread.request_stop();
123 cv.notify_all();
124 fence_thread.join();
125 }
126 }
92 127
93 /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is 128 /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is
94 /// true 129 /// true
@@ -104,15 +139,20 @@ protected:
104 Tegra::GPU& gpu; 139 Tegra::GPU& gpu;
105 Tegra::Host1x::SyncpointManager& syncpoint_manager; 140 Tegra::Host1x::SyncpointManager& syncpoint_manager;
106 TTextureCache& texture_cache; 141 TTextureCache& texture_cache;
107 TTBufferCache& buffer_cache; 142 TBufferCache& buffer_cache;
108 TQueryCache& query_cache; 143 TQueryCache& query_cache;
109 144
110private: 145private:
146 template <bool force_wait>
111 void TryReleasePendingFences() { 147 void TryReleasePendingFences() {
112 while (!fences.empty()) { 148 while (!fences.empty()) {
113 TFence& current_fence = fences.front(); 149 TFence& current_fence = fences.front();
114 if (ShouldWait() && !IsFenceSignaled(current_fence)) { 150 if (ShouldWait() && !IsFenceSignaled(current_fence)) {
115 return; 151 if constexpr (force_wait) {
152 WaitFence(current_fence);
153 } else {
154 return;
155 }
116 } 156 }
117 PopAsyncFlushes(); 157 PopAsyncFlushes();
118 auto operations = std::move(pending_operations.front()); 158 auto operations = std::move(pending_operations.front());
@@ -120,7 +160,49 @@ private:
120 for (auto& operation : operations) { 160 for (auto& operation : operations) {
121 operation(); 161 operation();
122 } 162 }
123 PopFence(); 163 {
164 std::unique_lock lock(ring_guard);
165 delayed_destruction_ring.Push(std::move(current_fence));
166 }
167 fences.pop();
168 }
169 }
170
171 void ReleaseThreadFunc(std::stop_token stop_token) {
172 std::string name = "GPUFencingThread";
173 MicroProfileOnThreadCreate(name.c_str());
174
175 // Cleanup
176 SCOPE_EXIT({ MicroProfileOnThreadExit(); });
177
178 Common::SetCurrentThreadName(name.c_str());
179 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
180
181 TFence current_fence;
182 std::deque<std::function<void()>> current_operations;
183 while (!stop_token.stop_requested()) {
184 {
185 std::unique_lock lock(guard);
186 cv.wait(lock, [&] { return stop_token.stop_requested() || !fences.empty(); });
187 if (stop_token.stop_requested()) [[unlikely]] {
188 return;
189 }
190 current_fence = std::move(fences.front());
191 current_operations = std::move(pending_operations.front());
192 fences.pop();
193 pending_operations.pop_front();
194 }
195 if (!current_fence->IsStubbed()) {
196 WaitFence(current_fence);
197 }
198 PopAsyncFlushes();
199 for (auto& operation : current_operations) {
200 operation();
201 }
202 {
203 std::unique_lock lock(ring_guard);
204 delayed_destruction_ring.Push(std::move(current_fence));
205 }
124 } 206 }
125 } 207 }
126 208
@@ -154,19 +236,16 @@ private:
154 query_cache.CommitAsyncFlushes(); 236 query_cache.CommitAsyncFlushes();
155 } 237 }
156 238
157 void PopFence() {
158 delayed_destruction_ring.Push(std::move(fences.front()));
159 fences.pop();
160 }
161
162 void CommitOperations() {
163 pending_operations.emplace_back(std::move(uncommitted_operations));
164 }
165
166 std::queue<TFence> fences; 239 std::queue<TFence> fences;
167 std::deque<std::function<void()>> uncommitted_operations; 240 std::deque<std::function<void()>> uncommitted_operations;
168 std::deque<std::deque<std::function<void()>>> pending_operations; 241 std::deque<std::deque<std::function<void()>>> pending_operations;
169 242
243 std::mutex guard;
244 std::mutex ring_guard;
245 std::condition_variable cv;
246
247 std::jthread fence_thread;
248
170 DelayedDestructionRing<TFence, 6> delayed_destruction_ring; 249 DelayedDestructionRing<TFence, 6> delayed_destruction_ring;
171}; 250};
172 251
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 01fb5b546..7b2cde7a7 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -82,6 +82,7 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
82} 82}
83 83
84PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { 84PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
85 std::unique_lock<std::mutex> lock(guard);
85 return kind_map.GetValueAt(gpu_addr); 86 return kind_map.GetValueAt(gpu_addr);
86} 87}
87 88
@@ -160,7 +161,10 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
160 } 161 }
161 remaining_size -= big_page_size; 162 remaining_size -= big_page_size;
162 } 163 }
163 kind_map.Map(gpu_addr, gpu_addr + size, kind); 164 {
165 std::unique_lock<std::mutex> lock(guard);
166 kind_map.Map(gpu_addr, gpu_addr + size, kind);
167 }
164 return gpu_addr; 168 return gpu_addr;
165} 169}
166 170
@@ -553,6 +557,7 @@ size_t MemoryManager::MaxContinuousRange(GPUVAddr gpu_addr, size_t size) const {
553} 557}
554 558
555size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const { 559size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const {
560 std::unique_lock<std::mutex> lock(guard);
556 return kind_map.GetContinuousSizeFrom(gpu_addr); 561 return kind_map.GetContinuousSizeFrom(gpu_addr);
557} 562}
558 563
@@ -745,10 +750,10 @@ void MemoryManager::FlushCaching() {
745 return; 750 return;
746 } 751 }
747 accumulator->Callback([this](GPUVAddr addr, size_t size) { 752 accumulator->Callback([this](GPUVAddr addr, size_t size) {
748 GetSubmappedRangeImpl<false>(addr, size, page_stash); 753 GetSubmappedRangeImpl<false>(addr, size, page_stash2);
749 }); 754 });
750 rasterizer->InnerInvalidation(page_stash); 755 rasterizer->InnerInvalidation(page_stash2);
751 page_stash.clear(); 756 page_stash2.clear();
752 accumulator->Clear(); 757 accumulator->Clear();
753} 758}
754 759
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index fbbe856c4..794535122 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -5,6 +5,7 @@
5 5
6#include <atomic> 6#include <atomic>
7#include <map> 7#include <map>
8#include <mutex>
8#include <optional> 9#include <optional>
9#include <vector> 10#include <vector>
10 11
@@ -215,6 +216,9 @@ private:
215 216
216 std::vector<u64> big_page_continuous; 217 std::vector<u64> big_page_continuous;
217 std::vector<std::pair<VAddr, std::size_t>> page_stash{}; 218 std::vector<std::pair<VAddr, std::size_t>> page_stash{};
219 std::vector<std::pair<VAddr, std::size_t>> page_stash2{};
220
221 mutable std::mutex guard;
218 222
219 static constexpr size_t continuous_bits = 64; 223 static constexpr size_t continuous_bits = 64;
220 224
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 8906ba6d8..941de95c1 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -6,6 +6,7 @@
6#include <algorithm> 6#include <algorithm>
7#include <array> 7#include <array>
8#include <cstring> 8#include <cstring>
9#include <functional>
9#include <iterator> 10#include <iterator>
10#include <list> 11#include <list>
11#include <memory> 12#include <memory>
@@ -17,13 +18,19 @@
17 18
18#include "common/assert.h" 19#include "common/assert.h"
19#include "common/settings.h" 20#include "common/settings.h"
21#include "core/memory.h"
20#include "video_core/control/channel_state_cache.h" 22#include "video_core/control/channel_state_cache.h"
21#include "video_core/engines/maxwell_3d.h" 23#include "video_core/engines/maxwell_3d.h"
22#include "video_core/memory_manager.h" 24#include "video_core/memory_manager.h"
23#include "video_core/rasterizer_interface.h" 25#include "video_core/rasterizer_interface.h"
26#include "video_core/texture_cache/slot_vector.h"
24 27
25namespace VideoCommon { 28namespace VideoCommon {
26 29
30using AsyncJobId = SlotId;
31
32static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0};
33
27template <class QueryCache, class HostCounter> 34template <class QueryCache, class HostCounter>
28class CounterStreamBase { 35class CounterStreamBase {
29public: 36public:
@@ -93,9 +100,13 @@ private:
93template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter> 100template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter>
94class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> { 101class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
95public: 102public:
96 explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_) 103 explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
97 : rasterizer{rasterizer_}, streams{{CounterStream{static_cast<QueryCache&>(*this), 104 Core::Memory::Memory& cpu_memory_)
98 VideoCore::QueryType::SamplesPassed}}} {} 105 : rasterizer{rasterizer_},
106 cpu_memory{cpu_memory_}, streams{{CounterStream{static_cast<QueryCache&>(*this),
107 VideoCore::QueryType::SamplesPassed}}} {
108 (void)slot_async_jobs.insert(); // Null value
109 }
99 110
100 void InvalidateRegion(VAddr addr, std::size_t size) { 111 void InvalidateRegion(VAddr addr, std::size_t size) {
101 std::unique_lock lock{mutex}; 112 std::unique_lock lock{mutex};
@@ -126,10 +137,15 @@ public:
126 query = Register(type, *cpu_addr, host_ptr, timestamp.has_value()); 137 query = Register(type, *cpu_addr, host_ptr, timestamp.has_value());
127 } 138 }
128 139
129 query->BindCounter(Stream(type).Current(), timestamp); 140 auto result = query->BindCounter(Stream(type).Current(), timestamp);
130 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { 141 if (result) {
131 AsyncFlushQuery(*cpu_addr); 142 auto async_job_id = query->GetAsyncJob();
143 auto& async_job = slot_async_jobs[async_job_id];
144 async_job.collected = true;
145 async_job.value = *result;
146 query->SetAsyncJob(NULL_ASYNC_JOB_ID);
132 } 147 }
148 AsyncFlushQuery(query, timestamp, lock);
133 } 149 }
134 150
135 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. 151 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
@@ -173,15 +189,18 @@ public:
173 } 189 }
174 190
175 void CommitAsyncFlushes() { 191 void CommitAsyncFlushes() {
192 std::unique_lock lock{mutex};
176 committed_flushes.push_back(uncommitted_flushes); 193 committed_flushes.push_back(uncommitted_flushes);
177 uncommitted_flushes.reset(); 194 uncommitted_flushes.reset();
178 } 195 }
179 196
180 bool HasUncommittedFlushes() const { 197 bool HasUncommittedFlushes() const {
198 std::unique_lock lock{mutex};
181 return uncommitted_flushes != nullptr; 199 return uncommitted_flushes != nullptr;
182 } 200 }
183 201
184 bool ShouldWaitAsyncFlushes() const { 202 bool ShouldWaitAsyncFlushes() const {
203 std::unique_lock lock{mutex};
185 if (committed_flushes.empty()) { 204 if (committed_flushes.empty()) {
186 return false; 205 return false;
187 } 206 }
@@ -189,6 +208,7 @@ public:
189 } 208 }
190 209
191 void PopAsyncFlushes() { 210 void PopAsyncFlushes() {
211 std::unique_lock lock{mutex};
192 if (committed_flushes.empty()) { 212 if (committed_flushes.empty()) {
193 return; 213 return;
194 } 214 }
@@ -197,15 +217,25 @@ public:
197 committed_flushes.pop_front(); 217 committed_flushes.pop_front();
198 return; 218 return;
199 } 219 }
200 for (VAddr query_address : *flush_list) { 220 for (AsyncJobId async_job_id : *flush_list) {
201 FlushAndRemoveRegion(query_address, 4); 221 AsyncJob& async_job = slot_async_jobs[async_job_id];
222 if (!async_job.collected) {
223 FlushAndRemoveRegion(async_job.query_location, 2, true);
224 }
202 } 225 }
203 committed_flushes.pop_front(); 226 committed_flushes.pop_front();
204 } 227 }
205 228
206private: 229private:
230 struct AsyncJob {
231 bool collected = false;
232 u64 value = 0;
233 VAddr query_location = 0;
234 std::optional<u64> timestamp{};
235 };
236
207 /// Flushes a memory range to guest memory and removes it from the cache. 237 /// Flushes a memory range to guest memory and removes it from the cache.
208 void FlushAndRemoveRegion(VAddr addr, std::size_t size) { 238 void FlushAndRemoveRegion(VAddr addr, std::size_t size, bool async = false) {
209 const u64 addr_begin = addr; 239 const u64 addr_begin = addr;
210 const u64 addr_end = addr_begin + size; 240 const u64 addr_end = addr_begin + size;
211 const auto in_range = [addr_begin, addr_end](const CachedQuery& query) { 241 const auto in_range = [addr_begin, addr_end](const CachedQuery& query) {
@@ -226,7 +256,16 @@ private:
226 continue; 256 continue;
227 } 257 }
228 rasterizer.UpdatePagesCachedCount(query.GetCpuAddr(), query.SizeInBytes(), -1); 258 rasterizer.UpdatePagesCachedCount(query.GetCpuAddr(), query.SizeInBytes(), -1);
229 query.Flush(); 259 AsyncJobId async_job_id = query.GetAsyncJob();
260 auto flush_result = query.Flush(async);
261 if (async_job_id == NULL_ASYNC_JOB_ID) {
262 ASSERT_MSG(false, "This should not be reachable at all");
263 continue;
264 }
265 AsyncJob& async_job = slot_async_jobs[async_job_id];
266 async_job.collected = true;
267 async_job.value = flush_result;
268 query.SetAsyncJob(NULL_ASYNC_JOB_ID);
230 } 269 }
231 std::erase_if(contents, in_range); 270 std::erase_if(contents, in_range);
232 } 271 }
@@ -253,26 +292,60 @@ private:
253 return found != std::end(contents) ? &*found : nullptr; 292 return found != std::end(contents) ? &*found : nullptr;
254 } 293 }
255 294
256 void AsyncFlushQuery(VAddr addr) { 295 void AsyncFlushQuery(CachedQuery* query, std::optional<u64> timestamp,
257 if (!uncommitted_flushes) { 296 std::unique_lock<std::recursive_mutex>& lock) {
258 uncommitted_flushes = std::make_shared<std::vector<VAddr>>(); 297 const AsyncJobId new_async_job_id = slot_async_jobs.insert();
298 {
299 AsyncJob& async_job = slot_async_jobs[new_async_job_id];
300 query->SetAsyncJob(new_async_job_id);
301 async_job.query_location = query->GetCpuAddr();
302 async_job.collected = false;
303
304 if (!uncommitted_flushes) {
305 uncommitted_flushes = std::make_shared<std::vector<AsyncJobId>>();
306 }
307 uncommitted_flushes->push_back(new_async_job_id);
259 } 308 }
260 uncommitted_flushes->push_back(addr); 309 lock.unlock();
310 std::function<void()> operation([this, new_async_job_id, timestamp] {
311 std::unique_lock local_lock{mutex};
312 AsyncJob& async_job = slot_async_jobs[new_async_job_id];
313 u64 value = async_job.value;
314 VAddr address = async_job.query_location;
315 slot_async_jobs.erase(new_async_job_id);
316 local_lock.unlock();
317 if (timestamp) {
318 u64 timestamp_value = *timestamp;
319 cpu_memory.WriteBlockUnsafe(address + sizeof(u64), &timestamp_value, sizeof(u64));
320 cpu_memory.WriteBlockUnsafe(address, &value, sizeof(u64));
321 rasterizer.InvalidateRegion(address, sizeof(u64) * 2,
322 VideoCommon::CacheType::NoQueryCache);
323 } else {
324 u32 small_value = static_cast<u32>(value);
325 cpu_memory.WriteBlockUnsafe(address, &small_value, sizeof(u32));
326 rasterizer.InvalidateRegion(address, sizeof(u32),
327 VideoCommon::CacheType::NoQueryCache);
328 }
329 });
330 rasterizer.SyncOperation(std::move(operation));
261 } 331 }
262 332
263 static constexpr std::uintptr_t YUZU_PAGESIZE = 4096; 333 static constexpr std::uintptr_t YUZU_PAGESIZE = 4096;
264 static constexpr unsigned YUZU_PAGEBITS = 12; 334 static constexpr unsigned YUZU_PAGEBITS = 12;
265 335
336 SlotVector<AsyncJob> slot_async_jobs;
337
266 VideoCore::RasterizerInterface& rasterizer; 338 VideoCore::RasterizerInterface& rasterizer;
339 Core::Memory::Memory& cpu_memory;
267 340
268 std::recursive_mutex mutex; 341 mutable std::recursive_mutex mutex;
269 342
270 std::unordered_map<u64, std::vector<CachedQuery>> cached_queries; 343 std::unordered_map<u64, std::vector<CachedQuery>> cached_queries;
271 344
272 std::array<CounterStream, VideoCore::NumQueryTypes> streams; 345 std::array<CounterStream, VideoCore::NumQueryTypes> streams;
273 346
274 std::shared_ptr<std::vector<VAddr>> uncommitted_flushes{}; 347 std::shared_ptr<std::vector<AsyncJobId>> uncommitted_flushes{};
275 std::list<std::shared_ptr<std::vector<VAddr>>> committed_flushes; 348 std::list<std::shared_ptr<std::vector<AsyncJobId>>> committed_flushes;
276}; 349};
277 350
278template <class QueryCache, class HostCounter> 351template <class QueryCache, class HostCounter>
@@ -291,12 +364,12 @@ public:
291 virtual ~HostCounterBase() = default; 364 virtual ~HostCounterBase() = default;
292 365
293 /// Returns the current value of the query. 366 /// Returns the current value of the query.
294 u64 Query() { 367 u64 Query(bool async = false) {
295 if (result) { 368 if (result) {
296 return *result; 369 return *result;
297 } 370 }
298 371
299 u64 value = BlockingQuery() + base_result; 372 u64 value = BlockingQuery(async) + base_result;
300 if (dependency) { 373 if (dependency) {
301 value += dependency->Query(); 374 value += dependency->Query();
302 dependency = nullptr; 375 dependency = nullptr;
@@ -317,7 +390,7 @@ public:
317 390
318protected: 391protected:
319 /// Returns the value of query from the backend API blocking as needed. 392 /// Returns the value of query from the backend API blocking as needed.
320 virtual u64 BlockingQuery() const = 0; 393 virtual u64 BlockingQuery(bool async = false) const = 0;
321 394
322private: 395private:
323 std::shared_ptr<HostCounter> dependency; ///< Counter to add to this value. 396 std::shared_ptr<HostCounter> dependency; ///< Counter to add to this value.
@@ -340,26 +413,33 @@ public:
340 CachedQueryBase& operator=(const CachedQueryBase&) = delete; 413 CachedQueryBase& operator=(const CachedQueryBase&) = delete;
341 414
342 /// Flushes the query to guest memory. 415 /// Flushes the query to guest memory.
343 virtual void Flush() { 416 virtual u64 Flush(bool async = false) {
344 // When counter is nullptr it means that it's just been reset. We are supposed to write a 417 // When counter is nullptr it means that it's just been reset. We are supposed to write a
345 // zero in these cases. 418 // zero in these cases.
346 const u64 value = counter ? counter->Query() : 0; 419 const u64 value = counter ? counter->Query(async) : 0;
420 if (async) {
421 return value;
422 }
347 std::memcpy(host_ptr, &value, sizeof(u64)); 423 std::memcpy(host_ptr, &value, sizeof(u64));
348 424
349 if (timestamp) { 425 if (timestamp) {
350 std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64)); 426 std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64));
351 } 427 }
428 return value;
352 } 429 }
353 430
354 /// Binds a counter to this query. 431 /// Binds a counter to this query.
355 void BindCounter(std::shared_ptr<HostCounter> counter_, std::optional<u64> timestamp_) { 432 std::optional<u64> BindCounter(std::shared_ptr<HostCounter> counter_,
433 std::optional<u64> timestamp_) {
434 std::optional<u64> result{};
356 if (counter) { 435 if (counter) {
357 // If there's an old counter set it means the query is being rewritten by the game. 436 // If there's an old counter set it means the query is being rewritten by the game.
358 // To avoid losing the data forever, flush here. 437 // To avoid losing the data forever, flush here.
359 Flush(); 438 result = std::make_optional(Flush());
360 } 439 }
361 counter = std::move(counter_); 440 counter = std::move(counter_);
362 timestamp = timestamp_; 441 timestamp = timestamp_;
442 return result;
363 } 443 }
364 444
365 VAddr GetCpuAddr() const noexcept { 445 VAddr GetCpuAddr() const noexcept {
@@ -374,6 +454,14 @@ public:
374 return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE; 454 return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE;
375 } 455 }
376 456
457 void SetAsyncJob(AsyncJobId assigned_async_job_) {
458 assigned_async_job = assigned_async_job_;
459 }
460
461 AsyncJobId GetAsyncJob() const {
462 return assigned_async_job;
463 }
464
377protected: 465protected:
378 /// Returns true when querying the counter may potentially block. 466 /// Returns true when querying the counter may potentially block.
379 bool WaitPending() const noexcept { 467 bool WaitPending() const noexcept {
@@ -389,6 +477,7 @@ private:
389 u8* host_ptr; ///< Writable host pointer. 477 u8* host_ptr; ///< Writable host pointer.
390 std::shared_ptr<HostCounter> counter; ///< Host counter to query, owns the dependency tree. 478 std::shared_ptr<HostCounter> counter; ///< Host counter to query, owns the dependency tree.
391 std::optional<u64> timestamp; ///< Timestamp to flush to guest memory. 479 std::optional<u64> timestamp; ///< Timestamp to flush to guest memory.
480 AsyncJobId assigned_async_job;
392}; 481};
393 482
394} // namespace VideoCommon 483} // namespace VideoCommon
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index f1446e732..e21b19dcc 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -30,7 +30,17 @@ private:
30}; 30};
31 31
32using Fence = std::shared_ptr<GLInnerFence>; 32using Fence = std::shared_ptr<GLInnerFence>;
33using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>; 33
34struct FenceManagerParams {
35 using FenceType = Fence;
36 using BufferCacheType = BufferCache;
37 using TextureCacheType = TextureCache;
38 using QueryCacheType = QueryCache;
39
40 static constexpr bool HAS_ASYNC_CHECK = false;
41};
42
43using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>;
34 44
35class FenceManagerOpenGL final : public GenericFenceManager { 45class FenceManagerOpenGL final : public GenericFenceManager {
36public: 46public:
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp
index 5070db441..99d7347f5 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_query_cache.cpp
@@ -26,8 +26,8 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) {
26 26
27} // Anonymous namespace 27} // Anonymous namespace
28 28
29QueryCache::QueryCache(RasterizerOpenGL& rasterizer_) 29QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_)
30 : QueryCacheBase(rasterizer_), gl_rasterizer{rasterizer_} {} 30 : QueryCacheBase(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {}
31 31
32QueryCache::~QueryCache() = default; 32QueryCache::~QueryCache() = default;
33 33
@@ -74,7 +74,7 @@ void HostCounter::EndQuery() {
74 glEndQuery(GetTarget(type)); 74 glEndQuery(GetTarget(type));
75} 75}
76 76
77u64 HostCounter::BlockingQuery() const { 77u64 HostCounter::BlockingQuery([[maybe_unused]] bool async) const {
78 GLint64 value; 78 GLint64 value;
79 glGetQueryObjecti64v(query.handle, GL_QUERY_RESULT, &value); 79 glGetQueryObjecti64v(query.handle, GL_QUERY_RESULT, &value);
80 return static_cast<u64>(value); 80 return static_cast<u64>(value);
@@ -96,7 +96,7 @@ CachedQuery& CachedQuery::operator=(CachedQuery&& rhs) noexcept {
96 return *this; 96 return *this;
97} 97}
98 98
99void CachedQuery::Flush() { 99u64 CachedQuery::Flush([[maybe_unused]] bool async) {
100 // Waiting for a query while another query of the same target is enabled locks Nvidia's driver. 100 // Waiting for a query while another query of the same target is enabled locks Nvidia's driver.
101 // To avoid this disable and re-enable keeping the dependency stream. 101 // To avoid this disable and re-enable keeping the dependency stream.
102 // But we only have to do this if we have pending waits to be done. 102 // But we only have to do this if we have pending waits to be done.
@@ -106,11 +106,13 @@ void CachedQuery::Flush() {
106 stream.Update(false); 106 stream.Update(false);
107 } 107 }
108 108
109 VideoCommon::CachedQueryBase<HostCounter>::Flush(); 109 auto result = VideoCommon::CachedQueryBase<HostCounter>::Flush();
110 110
111 if (slice_counter) { 111 if (slice_counter) {
112 stream.Update(true); 112 stream.Update(true);
113 } 113 }
114
115 return result;
114} 116}
115 117
116} // namespace OpenGL 118} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_query_cache.h b/src/video_core/renderer_opengl/gl_query_cache.h
index 14ce59990..872513f22 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.h
+++ b/src/video_core/renderer_opengl/gl_query_cache.h
@@ -28,7 +28,7 @@ using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
28class QueryCache final 28class QueryCache final
29 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { 29 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
30public: 30public:
31 explicit QueryCache(RasterizerOpenGL& rasterizer_); 31 explicit QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_);
32 ~QueryCache(); 32 ~QueryCache();
33 33
34 OGLQuery AllocateQuery(VideoCore::QueryType type); 34 OGLQuery AllocateQuery(VideoCore::QueryType type);
@@ -51,7 +51,7 @@ public:
51 void EndQuery(); 51 void EndQuery();
52 52
53private: 53private:
54 u64 BlockingQuery() const override; 54 u64 BlockingQuery(bool async = false) const override;
55 55
56 QueryCache& cache; 56 QueryCache& cache;
57 const VideoCore::QueryType type; 57 const VideoCore::QueryType type;
@@ -70,7 +70,7 @@ public:
70 CachedQuery(const CachedQuery&) = delete; 70 CachedQuery(const CachedQuery&) = delete;
71 CachedQuery& operator=(const CachedQuery&) = delete; 71 CachedQuery& operator=(const CachedQuery&) = delete;
72 72
73 void Flush() override; 73 u64 Flush(bool async = false) override;
74 74
75private: 75private:
76 QueryCache* cache; 76 QueryCache* cache;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4993d4709..0089b4b27 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -63,7 +63,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
63 buffer_cache(*this, cpu_memory_, buffer_cache_runtime), 63 buffer_cache(*this, cpu_memory_, buffer_cache_runtime),
64 shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, 64 shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager,
65 state_tracker, gpu.ShaderNotify()), 65 state_tracker, gpu.ShaderNotify()),
66 query_cache(*this), accelerate_dma(buffer_cache, texture_cache), 66 query_cache(*this, cpu_memory_), accelerate_dma(buffer_cache, texture_cache),
67 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), 67 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
68 blit_image(program_manager_) {} 68 blit_image(program_manager_) {}
69 69
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 032a8ebc5..47cccd0e5 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -861,9 +861,12 @@ GLuint Image::StorageHandle() noexcept {
861 case PixelFormat::ASTC_2D_8X5_SRGB: 861 case PixelFormat::ASTC_2D_8X5_SRGB:
862 case PixelFormat::ASTC_2D_5X4_SRGB: 862 case PixelFormat::ASTC_2D_5X4_SRGB:
863 case PixelFormat::ASTC_2D_5X5_SRGB: 863 case PixelFormat::ASTC_2D_5X5_SRGB:
864 case PixelFormat::ASTC_2D_10X5_SRGB:
865 case PixelFormat::ASTC_2D_10X6_SRGB:
864 case PixelFormat::ASTC_2D_10X8_SRGB: 866 case PixelFormat::ASTC_2D_10X8_SRGB:
865 case PixelFormat::ASTC_2D_6X6_SRGB: 867 case PixelFormat::ASTC_2D_6X6_SRGB:
866 case PixelFormat::ASTC_2D_10X10_SRGB: 868 case PixelFormat::ASTC_2D_10X10_SRGB:
869 case PixelFormat::ASTC_2D_12X10_SRGB:
867 case PixelFormat::ASTC_2D_12X12_SRGB: 870 case PixelFormat::ASTC_2D_12X12_SRGB:
868 case PixelFormat::ASTC_2D_8X6_SRGB: 871 case PixelFormat::ASTC_2D_8X6_SRGB:
869 case PixelFormat::ASTC_2D_6X5_SRGB: 872 case PixelFormat::ASTC_2D_6X5_SRGB:
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index ef1190e1f..c7dc7e0a1 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -100,10 +100,13 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
100 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM 100 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM
101 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB 101 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB
102 {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM 102 {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM
103 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR}, // ASTC_2D_10X6_SRGB
103 {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}, // ASTC_2D_10X5_UNORM 104 {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}, // ASTC_2D_10X5_UNORM
104 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}, // ASTC_2D_10X5_SRGB 105 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}, // ASTC_2D_10X5_SRGB
105 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM 106 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM
106 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB 107 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB
108 {GL_COMPRESSED_RGBA_ASTC_12x10_KHR}, // ASTC_2D_12X10_UNORM
109 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR}, // ASTC_2D_12X10_SRGB
107 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM 110 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM
108 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB 111 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB
109 {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6_UNORM 112 {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6_UNORM
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 5dce51be8..8853cf0f7 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -197,10 +197,13 @@ struct FormatTuple {
197 {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM 197 {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM
198 {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB 198 {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB
199 {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM 199 {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM
200 {VK_FORMAT_ASTC_10x6_SRGB_BLOCK}, // ASTC_2D_10X6_SRGB
200 {VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, // ASTC_2D_10X5_UNORM 201 {VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, // ASTC_2D_10X5_UNORM
201 {VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, // ASTC_2D_10X5_SRGB 202 {VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, // ASTC_2D_10X5_SRGB
202 {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM 203 {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM
203 {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB 204 {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB
205 {VK_FORMAT_ASTC_12x10_UNORM_BLOCK}, // ASTC_2D_12X10_UNORM
206 {VK_FORMAT_ASTC_12x10_SRGB_BLOCK}, // ASTC_2D_12X10_SRGB
204 {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM 207 {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM
205 {VK_FORMAT_ASTC_12x12_SRGB_BLOCK}, // ASTC_2D_12X12_SRGB 208 {VK_FORMAT_ASTC_12x12_SRGB_BLOCK}, // ASTC_2D_12X12_SRGB
206 {VK_FORMAT_ASTC_8x6_UNORM_BLOCK}, // ASTC_2D_8X6_UNORM 209 {VK_FORMAT_ASTC_8x6_UNORM_BLOCK}, // ASTC_2D_8X6_UNORM
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 2a8d9e377..908625c66 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -93,8 +93,9 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
93 state_tracker(), scheduler(device, state_tracker), 93 state_tracker(), scheduler(device, state_tracker),
94 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, 94 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
95 render_window.GetFramebufferLayout().height, false), 95 render_window.GetFramebufferLayout().height, false),
96 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, 96 present_manager(render_window, device, memory_allocator, scheduler, swapchain),
97 screen_info), 97 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager,
98 scheduler, screen_info),
98 rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator, 99 rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator,
99 state_tracker, scheduler) { 100 state_tracker, scheduler) {
100 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { 101 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
@@ -121,46 +122,19 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
121 return; 122 return;
122 } 123 }
123 // Update screen info if the framebuffer size has changed. 124 // Update screen info if the framebuffer size has changed.
124 if (screen_info.width != framebuffer->width || screen_info.height != framebuffer->height) { 125 screen_info.width = framebuffer->width;
125 screen_info.width = framebuffer->width; 126 screen_info.height = framebuffer->height;
126 screen_info.height = framebuffer->height; 127
127 }
128 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; 128 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
129 const bool use_accelerated = 129 const bool use_accelerated =
130 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 130 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
131 const bool is_srgb = use_accelerated && screen_info.is_srgb; 131 const bool is_srgb = use_accelerated && screen_info.is_srgb;
132 RenderScreenshot(*framebuffer, use_accelerated); 132 RenderScreenshot(*framebuffer, use_accelerated);
133 133
134 bool has_been_recreated = false; 134 Frame* frame = present_manager.GetRenderFrame();
135 const auto recreate_swapchain = [&](u32 width, u32 height) { 135 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
136 if (!has_been_recreated) { 136 scheduler.Flush(*frame->render_ready);
137 has_been_recreated = true; 137 present_manager.Present(frame);
138 scheduler.Finish();
139 }
140 swapchain.Create(width, height, is_srgb);
141 };
142
143 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
144 if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width ||
145 swapchain.GetHeight() != layout.height) {
146 recreate_swapchain(layout.width, layout.height);
147 }
148 bool is_outdated;
149 do {
150 swapchain.AcquireNextImage();
151 is_outdated = swapchain.IsOutDated();
152 if (is_outdated) {
153 recreate_swapchain(layout.width, layout.height);
154 }
155 } while (is_outdated);
156 if (has_been_recreated) {
157 blit_screen.Recreate();
158 }
159 const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated);
160 const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore();
161 scheduler.Flush(render_semaphore, present_semaphore);
162 scheduler.WaitWorker();
163 swapchain.Present(render_semaphore);
164 138
165 gpu.RendererFrameEndNotify(); 139 gpu.RendererFrameEndNotify();
166 rasterizer.TickFrame(); 140 rasterizer.TickFrame();
@@ -246,8 +220,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
246 }); 220 });
247 const VkExtent2D render_area{.width = layout.width, .height = layout.height}; 221 const VkExtent2D render_area{.width = layout.width, .height = layout.height};
248 const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area); 222 const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
249 // Since we're not rendering to the screen, ignore the render semaphore. 223 blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated);
250 void(blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated));
251 224
252 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); 225 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4);
253 const VkBufferCreateInfo dst_buffer_info{ 226 const VkBufferCreateInfo dst_buffer_info{
@@ -270,7 +243,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
270 .pNext = nullptr, 243 .pNext = nullptr,
271 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, 244 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
272 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, 245 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
273 .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 246 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
274 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 247 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
275 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 248 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
276 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 249 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 009e75e0d..f44367cb2 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -9,6 +9,7 @@
9#include "common/dynamic_library.h" 9#include "common/dynamic_library.h"
10#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
11#include "video_core/renderer_vulkan/vk_blit_screen.h" 11#include "video_core/renderer_vulkan/vk_blit_screen.h"
12#include "video_core/renderer_vulkan/vk_present_manager.h"
12#include "video_core/renderer_vulkan/vk_rasterizer.h" 13#include "video_core/renderer_vulkan/vk_rasterizer.h"
13#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
14#include "video_core/renderer_vulkan/vk_state_tracker.h" 15#include "video_core/renderer_vulkan/vk_state_tracker.h"
@@ -76,6 +77,7 @@ private:
76 StateTracker state_tracker; 77 StateTracker state_tracker;
77 Scheduler scheduler; 78 Scheduler scheduler;
78 Swapchain swapchain; 79 Swapchain swapchain;
80 PresentManager present_manager;
79 BlitScreen blit_screen; 81 BlitScreen blit_screen;
80 RasterizerVulkan rasterizer; 82 RasterizerVulkan rasterizer;
81 std::optional<TurboMode> turbo_mode; 83 std::optional<TurboMode> turbo_mode;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 2f0cc27e8..1e0fdd3d9 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -122,10 +122,12 @@ struct BlitScreen::BufferData {
122 122
123BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWindow& render_window_, 123BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWindow& render_window_,
124 const Device& device_, MemoryAllocator& memory_allocator_, 124 const Device& device_, MemoryAllocator& memory_allocator_,
125 Swapchain& swapchain_, Scheduler& scheduler_, const ScreenInfo& screen_info_) 125 Swapchain& swapchain_, PresentManager& present_manager_,
126 Scheduler& scheduler_, const ScreenInfo& screen_info_)
126 : cpu_memory{cpu_memory_}, render_window{render_window_}, device{device_}, 127 : cpu_memory{cpu_memory_}, render_window{render_window_}, device{device_},
127 memory_allocator{memory_allocator_}, swapchain{swapchain_}, scheduler{scheduler_}, 128 memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_},
128 image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { 129 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_},
130 current_srgb{swapchain.IsSrgb()}, image_view_format{swapchain.GetImageViewFormat()} {
129 resource_ticks.resize(image_count); 131 resource_ticks.resize(image_count);
130 132
131 CreateStaticResources(); 133 CreateStaticResources();
@@ -135,25 +137,20 @@ BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWin
135BlitScreen::~BlitScreen() = default; 137BlitScreen::~BlitScreen() = default;
136 138
137void BlitScreen::Recreate() { 139void BlitScreen::Recreate() {
140 present_manager.WaitPresent();
141 scheduler.Finish();
142 device.GetLogical().WaitIdle();
138 CreateDynamicResources(); 143 CreateDynamicResources();
139} 144}
140 145
141VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, 146void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
142 const VkFramebuffer& host_framebuffer, 147 const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout,
143 const Layout::FramebufferLayout layout, VkExtent2D render_area, 148 VkExtent2D render_area, bool use_accelerated) {
144 bool use_accelerated) {
145 RefreshResources(framebuffer); 149 RefreshResources(framebuffer);
146 150
147 // Finish any pending renderpass 151 // Finish any pending renderpass
148 scheduler.RequestOutsideRenderPassOperationContext(); 152 scheduler.RequestOutsideRenderPassOperationContext();
149 153
150 if (const auto swapchain_images = swapchain.GetImageCount(); swapchain_images != image_count) {
151 image_count = swapchain_images;
152 Recreate();
153 }
154
155 const std::size_t image_index = swapchain.GetImageIndex();
156
157 scheduler.Wait(resource_ticks[image_index]); 154 scheduler.Wait(resource_ticks[image_index]);
158 resource_ticks[image_index] = scheduler.CurrentTick(); 155 resource_ticks[image_index] = scheduler.CurrentTick();
159 156
@@ -169,7 +166,7 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
169 std::memcpy(mapped_span.data(), &data, sizeof(data)); 166 std::memcpy(mapped_span.data(), &data, sizeof(data));
170 167
171 if (!use_accelerated) { 168 if (!use_accelerated) {
172 const u64 image_offset = GetRawImageOffset(framebuffer, image_index); 169 const u64 image_offset = GetRawImageOffset(framebuffer);
173 170
174 const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset; 171 const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
175 const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr); 172 const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr);
@@ -204,8 +201,8 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
204 .depth = 1, 201 .depth = 1,
205 }, 202 },
206 }; 203 };
207 scheduler.Record([this, copy, image_index](vk::CommandBuffer cmdbuf) { 204 scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) {
208 const VkImage image = *raw_images[image_index]; 205 const VkImage image = *raw_images[index];
209 const VkImageMemoryBarrier base_barrier{ 206 const VkImageMemoryBarrier base_barrier{
210 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 207 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
211 .pNext = nullptr, 208 .pNext = nullptr,
@@ -245,14 +242,15 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
245 242
246 const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); 243 const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue();
247 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) { 244 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) {
248 UpdateAADescriptorSet(image_index, source_image_view, false); 245 UpdateAADescriptorSet(source_image_view, false);
249 const u32 up_scale = Settings::values.resolution_info.up_scale; 246 const u32 up_scale = Settings::values.resolution_info.up_scale;
250 const u32 down_shift = Settings::values.resolution_info.down_shift; 247 const u32 down_shift = Settings::values.resolution_info.down_shift;
251 VkExtent2D size{ 248 VkExtent2D size{
252 .width = (up_scale * framebuffer.width) >> down_shift, 249 .width = (up_scale * framebuffer.width) >> down_shift,
253 .height = (up_scale * framebuffer.height) >> down_shift, 250 .height = (up_scale * framebuffer.height) >> down_shift,
254 }; 251 };
255 scheduler.Record([this, image_index, size, anti_alias_pass](vk::CommandBuffer cmdbuf) { 252 scheduler.Record([this, index = image_index, size,
253 anti_alias_pass](vk::CommandBuffer cmdbuf) {
256 const VkImageMemoryBarrier base_barrier{ 254 const VkImageMemoryBarrier base_barrier{
257 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 255 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
258 .pNext = nullptr, 256 .pNext = nullptr,
@@ -326,7 +324,7 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
326 324
327 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 325 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
328 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0, 326 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0,
329 aa_descriptor_sets[image_index], {}); 327 aa_descriptor_sets[index], {});
330 cmdbuf.Draw(4, 1, 0, 0); 328 cmdbuf.Draw(4, 1, 0, 0);
331 cmdbuf.EndRenderPass(); 329 cmdbuf.EndRenderPass();
332 330
@@ -369,81 +367,99 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
369 }; 367 };
370 VkImageView fsr_image_view = 368 VkImageView fsr_image_view =
371 fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); 369 fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect);
372 UpdateDescriptorSet(image_index, fsr_image_view, true); 370 UpdateDescriptorSet(fsr_image_view, true);
373 } else { 371 } else {
374 const bool is_nn = 372 const bool is_nn =
375 Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor; 373 Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor;
376 UpdateDescriptorSet(image_index, source_image_view, is_nn); 374 UpdateDescriptorSet(source_image_view, is_nn);
377 } 375 }
378 376
379 scheduler.Record( 377 scheduler.Record([this, host_framebuffer, index = image_index,
380 [this, host_framebuffer, image_index, size = render_area](vk::CommandBuffer cmdbuf) { 378 size = render_area](vk::CommandBuffer cmdbuf) {
381 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; 379 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
382 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; 380 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
383 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; 381 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
384 const VkClearValue clear_color{ 382 const VkClearValue clear_color{
385 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, 383 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
386 }; 384 };
387 const VkRenderPassBeginInfo renderpass_bi{ 385 const VkRenderPassBeginInfo renderpass_bi{
388 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 386 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
389 .pNext = nullptr, 387 .pNext = nullptr,
390 .renderPass = *renderpass, 388 .renderPass = *renderpass,
391 .framebuffer = host_framebuffer, 389 .framebuffer = host_framebuffer,
392 .renderArea = 390 .renderArea =
393 { 391 {
394 .offset = {0, 0}, 392 .offset = {0, 0},
395 .extent = size, 393 .extent = size,
396 }, 394 },
397 .clearValueCount = 1, 395 .clearValueCount = 1,
398 .pClearValues = &clear_color, 396 .pClearValues = &clear_color,
399 }; 397 };
400 const VkViewport viewport{ 398 const VkViewport viewport{
401 .x = 0.0f, 399 .x = 0.0f,
402 .y = 0.0f, 400 .y = 0.0f,
403 .width = static_cast<float>(size.width), 401 .width = static_cast<float>(size.width),
404 .height = static_cast<float>(size.height), 402 .height = static_cast<float>(size.height),
405 .minDepth = 0.0f, 403 .minDepth = 0.0f,
406 .maxDepth = 1.0f, 404 .maxDepth = 1.0f,
407 }; 405 };
408 const VkRect2D scissor{ 406 const VkRect2D scissor{
409 .offset = {0, 0}, 407 .offset = {0, 0},
410 .extent = size, 408 .extent = size,
411 }; 409 };
412 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); 410 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
413 auto graphics_pipeline = [this]() { 411 auto graphics_pipeline = [this]() {
414 switch (Settings::values.scaling_filter.GetValue()) { 412 switch (Settings::values.scaling_filter.GetValue()) {
415 case Settings::ScalingFilter::NearestNeighbor: 413 case Settings::ScalingFilter::NearestNeighbor:
416 case Settings::ScalingFilter::Bilinear: 414 case Settings::ScalingFilter::Bilinear:
417 return *bilinear_pipeline; 415 return *bilinear_pipeline;
418 case Settings::ScalingFilter::Bicubic: 416 case Settings::ScalingFilter::Bicubic:
419 return *bicubic_pipeline; 417 return *bicubic_pipeline;
420 case Settings::ScalingFilter::Gaussian: 418 case Settings::ScalingFilter::Gaussian:
421 return *gaussian_pipeline; 419 return *gaussian_pipeline;
422 case Settings::ScalingFilter::ScaleForce: 420 case Settings::ScalingFilter::ScaleForce:
423 return *scaleforce_pipeline; 421 return *scaleforce_pipeline;
424 default: 422 default:
425 return *bilinear_pipeline; 423 return *bilinear_pipeline;
426 } 424 }
427 }(); 425 }();
428 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); 426 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
429 cmdbuf.SetViewport(0, viewport); 427 cmdbuf.SetViewport(0, viewport);
430 cmdbuf.SetScissor(0, scissor); 428 cmdbuf.SetScissor(0, scissor);
431 429
432 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 430 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
433 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, 431 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
434 descriptor_sets[image_index], {}); 432 descriptor_sets[index], {});
435 cmdbuf.Draw(4, 1, 0, 0); 433 cmdbuf.Draw(4, 1, 0, 0);
436 cmdbuf.EndRenderPass(); 434 cmdbuf.EndRenderPass();
437 }); 435 });
438 return *semaphores[image_index];
439} 436}
440 437
441VkSemaphore BlitScreen::DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer, 438void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
442 bool use_accelerated) { 439 bool use_accelerated, bool is_srgb) {
443 const std::size_t image_index = swapchain.GetImageIndex(); 440 // Recreate dynamic resources if the the image count or colorspace changed
444 const VkExtent2D render_area = swapchain.GetSize(); 441 if (const std::size_t swapchain_images = swapchain.GetImageCount();
442 swapchain_images != image_count || current_srgb != is_srgb) {
443 current_srgb = is_srgb;
444 image_view_format = current_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
445 image_count = swapchain_images;
446 Recreate();
447 }
448
449 // Recreate the presentation frame if the dimensions of the window changed
445 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 450 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
446 return Draw(framebuffer, *framebuffers[image_index], layout, render_area, use_accelerated); 451 if (layout.width != frame->width || layout.height != frame->height ||
452 is_srgb != frame->is_srgb) {
453 Recreate();
454 present_manager.RecreateFrame(frame, layout.width, layout.height, is_srgb,
455 image_view_format, *renderpass);
456 }
457
458 const VkExtent2D render_area{frame->width, frame->height};
459 Draw(framebuffer, *frame->framebuffer, layout, render_area, use_accelerated);
460 if (++image_index >= image_count) {
461 image_index = 0;
462 }
447} 463}
448 464
449vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { 465vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) {
@@ -471,13 +487,11 @@ void BlitScreen::CreateStaticResources() {
471} 487}
472 488
473void BlitScreen::CreateDynamicResources() { 489void BlitScreen::CreateDynamicResources() {
474 CreateSemaphores();
475 CreateDescriptorPool(); 490 CreateDescriptorPool();
476 CreateDescriptorSetLayout(); 491 CreateDescriptorSetLayout();
477 CreateDescriptorSets(); 492 CreateDescriptorSets();
478 CreatePipelineLayout(); 493 CreatePipelineLayout();
479 CreateRenderPass(); 494 CreateRenderPass();
480 CreateFramebuffers();
481 CreateGraphicsPipeline(); 495 CreateGraphicsPipeline();
482 fsr.reset(); 496 fsr.reset();
483 smaa.reset(); 497 smaa.reset();
@@ -525,11 +539,6 @@ void BlitScreen::CreateShaders() {
525 } 539 }
526} 540}
527 541
528void BlitScreen::CreateSemaphores() {
529 semaphores.resize(image_count);
530 std::ranges::generate(semaphores, [this] { return device.GetLogical().CreateSemaphore(); });
531}
532
533void BlitScreen::CreateDescriptorPool() { 542void BlitScreen::CreateDescriptorPool() {
534 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{ 543 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
535 { 544 {
@@ -571,10 +580,10 @@ void BlitScreen::CreateDescriptorPool() {
571} 580}
572 581
573void BlitScreen::CreateRenderPass() { 582void BlitScreen::CreateRenderPass() {
574 renderpass = CreateRenderPassImpl(swapchain.GetImageViewFormat()); 583 renderpass = CreateRenderPassImpl(image_view_format);
575} 584}
576 585
577vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format, bool is_present) { 586vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) {
578 const VkAttachmentDescription color_attachment{ 587 const VkAttachmentDescription color_attachment{
579 .flags = 0, 588 .flags = 0,
580 .format = format, 589 .format = format,
@@ -584,7 +593,7 @@ vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format, bool is_present
584 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, 593 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
585 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, 594 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
586 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 595 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
587 .finalLayout = is_present ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_GENERAL, 596 .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
588 }; 597 };
589 598
590 const VkAttachmentReference color_attachment_ref{ 599 const VkAttachmentReference color_attachment_ref{
@@ -1052,16 +1061,6 @@ void BlitScreen::CreateSampler() {
1052 nn_sampler = device.GetLogical().CreateSampler(ci_nn); 1061 nn_sampler = device.GetLogical().CreateSampler(ci_nn);
1053} 1062}
1054 1063
1055void BlitScreen::CreateFramebuffers() {
1056 const VkExtent2D size{swapchain.GetSize()};
1057 framebuffers.resize(image_count);
1058
1059 for (std::size_t i = 0; i < image_count; ++i) {
1060 const VkImageView image_view{swapchain.GetImageViewIndex(i)};
1061 framebuffers[i] = CreateFramebuffer(image_view, size, renderpass);
1062 }
1063}
1064
1065void BlitScreen::ReleaseRawImages() { 1064void BlitScreen::ReleaseRawImages() {
1066 for (const u64 tick : resource_ticks) { 1065 for (const u64 tick : resource_ticks) {
1067 scheduler.Wait(tick); 1066 scheduler.Wait(tick);
@@ -1175,7 +1174,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1175 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); 1174 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1176 return; 1175 return;
1177 } 1176 }
1178 aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer), false); 1177 aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer));
1179 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); 1178 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1180 1179
1181 const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{ 1180 const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{
@@ -1319,8 +1318,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1319 aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci); 1318 aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci);
1320} 1319}
1321 1320
1322void BlitScreen::UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, 1321void BlitScreen::UpdateAADescriptorSet(VkImageView image_view, bool nn) const {
1323 bool nn) const {
1324 const VkDescriptorImageInfo image_info{ 1322 const VkDescriptorImageInfo image_info{
1325 .sampler = nn ? *nn_sampler : *sampler, 1323 .sampler = nn ? *nn_sampler : *sampler,
1326 .imageView = image_view, 1324 .imageView = image_view,
@@ -1356,8 +1354,7 @@ void BlitScreen::UpdateAADescriptorSet(std::size_t image_index, VkImageView imag
1356 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {}); 1354 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {});
1357} 1355}
1358 1356
1359void BlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, 1357void BlitScreen::UpdateDescriptorSet(VkImageView image_view, bool nn) const {
1360 bool nn) const {
1361 const VkDescriptorBufferInfo buffer_info{ 1358 const VkDescriptorBufferInfo buffer_info{
1362 .buffer = *buffer, 1359 .buffer = *buffer,
1363 .offset = offsetof(BufferData, uniform), 1360 .offset = offsetof(BufferData, uniform),
@@ -1480,8 +1477,7 @@ u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer)
1480 return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count; 1477 return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count;
1481} 1478}
1482 1479
1483u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, 1480u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const {
1484 std::size_t image_index) const {
1485 constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData)); 1481 constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData));
1486 return first_image_offset + GetSizeInBytes(framebuffer) * image_index; 1482 return first_image_offset + GetSizeInBytes(framebuffer) * image_index;
1487} 1483}
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index ebe10b08b..68ec20253 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -5,6 +5,7 @@
5 5
6#include <memory> 6#include <memory>
7 7
8#include "core/frontend/framebuffer_layout.h"
8#include "video_core/vulkan_common/vulkan_memory_allocator.h" 9#include "video_core/vulkan_common/vulkan_memory_allocator.h"
9#include "video_core/vulkan_common/vulkan_wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
10 11
@@ -42,6 +43,9 @@ class RasterizerVulkan;
42class Scheduler; 43class Scheduler;
43class SMAA; 44class SMAA;
44class Swapchain; 45class Swapchain;
46class PresentManager;
47
48struct Frame;
45 49
46struct ScreenInfo { 50struct ScreenInfo {
47 VkImage image{}; 51 VkImage image{};
@@ -55,18 +59,17 @@ class BlitScreen {
55public: 59public:
56 explicit BlitScreen(Core::Memory::Memory& cpu_memory, Core::Frontend::EmuWindow& render_window, 60 explicit BlitScreen(Core::Memory::Memory& cpu_memory, Core::Frontend::EmuWindow& render_window,
57 const Device& device, MemoryAllocator& memory_manager, Swapchain& swapchain, 61 const Device& device, MemoryAllocator& memory_manager, Swapchain& swapchain,
58 Scheduler& scheduler, const ScreenInfo& screen_info); 62 PresentManager& present_manager, Scheduler& scheduler,
63 const ScreenInfo& screen_info);
59 ~BlitScreen(); 64 ~BlitScreen();
60 65
61 void Recreate(); 66 void Recreate();
62 67
63 [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer, 68 void Draw(const Tegra::FramebufferConfig& framebuffer, const VkFramebuffer& host_framebuffer,
64 const VkFramebuffer& host_framebuffer, 69 const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated);
65 const Layout::FramebufferLayout layout, VkExtent2D render_area,
66 bool use_accelerated);
67 70
68 [[nodiscard]] VkSemaphore DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer, 71 void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
69 bool use_accelerated); 72 bool use_accelerated, bool is_srgb);
70 73
71 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, 74 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
72 VkExtent2D extent); 75 VkExtent2D extent);
@@ -79,10 +82,9 @@ private:
79 82
80 void CreateStaticResources(); 83 void CreateStaticResources();
81 void CreateShaders(); 84 void CreateShaders();
82 void CreateSemaphores();
83 void CreateDescriptorPool(); 85 void CreateDescriptorPool();
84 void CreateRenderPass(); 86 void CreateRenderPass();
85 vk::RenderPass CreateRenderPassImpl(VkFormat, bool is_present = true); 87 vk::RenderPass CreateRenderPassImpl(VkFormat format);
86 void CreateDescriptorSetLayout(); 88 void CreateDescriptorSetLayout();
87 void CreateDescriptorSets(); 89 void CreateDescriptorSets();
88 void CreatePipelineLayout(); 90 void CreatePipelineLayout();
@@ -90,15 +92,14 @@ private:
90 void CreateSampler(); 92 void CreateSampler();
91 93
92 void CreateDynamicResources(); 94 void CreateDynamicResources();
93 void CreateFramebuffers();
94 95
95 void RefreshResources(const Tegra::FramebufferConfig& framebuffer); 96 void RefreshResources(const Tegra::FramebufferConfig& framebuffer);
96 void ReleaseRawImages(); 97 void ReleaseRawImages();
97 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); 98 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
98 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); 99 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
99 100
100 void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const; 101 void UpdateDescriptorSet(VkImageView image_view, bool nn) const;
101 void UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const; 102 void UpdateAADescriptorSet(VkImageView image_view, bool nn) const;
102 void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const; 103 void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
103 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, 104 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
104 const Layout::FramebufferLayout layout) const; 105 const Layout::FramebufferLayout layout) const;
@@ -107,16 +108,17 @@ private:
107 void CreateFSR(); 108 void CreateFSR();
108 109
109 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; 110 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
110 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, 111 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const;
111 std::size_t image_index) const;
112 112
113 Core::Memory::Memory& cpu_memory; 113 Core::Memory::Memory& cpu_memory;
114 Core::Frontend::EmuWindow& render_window; 114 Core::Frontend::EmuWindow& render_window;
115 const Device& device; 115 const Device& device;
116 MemoryAllocator& memory_allocator; 116 MemoryAllocator& memory_allocator;
117 Swapchain& swapchain; 117 Swapchain& swapchain;
118 PresentManager& present_manager;
118 Scheduler& scheduler; 119 Scheduler& scheduler;
119 std::size_t image_count; 120 std::size_t image_count;
121 std::size_t image_index{};
120 const ScreenInfo& screen_info; 122 const ScreenInfo& screen_info;
121 123
122 vk::ShaderModule vertex_shader; 124 vk::ShaderModule vertex_shader;
@@ -135,7 +137,6 @@ private:
135 vk::Pipeline gaussian_pipeline; 137 vk::Pipeline gaussian_pipeline;
136 vk::Pipeline scaleforce_pipeline; 138 vk::Pipeline scaleforce_pipeline;
137 vk::RenderPass renderpass; 139 vk::RenderPass renderpass;
138 std::vector<vk::Framebuffer> framebuffers;
139 vk::DescriptorSets descriptor_sets; 140 vk::DescriptorSets descriptor_sets;
140 vk::Sampler nn_sampler; 141 vk::Sampler nn_sampler;
141 vk::Sampler sampler; 142 vk::Sampler sampler;
@@ -145,7 +146,6 @@ private:
145 146
146 std::vector<u64> resource_ticks; 147 std::vector<u64> resource_ticks;
147 148
148 std::vector<vk::Semaphore> semaphores;
149 std::vector<vk::Image> raw_images; 149 std::vector<vk::Image> raw_images;
150 std::vector<vk::ImageView> raw_image_views; 150 std::vector<vk::ImageView> raw_image_views;
151 std::vector<MemoryCommit> raw_buffer_commits; 151 std::vector<MemoryCommit> raw_buffer_commits;
@@ -164,6 +164,8 @@ private:
164 u32 raw_width = 0; 164 u32 raw_width = 0;
165 u32 raw_height = 0; 165 u32 raw_height = 0;
166 Service::android::PixelFormat pixel_format{}; 166 Service::android::PixelFormat pixel_format{};
167 bool current_srgb;
168 VkFormat image_view_format;
167 169
168 std::unique_ptr<FSR> fsr; 170 std::unique_ptr<FSR> fsr;
169 std::unique_ptr<SMAA> smaa; 171 std::unique_ptr<SMAA> smaa;
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index 0214b103a..fad9e3832 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -5,6 +5,7 @@
5 5
6#include "video_core/renderer_vulkan/vk_buffer_cache.h" 6#include "video_core/renderer_vulkan/vk_buffer_cache.h"
7#include "video_core/renderer_vulkan/vk_fence_manager.h" 7#include "video_core/renderer_vulkan/vk_fence_manager.h"
8#include "video_core/renderer_vulkan/vk_query_cache.h"
8#include "video_core/renderer_vulkan/vk_scheduler.h" 9#include "video_core/renderer_vulkan/vk_scheduler.h"
9#include "video_core/renderer_vulkan/vk_texture_cache.h" 10#include "video_core/renderer_vulkan/vk_texture_cache.h"
10#include "video_core/vulkan_common/vulkan_device.h" 11#include "video_core/vulkan_common/vulkan_device.h"
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
index 7fe2afcd9..145359d4e 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.h
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -40,7 +40,16 @@ private:
40}; 40};
41using Fence = std::shared_ptr<InnerFence>; 41using Fence = std::shared_ptr<InnerFence>;
42 42
43using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>; 43struct FenceManagerParams {
44 using FenceType = Fence;
45 using BufferCacheType = BufferCache;
46 using TextureCacheType = TextureCache;
47 using QueryCacheType = QueryCache;
48
49 static constexpr bool HAS_ASYNC_CHECK = true;
50};
51
52using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>;
44 53
45class FenceManager final : public GenericFenceManager { 54class FenceManager final : public GenericFenceManager {
46public: 55public:
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
new file mode 100644
index 000000000..c49583013
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -0,0 +1,457 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/microprofile.h"
5#include "common/settings.h"
6#include "common/thread.h"
7#include "video_core/renderer_vulkan/vk_present_manager.h"
8#include "video_core/renderer_vulkan/vk_scheduler.h"
9#include "video_core/renderer_vulkan/vk_swapchain.h"
10#include "video_core/vulkan_common/vulkan_device.h"
11
12namespace Vulkan {
13
14MICROPROFILE_DEFINE(Vulkan_WaitPresent, "Vulkan", "Wait For Present", MP_RGB(128, 128, 128));
15MICROPROFILE_DEFINE(Vulkan_CopyToSwapchain, "Vulkan", "Copy to swapchain", MP_RGB(192, 255, 192));
16
17namespace {
18
19bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat format) {
20 const VkFormatProperties props{physical_device.GetFormatProperties(format)};
21 return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT);
22}
23
24[[nodiscard]] VkImageSubresourceLayers MakeImageSubresourceLayers() {
25 return VkImageSubresourceLayers{
26 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
27 .mipLevel = 0,
28 .baseArrayLayer = 0,
29 .layerCount = 1,
30 };
31}
32
33[[nodiscard]] VkImageBlit MakeImageBlit(s32 frame_width, s32 frame_height, s32 swapchain_width,
34 s32 swapchain_height) {
35 return VkImageBlit{
36 .srcSubresource = MakeImageSubresourceLayers(),
37 .srcOffsets =
38 {
39 {
40 .x = 0,
41 .y = 0,
42 .z = 0,
43 },
44 {
45 .x = frame_width,
46 .y = frame_height,
47 .z = 1,
48 },
49 },
50 .dstSubresource = MakeImageSubresourceLayers(),
51 .dstOffsets =
52 {
53 {
54 .x = 0,
55 .y = 0,
56 .z = 0,
57 },
58 {
59 .x = swapchain_width,
60 .y = swapchain_height,
61 .z = 1,
62 },
63 },
64 };
65}
66
67[[nodiscard]] VkImageCopy MakeImageCopy(u32 frame_width, u32 frame_height, u32 swapchain_width,
68 u32 swapchain_height) {
69 return VkImageCopy{
70 .srcSubresource = MakeImageSubresourceLayers(),
71 .srcOffset =
72 {
73 .x = 0,
74 .y = 0,
75 .z = 0,
76 },
77 .dstSubresource = MakeImageSubresourceLayers(),
78 .dstOffset =
79 {
80 .x = 0,
81 .y = 0,
82 .z = 0,
83 },
84 .extent =
85 {
86 .width = std::min(frame_width, swapchain_width),
87 .height = std::min(frame_height, swapchain_height),
88 .depth = 1,
89 },
90 };
91}
92
93} // Anonymous namespace
94
95PresentManager::PresentManager(Core::Frontend::EmuWindow& render_window_, const Device& device_,
96 MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
97 Swapchain& swapchain_)
98 : render_window{render_window_}, device{device_},
99 memory_allocator{memory_allocator_}, scheduler{scheduler_}, swapchain{swapchain_},
100 blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())},
101 use_present_thread{Settings::values.async_presentation.GetValue()},
102 image_count{swapchain.GetImageCount()} {
103
104 auto& dld = device.GetLogical();
105 cmdpool = dld.CreateCommandPool({
106 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
107 .pNext = nullptr,
108 .flags =
109 VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
110 .queueFamilyIndex = device.GetGraphicsFamily(),
111 });
112 auto cmdbuffers = cmdpool.Allocate(image_count);
113
114 frames.resize(image_count);
115 for (u32 i = 0; i < frames.size(); i++) {
116 Frame& frame = frames[i];
117 frame.cmdbuf = vk::CommandBuffer{cmdbuffers[i], device.GetDispatchLoader()};
118 frame.render_ready = dld.CreateSemaphore({
119 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
120 .pNext = nullptr,
121 .flags = 0,
122 });
123 frame.present_done = dld.CreateFence({
124 .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
125 .pNext = nullptr,
126 .flags = VK_FENCE_CREATE_SIGNALED_BIT,
127 });
128 free_queue.push(&frame);
129 }
130
131 if (use_present_thread) {
132 present_thread = std::jthread([this](std::stop_token token) { PresentThread(token); });
133 }
134}
135
136PresentManager::~PresentManager() = default;
137
138Frame* PresentManager::GetRenderFrame() {
139 MICROPROFILE_SCOPE(Vulkan_WaitPresent);
140
141 // Wait for free presentation frames
142 std::unique_lock lock{free_mutex};
143 free_cv.wait(lock, [this] { return !free_queue.empty(); });
144
145 // Take the frame from the queue
146 Frame* frame = free_queue.front();
147 free_queue.pop();
148
149 // Wait for the presentation to be finished so all frame resources are free
150 frame->present_done.Wait();
151 frame->present_done.Reset();
152
153 return frame;
154}
155
156void PresentManager::Present(Frame* frame) {
157 if (!use_present_thread) {
158 scheduler.WaitWorker();
159 CopyToSwapchain(frame);
160 free_queue.push(frame);
161 return;
162 }
163
164 scheduler.Record([this, frame](vk::CommandBuffer) {
165 std::unique_lock lock{queue_mutex};
166 present_queue.push(frame);
167 frame_cv.notify_one();
168 });
169}
170
171void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb,
172 VkFormat image_view_format, VkRenderPass rd) {
173 auto& dld = device.GetLogical();
174
175 frame->width = width;
176 frame->height = height;
177 frame->is_srgb = is_srgb;
178
179 frame->image = dld.CreateImage({
180 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
181 .pNext = nullptr,
182 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
183 .imageType = VK_IMAGE_TYPE_2D,
184 .format = swapchain.GetImageFormat(),
185 .extent =
186 {
187 .width = width,
188 .height = height,
189 .depth = 1,
190 },
191 .mipLevels = 1,
192 .arrayLayers = 1,
193 .samples = VK_SAMPLE_COUNT_1_BIT,
194 .tiling = VK_IMAGE_TILING_OPTIMAL,
195 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
196 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
197 .queueFamilyIndexCount = 0,
198 .pQueueFamilyIndices = nullptr,
199 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
200 });
201
202 frame->image_commit = memory_allocator.Commit(frame->image, MemoryUsage::DeviceLocal);
203
204 frame->image_view = dld.CreateImageView({
205 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
206 .pNext = nullptr,
207 .flags = 0,
208 .image = *frame->image,
209 .viewType = VK_IMAGE_VIEW_TYPE_2D,
210 .format = image_view_format,
211 .components =
212 {
213 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
214 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
215 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
216 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
217 },
218 .subresourceRange =
219 {
220 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
221 .baseMipLevel = 0,
222 .levelCount = 1,
223 .baseArrayLayer = 0,
224 .layerCount = 1,
225 },
226 });
227
228 const VkImageView image_view{*frame->image_view};
229 frame->framebuffer = dld.CreateFramebuffer({
230 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
231 .pNext = nullptr,
232 .flags = 0,
233 .renderPass = rd,
234 .attachmentCount = 1,
235 .pAttachments = &image_view,
236 .width = width,
237 .height = height,
238 .layers = 1,
239 });
240}
241
242void PresentManager::WaitPresent() {
243 if (!use_present_thread) {
244 return;
245 }
246
247 // Wait for the present queue to be empty
248 {
249 std::unique_lock queue_lock{queue_mutex};
250 frame_cv.wait(queue_lock, [this] { return present_queue.empty(); });
251 }
252
253 // The above condition will be satisfied when the last frame is taken from the queue.
254 // To ensure that frame has been presented as well take hold of the swapchain
255 // mutex.
256 std::scoped_lock swapchain_lock{swapchain_mutex};
257}
258
259void PresentManager::PresentThread(std::stop_token token) {
260 Common::SetCurrentThreadName("VulkanPresent");
261 while (!token.stop_requested()) {
262 std::unique_lock lock{queue_mutex};
263
264 // Wait for presentation frames
265 Common::CondvarWait(frame_cv, lock, token, [this] { return !present_queue.empty(); });
266 if (token.stop_requested()) {
267 return;
268 }
269
270 // Take the frame and notify anyone waiting
271 Frame* frame = present_queue.front();
272 present_queue.pop();
273 frame_cv.notify_one();
274
275 // By exchanging the lock ownership we take the swapchain lock
276 // before the queue lock goes out of scope. This way the swapchain
277 // lock in WaitPresent is guaranteed to occur after here.
278 std::exchange(lock, std::unique_lock{swapchain_mutex});
279
280 CopyToSwapchain(frame);
281
282 // Free the frame for reuse
283 std::scoped_lock fl{free_mutex};
284 free_queue.push(frame);
285 free_cv.notify_one();
286 }
287}
288
289void PresentManager::CopyToSwapchain(Frame* frame) {
290 MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain);
291
292 const auto recreate_swapchain = [&] {
293 swapchain.Create(frame->width, frame->height, frame->is_srgb);
294 image_count = swapchain.GetImageCount();
295 };
296
297 // If the size or colorspace of the incoming frames has changed, recreate the swapchain
298 // to account for that.
299 const bool srgb_changed = swapchain.NeedsRecreation(frame->is_srgb);
300 const bool size_changed =
301 swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height;
302 if (srgb_changed || size_changed) {
303 recreate_swapchain();
304 }
305
306 while (swapchain.AcquireNextImage()) {
307 recreate_swapchain();
308 }
309
310 const vk::CommandBuffer cmdbuf{frame->cmdbuf};
311 cmdbuf.Begin({
312 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
313 .pNext = nullptr,
314 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
315 .pInheritanceInfo = nullptr,
316 });
317
318 const VkImage image{swapchain.CurrentImage()};
319 const VkExtent2D extent = swapchain.GetExtent();
320 const std::array pre_barriers{
321 VkImageMemoryBarrier{
322 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
323 .pNext = nullptr,
324 .srcAccessMask = 0,
325 .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
326 .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
327 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
328 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
329 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
330 .image = image,
331 .subresourceRange{
332 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
333 .baseMipLevel = 0,
334 .levelCount = 1,
335 .baseArrayLayer = 0,
336 .layerCount = VK_REMAINING_ARRAY_LAYERS,
337 },
338 },
339 VkImageMemoryBarrier{
340 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
341 .pNext = nullptr,
342 .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
343 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
344 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
345 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
346 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
347 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
348 .image = *frame->image,
349 .subresourceRange{
350 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
351 .baseMipLevel = 0,
352 .levelCount = 1,
353 .baseArrayLayer = 0,
354 .layerCount = VK_REMAINING_ARRAY_LAYERS,
355 },
356 },
357 };
358 const std::array post_barriers{
359 VkImageMemoryBarrier{
360 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
361 .pNext = nullptr,
362 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
363 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
364 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
365 .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
366 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
367 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
368 .image = image,
369 .subresourceRange{
370 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
371 .baseMipLevel = 0,
372 .levelCount = 1,
373 .baseArrayLayer = 0,
374 .layerCount = VK_REMAINING_ARRAY_LAYERS,
375 },
376 },
377 VkImageMemoryBarrier{
378 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
379 .pNext = nullptr,
380 .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
381 .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
382 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
383 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
384 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
385 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
386 .image = *frame->image,
387 .subresourceRange{
388 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
389 .baseMipLevel = 0,
390 .levelCount = 1,
391 .baseArrayLayer = 0,
392 .layerCount = VK_REMAINING_ARRAY_LAYERS,
393 },
394 },
395 };
396
397 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, {},
398 {}, {}, pre_barriers);
399
400 if (blit_supported) {
401 cmdbuf.BlitImage(*frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image,
402 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
403 MakeImageBlit(frame->width, frame->height, extent.width, extent.height),
404 VK_FILTER_LINEAR);
405 } else {
406 cmdbuf.CopyImage(*frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image,
407 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
408 MakeImageCopy(frame->width, frame->height, extent.width, extent.height));
409 }
410
411 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, {},
412 {}, {}, post_barriers);
413
414 cmdbuf.End();
415
416 const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore();
417 const VkSemaphore render_semaphore = swapchain.CurrentRenderSemaphore();
418 const std::array wait_semaphores = {present_semaphore, *frame->render_ready};
419
420 static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
421 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
422 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
423 };
424
425 const VkSubmitInfo submit_info{
426 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
427 .pNext = nullptr,
428 .waitSemaphoreCount = 2U,
429 .pWaitSemaphores = wait_semaphores.data(),
430 .pWaitDstStageMask = wait_stage_masks.data(),
431 .commandBufferCount = 1,
432 .pCommandBuffers = cmdbuf.address(),
433 .signalSemaphoreCount = 1U,
434 .pSignalSemaphores = &render_semaphore,
435 };
436
437 // Submit the image copy/blit to the swapchain
438 {
439 std::scoped_lock lock{scheduler.submit_mutex};
440 switch (const VkResult result =
441 device.GetGraphicsQueue().Submit(submit_info, *frame->present_done)) {
442 case VK_SUCCESS:
443 break;
444 case VK_ERROR_DEVICE_LOST:
445 device.ReportLoss();
446 [[fallthrough]];
447 default:
448 vk::Check(result);
449 break;
450 }
451 }
452
453 // Present
454 swapchain.Present(render_semaphore);
455}
456
457} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.h b/src/video_core/renderer_vulkan/vk_present_manager.h
new file mode 100644
index 000000000..420a775e2
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_present_manager.h
@@ -0,0 +1,83 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <condition_variable>
7#include <mutex>
8#include <queue>
9
10#include "common/common_types.h"
11#include "common/polyfill_thread.h"
12#include "video_core/vulkan_common/vulkan_memory_allocator.h"
13#include "video_core/vulkan_common/vulkan_wrapper.h"
14
15namespace Core::Frontend {
16class EmuWindow;
17} // namespace Core::Frontend
18
19namespace Vulkan {
20
21class Device;
22class Scheduler;
23class Swapchain;
24
25struct Frame {
26 u32 width;
27 u32 height;
28 bool is_srgb;
29 vk::Image image;
30 vk::ImageView image_view;
31 vk::Framebuffer framebuffer;
32 MemoryCommit image_commit;
33 vk::CommandBuffer cmdbuf;
34 vk::Semaphore render_ready;
35 vk::Fence present_done;
36};
37
38class PresentManager {
39public:
40 PresentManager(Core::Frontend::EmuWindow& render_window, const Device& device,
41 MemoryAllocator& memory_allocator, Scheduler& scheduler, Swapchain& swapchain);
42 ~PresentManager();
43
44 /// Returns the last used presentation frame
45 Frame* GetRenderFrame();
46
47 /// Pushes a frame for presentation
48 void Present(Frame* frame);
49
50 /// Recreates the present frame to match the provided parameters
51 void RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb,
52 VkFormat image_view_format, VkRenderPass rd);
53
54 /// Waits for the present thread to finish presenting all queued frames.
55 void WaitPresent();
56
57private:
58 void PresentThread(std::stop_token token);
59
60 void CopyToSwapchain(Frame* frame);
61
62private:
63 Core::Frontend::EmuWindow& render_window;
64 const Device& device;
65 MemoryAllocator& memory_allocator;
66 Scheduler& scheduler;
67 Swapchain& swapchain;
68 vk::CommandPool cmdpool;
69 std::vector<Frame> frames;
70 std::queue<Frame*> present_queue;
71 std::queue<Frame*> free_queue;
72 std::condition_variable_any frame_cv;
73 std::condition_variable free_cv;
74 std::mutex swapchain_mutex;
75 std::mutex queue_mutex;
76 std::mutex free_mutex;
77 std::jthread present_thread;
78 bool blit_supported;
79 bool use_present_thread;
80 std::size_t image_count;
81};
82
83} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 929c8ece6..d67490449 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -66,9 +66,10 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
66 } 66 }
67} 67}
68 68
69QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, 69QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_,
70 Core::Memory::Memory& cpu_memory_, const Device& device_,
70 Scheduler& scheduler_) 71 Scheduler& scheduler_)
71 : QueryCacheBase{rasterizer_}, device{device_}, scheduler{scheduler_}, 72 : QueryCacheBase{rasterizer_, cpu_memory_}, device{device_}, scheduler{scheduler_},
72 query_pools{ 73 query_pools{
73 QueryPool{device_, scheduler_, QueryType::SamplesPassed}, 74 QueryPool{device_, scheduler_, QueryType::SamplesPassed},
74 } {} 75 } {}
@@ -98,8 +99,10 @@ HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr<HostCounter> depend
98 query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} { 99 query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} {
99 const vk::Device* logical = &cache.GetDevice().GetLogical(); 100 const vk::Device* logical = &cache.GetDevice().GetLogical();
100 cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { 101 cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
102 const bool use_precise = Settings::IsGPULevelHigh();
101 logical->ResetQueryPool(query.first, query.second, 1); 103 logical->ResetQueryPool(query.first, query.second, 1);
102 cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT); 104 cmdbuf.BeginQuery(query.first, query.second,
105 use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0);
103 }); 106 });
104} 107}
105 108
@@ -112,8 +115,10 @@ void HostCounter::EndQuery() {
112 [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); }); 115 [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); });
113} 116}
114 117
115u64 HostCounter::BlockingQuery() const { 118u64 HostCounter::BlockingQuery(bool async) const {
116 cache.GetScheduler().Wait(tick); 119 if (!async) {
120 cache.GetScheduler().Wait(tick);
121 }
117 u64 data; 122 u64 data;
118 const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults( 123 const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults(
119 query.first, query.second, 1, sizeof(data), &data, sizeof(data), 124 query.first, query.second, 1, sizeof(data), &data, sizeof(data),
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h
index 26762ee09..c1b9552eb 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.h
+++ b/src/video_core/renderer_vulkan/vk_query_cache.h
@@ -52,7 +52,8 @@ private:
52class QueryCache final 52class QueryCache final
53 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { 53 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
54public: 54public:
55 explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, 55 explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_,
56 Core::Memory::Memory& cpu_memory_, const Device& device_,
56 Scheduler& scheduler_); 57 Scheduler& scheduler_);
57 ~QueryCache(); 58 ~QueryCache();
58 59
@@ -83,7 +84,7 @@ public:
83 void EndQuery(); 84 void EndQuery();
84 85
85private: 86private:
86 u64 BlockingQuery() const override; 87 u64 BlockingQuery(bool async = false) const override;
87 88
88 QueryCache& cache; 89 QueryCache& cache;
89 const VideoCore::QueryType type; 90 const VideoCore::QueryType type;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 2559a3aa7..d1489fc95 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -172,7 +172,8 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
172 buffer_cache(*this, cpu_memory_, buffer_cache_runtime), 172 buffer_cache(*this, cpu_memory_, buffer_cache_runtime),
173 pipeline_cache(*this, device, scheduler, descriptor_pool, update_descriptor_queue, 173 pipeline_cache(*this, device, scheduler, descriptor_pool, update_descriptor_queue,
174 render_pass_cache, buffer_cache, texture_cache, gpu.ShaderNotify()), 174 render_pass_cache, buffer_cache, texture_cache, gpu.ShaderNotify()),
175 query_cache{*this, device, scheduler}, accelerate_dma(buffer_cache, texture_cache, scheduler), 175 query_cache{*this, cpu_memory_, device, scheduler},
176 accelerate_dma(buffer_cache, texture_cache, scheduler),
176 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), 177 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
177 wfi_event(device.GetLogical().CreateEvent()) { 178 wfi_event(device.GetLogical().CreateEvent()) {
178 scheduler.SetQueryCache(query_cache); 179 scheduler.SetQueryCache(query_cache);
@@ -675,7 +676,8 @@ bool RasterizerVulkan::AccelerateConditionalRendering() {
675 const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()}; 676 const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
676 Maxwell::ReportSemaphore::Compare cmp; 677 Maxwell::ReportSemaphore::Compare cmp;
677 if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp), 678 if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
678 VideoCommon::CacheType::BufferCache)) { 679 VideoCommon::CacheType::BufferCache |
680 VideoCommon::CacheType::QueryCache)) {
679 return true; 681 return true;
680 } 682 }
681 return false; 683 return false;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 057e16967..80455ec08 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -46,10 +46,11 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_)
46 46
47Scheduler::~Scheduler() = default; 47Scheduler::~Scheduler() = default;
48 48
49void Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 49u64 Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
50 // When flushing, we only send data to the worker thread; no waiting is necessary. 50 // When flushing, we only send data to the worker thread; no waiting is necessary.
51 SubmitExecution(signal_semaphore, wait_semaphore); 51 const u64 signal_value = SubmitExecution(signal_semaphore, wait_semaphore);
52 AllocateNewContext(); 52 AllocateNewContext();
53 return signal_value;
53} 54}
54 55
55void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 56void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
@@ -205,7 +206,7 @@ void Scheduler::AllocateWorkerCommandBuffer() {
205 }); 206 });
206} 207}
207 208
208void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 209u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
209 EndPendingOperations(); 210 EndPendingOperations();
210 InvalidateState(); 211 InvalidateState();
211 212
@@ -217,6 +218,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
217 on_submit(); 218 on_submit();
218 } 219 }
219 220
221 std::scoped_lock lock{submit_mutex};
220 switch (const VkResult result = master_semaphore->SubmitQueue( 222 switch (const VkResult result = master_semaphore->SubmitQueue(
221 cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { 223 cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
222 case VK_SUCCESS: 224 case VK_SUCCESS:
@@ -231,6 +233,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
231 }); 233 });
232 chunk->MarkSubmit(); 234 chunk->MarkSubmit();
233 DispatchWork(); 235 DispatchWork();
236 return signal_value;
234} 237}
235 238
236void Scheduler::AllocateNewContext() { 239void Scheduler::AllocateNewContext() {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 8d75ce987..475c682eb 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -34,7 +34,7 @@ public:
34 ~Scheduler(); 34 ~Scheduler();
35 35
36 /// Sends the current execution context to the GPU. 36 /// Sends the current execution context to the GPU.
37 void Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); 37 u64 Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
38 38
39 /// Sends the current execution context to the GPU and waits for it to complete. 39 /// Sends the current execution context to the GPU and waits for it to complete.
40 void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); 40 void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
@@ -106,6 +106,8 @@ public:
106 return *master_semaphore; 106 return *master_semaphore;
107 } 107 }
108 108
109 std::mutex submit_mutex;
110
109private: 111private:
110 class Command { 112 class Command {
111 public: 113 public:
@@ -201,7 +203,7 @@ private:
201 203
202 void AllocateWorkerCommandBuffer(); 204 void AllocateWorkerCommandBuffer();
203 205
204 void SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore); 206 u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
205 207
206 void AllocateNewContext(); 208 void AllocateNewContext();
207 209
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index b1465e35c..23bbea7f1 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -99,18 +99,16 @@ void Swapchain::Create(u32 width_, u32 height_, bool srgb) {
99 return; 99 return;
100 } 100 }
101 101
102 device.GetLogical().WaitIdle();
103 Destroy(); 102 Destroy();
104 103
105 CreateSwapchain(capabilities, srgb); 104 CreateSwapchain(capabilities, srgb);
106 CreateSemaphores(); 105 CreateSemaphores();
107 CreateImageViews();
108 106
109 resource_ticks.clear(); 107 resource_ticks.clear();
110 resource_ticks.resize(image_count); 108 resource_ticks.resize(image_count);
111} 109}
112 110
113void Swapchain::AcquireNextImage() { 111bool Swapchain::AcquireNextImage() {
114 const VkResult result = device.GetLogical().AcquireNextImageKHR( 112 const VkResult result = device.GetLogical().AcquireNextImageKHR(
115 *swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index], 113 *swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index],
116 VK_NULL_HANDLE, &image_index); 114 VK_NULL_HANDLE, &image_index);
@@ -127,8 +125,11 @@ void Swapchain::AcquireNextImage() {
127 LOG_ERROR(Render_Vulkan, "vkAcquireNextImageKHR returned {}", vk::ToString(result)); 125 LOG_ERROR(Render_Vulkan, "vkAcquireNextImageKHR returned {}", vk::ToString(result));
128 break; 126 break;
129 } 127 }
128
130 scheduler.Wait(resource_ticks[image_index]); 129 scheduler.Wait(resource_ticks[image_index]);
131 resource_ticks[image_index] = scheduler.CurrentTick(); 130 resource_ticks[image_index] = scheduler.CurrentTick();
131
132 return is_suboptimal || is_outdated;
132} 133}
133 134
134void Swapchain::Present(VkSemaphore render_semaphore) { 135void Swapchain::Present(VkSemaphore render_semaphore) {
@@ -143,6 +144,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
143 .pImageIndices = &image_index, 144 .pImageIndices = &image_index,
144 .pResults = nullptr, 145 .pResults = nullptr,
145 }; 146 };
147 std::scoped_lock lock{scheduler.submit_mutex};
146 switch (const VkResult result = present_queue.Present(present_info)) { 148 switch (const VkResult result = present_queue.Present(present_info)) {
147 case VK_SUCCESS: 149 case VK_SUCCESS:
148 break; 150 break;
@@ -168,7 +170,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
168 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; 170 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
169 171
170 const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)}; 172 const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
171 const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)}; 173 surface_format = ChooseSwapSurfaceFormat(formats);
172 present_mode = ChooseSwapPresentMode(present_modes); 174 present_mode = ChooseSwapPresentMode(present_modes);
173 175
174 u32 requested_image_count{capabilities.minImageCount + 1}; 176 u32 requested_image_count{capabilities.minImageCount + 1};
@@ -193,7 +195,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
193 .imageColorSpace = surface_format.colorSpace, 195 .imageColorSpace = surface_format.colorSpace,
194 .imageExtent = {}, 196 .imageExtent = {},
195 .imageArrayLayers = 1, 197 .imageArrayLayers = 1,
196 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 198 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
197 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, 199 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
198 .queueFamilyIndexCount = 0, 200 .queueFamilyIndexCount = 0,
199 .pQueueFamilyIndices = nullptr, 201 .pQueueFamilyIndices = nullptr,
@@ -241,45 +243,14 @@ void Swapchain::CreateSemaphores() {
241 present_semaphores.resize(image_count); 243 present_semaphores.resize(image_count);
242 std::ranges::generate(present_semaphores, 244 std::ranges::generate(present_semaphores,
243 [this] { return device.GetLogical().CreateSemaphore(); }); 245 [this] { return device.GetLogical().CreateSemaphore(); });
244} 246 render_semaphores.resize(image_count);
245 247 std::ranges::generate(render_semaphores,
246void Swapchain::CreateImageViews() { 248 [this] { return device.GetLogical().CreateSemaphore(); });
247 VkImageViewCreateInfo ci{
248 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
249 .pNext = nullptr,
250 .flags = 0,
251 .image = {},
252 .viewType = VK_IMAGE_VIEW_TYPE_2D,
253 .format = image_view_format,
254 .components =
255 {
256 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
257 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
258 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
259 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
260 },
261 .subresourceRange =
262 {
263 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
264 .baseMipLevel = 0,
265 .levelCount = 1,
266 .baseArrayLayer = 0,
267 .layerCount = 1,
268 },
269 };
270
271 image_views.resize(image_count);
272 for (std::size_t i = 0; i < image_count; i++) {
273 ci.image = images[i];
274 image_views[i] = device.GetLogical().CreateImageView(ci);
275 }
276} 249}
277 250
278void Swapchain::Destroy() { 251void Swapchain::Destroy() {
279 frame_index = 0; 252 frame_index = 0;
280 present_semaphores.clear(); 253 present_semaphores.clear();
281 framebuffers.clear();
282 image_views.clear();
283 swapchain.reset(); 254 swapchain.reset();
284} 255}
285 256
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index caf1ff32b..419742586 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -27,7 +27,7 @@ public:
27 void Create(u32 width, u32 height, bool srgb); 27 void Create(u32 width, u32 height, bool srgb);
28 28
29 /// Acquires the next image in the swapchain, waits as needed. 29 /// Acquires the next image in the swapchain, waits as needed.
30 void AcquireNextImage(); 30 bool AcquireNextImage();
31 31
32 /// Presents the rendered image to the swapchain. 32 /// Presents the rendered image to the swapchain.
33 void Present(VkSemaphore render_semaphore); 33 void Present(VkSemaphore render_semaphore);
@@ -52,6 +52,11 @@ public:
52 return is_suboptimal; 52 return is_suboptimal;
53 } 53 }
54 54
55 /// Returns true when the swapchain format is in the srgb color space
56 bool IsSrgb() const {
57 return current_srgb;
58 }
59
55 VkExtent2D GetSize() const { 60 VkExtent2D GetSize() const {
56 return extent; 61 return extent;
57 } 62 }
@@ -64,22 +69,34 @@ public:
64 return image_index; 69 return image_index;
65 } 70 }
66 71
72 std::size_t GetFrameIndex() const {
73 return frame_index;
74 }
75
67 VkImage GetImageIndex(std::size_t index) const { 76 VkImage GetImageIndex(std::size_t index) const {
68 return images[index]; 77 return images[index];
69 } 78 }
70 79
71 VkImageView GetImageViewIndex(std::size_t index) const { 80 VkImage CurrentImage() const {
72 return *image_views[index]; 81 return images[image_index];
73 } 82 }
74 83
75 VkFormat GetImageViewFormat() const { 84 VkFormat GetImageViewFormat() const {
76 return image_view_format; 85 return image_view_format;
77 } 86 }
78 87
88 VkFormat GetImageFormat() const {
89 return surface_format.format;
90 }
91
79 VkSemaphore CurrentPresentSemaphore() const { 92 VkSemaphore CurrentPresentSemaphore() const {
80 return *present_semaphores[frame_index]; 93 return *present_semaphores[frame_index];
81 } 94 }
82 95
96 VkSemaphore CurrentRenderSemaphore() const {
97 return *render_semaphores[frame_index];
98 }
99
83 u32 GetWidth() const { 100 u32 GetWidth() const {
84 return width; 101 return width;
85 } 102 }
@@ -88,6 +105,10 @@ public:
88 return height; 105 return height;
89 } 106 }
90 107
108 VkExtent2D GetExtent() const {
109 return extent;
110 }
111
91private: 112private:
92 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb); 113 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb);
93 void CreateSemaphores(); 114 void CreateSemaphores();
@@ -107,10 +128,9 @@ private:
107 128
108 std::size_t image_count{}; 129 std::size_t image_count{};
109 std::vector<VkImage> images; 130 std::vector<VkImage> images;
110 std::vector<vk::ImageView> image_views;
111 std::vector<vk::Framebuffer> framebuffers;
112 std::vector<u64> resource_ticks; 131 std::vector<u64> resource_ticks;
113 std::vector<vk::Semaphore> present_semaphores; 132 std::vector<vk::Semaphore> present_semaphores;
133 std::vector<vk::Semaphore> render_semaphores;
114 134
115 u32 width; 135 u32 width;
116 u32 height; 136 u32 height;
@@ -121,6 +141,7 @@ private:
121 VkFormat image_view_format{}; 141 VkFormat image_view_format{};
122 VkExtent2D extent{}; 142 VkExtent2D extent{};
123 VkPresentModeKHR present_mode{}; 143 VkPresentModeKHR present_mode{};
144 VkSurfaceFormatKHR surface_format{};
124 145
125 bool current_srgb{}; 146 bool current_srgb{};
126 bool current_fps_unlocked{}; 147 bool current_fps_unlocked{};
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
index 009dab0b6..0630ebda5 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
@@ -14,13 +14,18 @@ namespace Vulkan {
14 14
15UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_) 15UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_)
16 : device{device_}, scheduler{scheduler_} { 16 : device{device_}, scheduler{scheduler_} {
17 payload_start = payload.data();
17 payload_cursor = payload.data(); 18 payload_cursor = payload.data();
18} 19}
19 20
20UpdateDescriptorQueue::~UpdateDescriptorQueue() = default; 21UpdateDescriptorQueue::~UpdateDescriptorQueue() = default;
21 22
22void UpdateDescriptorQueue::TickFrame() { 23void UpdateDescriptorQueue::TickFrame() {
23 payload_cursor = payload.data(); 24 if (++frame_index >= FRAMES_IN_FLIGHT) {
25 frame_index = 0;
26 }
27 payload_start = payload.data() + frame_index * FRAME_PAYLOAD_SIZE;
28 payload_cursor = payload_start;
24} 29}
25 30
26void UpdateDescriptorQueue::Acquire() { 31void UpdateDescriptorQueue::Acquire() {
@@ -28,10 +33,10 @@ void UpdateDescriptorQueue::Acquire() {
28 // This is the maximum number of entries a single draw call might use. 33 // This is the maximum number of entries a single draw call might use.
29 static constexpr size_t MIN_ENTRIES = 0x400; 34 static constexpr size_t MIN_ENTRIES = 0x400;
30 35
31 if (std::distance(payload.data(), payload_cursor) + MIN_ENTRIES >= payload.max_size()) { 36 if (std::distance(payload_start, payload_cursor) + MIN_ENTRIES >= FRAME_PAYLOAD_SIZE) {
32 LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); 37 LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread");
33 scheduler.WaitWorker(); 38 scheduler.WaitWorker();
34 payload_cursor = payload.data(); 39 payload_cursor = payload_start;
35 } 40 }
36 upload_start = payload_cursor; 41 upload_start = payload_cursor;
37} 42}
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h
index 625bcc809..1c1a7020b 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.h
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h
@@ -29,6 +29,12 @@ struct DescriptorUpdateEntry {
29}; 29};
30 30
31class UpdateDescriptorQueue final { 31class UpdateDescriptorQueue final {
32 // This should be plenty for the vast majority of cases. Most desktop platforms only
33 // provide up to 3 swapchain images.
34 static constexpr size_t FRAMES_IN_FLIGHT = 5;
35 static constexpr size_t FRAME_PAYLOAD_SIZE = 0x10000;
36 static constexpr size_t PAYLOAD_SIZE = FRAME_PAYLOAD_SIZE * FRAMES_IN_FLIGHT;
37
32public: 38public:
33 explicit UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_); 39 explicit UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_);
34 ~UpdateDescriptorQueue(); 40 ~UpdateDescriptorQueue();
@@ -73,9 +79,11 @@ private:
73 const Device& device; 79 const Device& device;
74 Scheduler& scheduler; 80 Scheduler& scheduler;
75 81
82 size_t frame_index{0};
76 DescriptorUpdateEntry* payload_cursor = nullptr; 83 DescriptorUpdateEntry* payload_cursor = nullptr;
84 DescriptorUpdateEntry* payload_start = nullptr;
77 const DescriptorUpdateEntry* upload_start = nullptr; 85 const DescriptorUpdateEntry* upload_start = nullptr;
78 std::array<DescriptorUpdateEntry, 0x10000> payload; 86 std::array<DescriptorUpdateEntry, PAYLOAD_SIZE> payload;
79}; 87};
80 88
81} // namespace Vulkan 89} // namespace Vulkan
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 1a76d4178..cb51529e4 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -250,10 +250,13 @@ bool IsPixelFormatASTC(PixelFormat format) {
250 case PixelFormat::ASTC_2D_6X6_UNORM: 250 case PixelFormat::ASTC_2D_6X6_UNORM:
251 case PixelFormat::ASTC_2D_6X6_SRGB: 251 case PixelFormat::ASTC_2D_6X6_SRGB:
252 case PixelFormat::ASTC_2D_10X6_UNORM: 252 case PixelFormat::ASTC_2D_10X6_UNORM:
253 case PixelFormat::ASTC_2D_10X6_SRGB:
253 case PixelFormat::ASTC_2D_10X5_UNORM: 254 case PixelFormat::ASTC_2D_10X5_UNORM:
254 case PixelFormat::ASTC_2D_10X5_SRGB: 255 case PixelFormat::ASTC_2D_10X5_SRGB:
255 case PixelFormat::ASTC_2D_10X10_UNORM: 256 case PixelFormat::ASTC_2D_10X10_UNORM:
256 case PixelFormat::ASTC_2D_10X10_SRGB: 257 case PixelFormat::ASTC_2D_10X10_SRGB:
258 case PixelFormat::ASTC_2D_12X10_UNORM:
259 case PixelFormat::ASTC_2D_12X10_SRGB:
257 case PixelFormat::ASTC_2D_12X12_UNORM: 260 case PixelFormat::ASTC_2D_12X12_UNORM:
258 case PixelFormat::ASTC_2D_12X12_SRGB: 261 case PixelFormat::ASTC_2D_12X12_SRGB:
259 case PixelFormat::ASTC_2D_8X6_UNORM: 262 case PixelFormat::ASTC_2D_8X6_UNORM:
@@ -279,11 +282,13 @@ bool IsPixelFormatSRGB(PixelFormat format) {
279 case PixelFormat::ASTC_2D_8X5_SRGB: 282 case PixelFormat::ASTC_2D_8X5_SRGB:
280 case PixelFormat::ASTC_2D_5X4_SRGB: 283 case PixelFormat::ASTC_2D_5X4_SRGB:
281 case PixelFormat::ASTC_2D_5X5_SRGB: 284 case PixelFormat::ASTC_2D_5X5_SRGB:
285 case PixelFormat::ASTC_2D_10X6_SRGB:
282 case PixelFormat::ASTC_2D_10X8_SRGB: 286 case PixelFormat::ASTC_2D_10X8_SRGB:
283 case PixelFormat::ASTC_2D_6X6_SRGB: 287 case PixelFormat::ASTC_2D_6X6_SRGB:
284 case PixelFormat::ASTC_2D_10X5_SRGB: 288 case PixelFormat::ASTC_2D_10X5_SRGB:
285 case PixelFormat::ASTC_2D_10X10_SRGB: 289 case PixelFormat::ASTC_2D_10X10_SRGB:
286 case PixelFormat::ASTC_2D_12X12_SRGB: 290 case PixelFormat::ASTC_2D_12X12_SRGB:
291 case PixelFormat::ASTC_2D_12X10_SRGB:
287 case PixelFormat::ASTC_2D_8X6_SRGB: 292 case PixelFormat::ASTC_2D_8X6_SRGB:
288 case PixelFormat::ASTC_2D_6X5_SRGB: 293 case PixelFormat::ASTC_2D_6X5_SRGB:
289 return true; 294 return true;
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 44b79af20..0225d3287 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -95,10 +95,13 @@ enum class PixelFormat {
95 ASTC_2D_6X6_UNORM, 95 ASTC_2D_6X6_UNORM,
96 ASTC_2D_6X6_SRGB, 96 ASTC_2D_6X6_SRGB,
97 ASTC_2D_10X6_UNORM, 97 ASTC_2D_10X6_UNORM,
98 ASTC_2D_10X6_SRGB,
98 ASTC_2D_10X5_UNORM, 99 ASTC_2D_10X5_UNORM,
99 ASTC_2D_10X5_SRGB, 100 ASTC_2D_10X5_SRGB,
100 ASTC_2D_10X10_UNORM, 101 ASTC_2D_10X10_UNORM,
101 ASTC_2D_10X10_SRGB, 102 ASTC_2D_10X10_SRGB,
103 ASTC_2D_12X10_UNORM,
104 ASTC_2D_12X10_SRGB,
102 ASTC_2D_12X12_UNORM, 105 ASTC_2D_12X12_UNORM,
103 ASTC_2D_12X12_SRGB, 106 ASTC_2D_12X12_SRGB,
104 ASTC_2D_8X6_UNORM, 107 ASTC_2D_8X6_UNORM,
@@ -232,10 +235,13 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
232 6, // ASTC_2D_6X6_UNORM 235 6, // ASTC_2D_6X6_UNORM
233 6, // ASTC_2D_6X6_SRGB 236 6, // ASTC_2D_6X6_SRGB
234 10, // ASTC_2D_10X6_UNORM 237 10, // ASTC_2D_10X6_UNORM
238 10, // ASTC_2D_10X6_SRGB
235 10, // ASTC_2D_10X5_UNORM 239 10, // ASTC_2D_10X5_UNORM
236 10, // ASTC_2D_10X5_SRGB 240 10, // ASTC_2D_10X5_SRGB
237 10, // ASTC_2D_10X10_UNORM 241 10, // ASTC_2D_10X10_UNORM
238 10, // ASTC_2D_10X10_SRGB 242 10, // ASTC_2D_10X10_SRGB
243 12, // ASTC_2D_12X10_UNORM
244 12, // ASTC_2D_12X10_SRGB
239 12, // ASTC_2D_12X12_UNORM 245 12, // ASTC_2D_12X12_UNORM
240 12, // ASTC_2D_12X12_SRGB 246 12, // ASTC_2D_12X12_SRGB
241 8, // ASTC_2D_8X6_UNORM 247 8, // ASTC_2D_8X6_UNORM
@@ -338,10 +344,13 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
338 6, // ASTC_2D_6X6_UNORM 344 6, // ASTC_2D_6X6_UNORM
339 6, // ASTC_2D_6X6_SRGB 345 6, // ASTC_2D_6X6_SRGB
340 6, // ASTC_2D_10X6_UNORM 346 6, // ASTC_2D_10X6_UNORM
347 6, // ASTC_2D_10X6_SRGB
341 5, // ASTC_2D_10X5_UNORM 348 5, // ASTC_2D_10X5_UNORM
342 5, // ASTC_2D_10X5_SRGB 349 5, // ASTC_2D_10X5_SRGB
343 10, // ASTC_2D_10X10_UNORM 350 10, // ASTC_2D_10X10_UNORM
344 10, // ASTC_2D_10X10_SRGB 351 10, // ASTC_2D_10X10_SRGB
352 10, // ASTC_2D_12X10_UNORM
353 10, // ASTC_2D_12X10_SRGB
345 12, // ASTC_2D_12X12_UNORM 354 12, // ASTC_2D_12X12_UNORM
346 12, // ASTC_2D_12X12_SRGB 355 12, // ASTC_2D_12X12_SRGB
347 6, // ASTC_2D_8X6_UNORM 356 6, // ASTC_2D_8X6_UNORM
@@ -444,10 +453,13 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
444 128, // ASTC_2D_6X6_UNORM 453 128, // ASTC_2D_6X6_UNORM
445 128, // ASTC_2D_6X6_SRGB 454 128, // ASTC_2D_6X6_SRGB
446 128, // ASTC_2D_10X6_UNORM 455 128, // ASTC_2D_10X6_UNORM
456 128, // ASTC_2D_10X6_SRGB
447 128, // ASTC_2D_10X5_UNORM 457 128, // ASTC_2D_10X5_UNORM
448 128, // ASTC_2D_10X5_SRGB 458 128, // ASTC_2D_10X5_SRGB
449 128, // ASTC_2D_10X10_UNORM 459 128, // ASTC_2D_10X10_UNORM
450 128, // ASTC_2D_10X10_SRGB 460 128, // ASTC_2D_10X10_SRGB
461 128, // ASTC_2D_12X10_UNORM
462 128, // ASTC_2D_12X10_SRGB
451 128, // ASTC_2D_12X12_UNORM 463 128, // ASTC_2D_12X12_UNORM
452 128, // ASTC_2D_12X12_SRGB 464 128, // ASTC_2D_12X12_SRGB
453 128, // ASTC_2D_8X6_UNORM 465 128, // ASTC_2D_8X6_UNORM
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index 5fc2b2fec..11ced6c38 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -210,6 +210,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
210 return PixelFormat::ASTC_2D_6X6_SRGB; 210 return PixelFormat::ASTC_2D_6X6_SRGB;
211 case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR): 211 case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR):
212 return PixelFormat::ASTC_2D_10X6_UNORM; 212 return PixelFormat::ASTC_2D_10X6_UNORM;
213 case Hash(TextureFormat::ASTC_2D_10X6, UNORM, SRGB):
214 return PixelFormat::ASTC_2D_10X6_SRGB;
213 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR): 215 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR):
214 return PixelFormat::ASTC_2D_10X5_UNORM; 216 return PixelFormat::ASTC_2D_10X5_UNORM;
215 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB): 217 case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB):
@@ -218,6 +220,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
218 return PixelFormat::ASTC_2D_10X10_UNORM; 220 return PixelFormat::ASTC_2D_10X10_UNORM;
219 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB): 221 case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB):
220 return PixelFormat::ASTC_2D_10X10_SRGB; 222 return PixelFormat::ASTC_2D_10X10_SRGB;
223 case Hash(TextureFormat::ASTC_2D_12X10, UNORM, LINEAR):
224 return PixelFormat::ASTC_2D_12X10_UNORM;
225 case Hash(TextureFormat::ASTC_2D_12X10, UNORM, SRGB):
226 return PixelFormat::ASTC_2D_12X10_SRGB;
221 case Hash(TextureFormat::ASTC_2D_12X12, UNORM, LINEAR): 227 case Hash(TextureFormat::ASTC_2D_12X12, UNORM, LINEAR):
222 return PixelFormat::ASTC_2D_12X12_UNORM; 228 return PixelFormat::ASTC_2D_12X12_UNORM;
223 case Hash(TextureFormat::ASTC_2D_12X12, UNORM, SRGB): 229 case Hash(TextureFormat::ASTC_2D_12X12, UNORM, SRGB):
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index f1f0a057b..b97147797 100644
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -179,6 +179,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
179 return "ASTC_2D_6X6_SRGB"; 179 return "ASTC_2D_6X6_SRGB";
180 case PixelFormat::ASTC_2D_10X6_UNORM: 180 case PixelFormat::ASTC_2D_10X6_UNORM:
181 return "ASTC_2D_10X6_UNORM"; 181 return "ASTC_2D_10X6_UNORM";
182 case PixelFormat::ASTC_2D_10X6_SRGB:
183 return "ASTC_2D_10X6_SRGB";
182 case PixelFormat::ASTC_2D_10X5_UNORM: 184 case PixelFormat::ASTC_2D_10X5_UNORM:
183 return "ASTC_2D_10X5_UNORM"; 185 return "ASTC_2D_10X5_UNORM";
184 case PixelFormat::ASTC_2D_10X5_SRGB: 186 case PixelFormat::ASTC_2D_10X5_SRGB:
@@ -187,6 +189,10 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
187 return "ASTC_2D_10X10_UNORM"; 189 return "ASTC_2D_10X10_UNORM";
188 case PixelFormat::ASTC_2D_10X10_SRGB: 190 case PixelFormat::ASTC_2D_10X10_SRGB:
189 return "ASTC_2D_10X10_SRGB"; 191 return "ASTC_2D_10X10_SRGB";
192 case PixelFormat::ASTC_2D_12X10_UNORM:
193 return "ASTC_2D_12X10_UNORM";
194 case PixelFormat::ASTC_2D_12X10_SRGB:
195 return "ASTC_2D_12X10_SRGB";
190 case PixelFormat::ASTC_2D_12X12_UNORM: 196 case PixelFormat::ASTC_2D_12X12_UNORM:
191 return "ASTC_2D_12X12_UNORM"; 197 return "ASTC_2D_12X12_UNORM";
192 case PixelFormat::ASTC_2D_12X12_SRGB: 198 case PixelFormat::ASTC_2D_12X12_SRGB:
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index e601f8446..f335009d0 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -888,7 +888,7 @@ void TextureCache<P>::DownloadImageIntoBuffer(typename TextureCache<P>::Image* i
888 buffer, 888 buffer,
889 download_map.buffer, 889 download_map.buffer,
890 }; 890 };
891 std::array buffer_offsets{ 891 std::array<u64, 2> buffer_offsets{
892 buffer_offset, 892 buffer_offset,
893 download_map.offset, 893 download_map.offset,
894 }; 894 };
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 6f288b3f8..6ffca2af2 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -617,7 +617,9 @@ bool Device::ShouldBoostClocks() const {
617 617
618 const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; 618 const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F;
619 619
620 return validated_driver && !is_steam_deck; 620 const bool is_debugging = this->HasDebuggingToolAttached();
621
622 return validated_driver && !is_steam_deck && !is_debugging;
621} 623}
622 624
623bool Device::GetSuitability(bool requires_swapchain) { 625bool Device::GetSuitability(bool requires_swapchain) {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index bb731276e..0131f63e7 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -497,7 +497,7 @@ void Config::ReadCoreValues() {
497 qt_config->beginGroup(QStringLiteral("Core")); 497 qt_config->beginGroup(QStringLiteral("Core"));
498 498
499 ReadGlobalSetting(Settings::values.use_multi_core); 499 ReadGlobalSetting(Settings::values.use_multi_core);
500 ReadGlobalSetting(Settings::values.use_extended_memory_layout); 500 ReadGlobalSetting(Settings::values.use_unsafe_extended_memory_layout);
501 501
502 qt_config->endGroup(); 502 qt_config->endGroup();
503} 503}
@@ -692,6 +692,7 @@ void Config::ReadRendererValues() {
692 qt_config->beginGroup(QStringLiteral("Renderer")); 692 qt_config->beginGroup(QStringLiteral("Renderer"));
693 693
694 ReadGlobalSetting(Settings::values.renderer_backend); 694 ReadGlobalSetting(Settings::values.renderer_backend);
695 ReadGlobalSetting(Settings::values.async_presentation);
695 ReadGlobalSetting(Settings::values.renderer_force_max_clock); 696 ReadGlobalSetting(Settings::values.renderer_force_max_clock);
696 ReadGlobalSetting(Settings::values.vulkan_device); 697 ReadGlobalSetting(Settings::values.vulkan_device);
697 ReadGlobalSetting(Settings::values.fullscreen_mode); 698 ReadGlobalSetting(Settings::values.fullscreen_mode);
@@ -712,7 +713,6 @@ void Config::ReadRendererValues() {
712 ReadGlobalSetting(Settings::values.shader_backend); 713 ReadGlobalSetting(Settings::values.shader_backend);
713 ReadGlobalSetting(Settings::values.use_asynchronous_shaders); 714 ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
714 ReadGlobalSetting(Settings::values.use_fast_gpu_time); 715 ReadGlobalSetting(Settings::values.use_fast_gpu_time);
715 ReadGlobalSetting(Settings::values.use_pessimistic_flushes);
716 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); 716 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
717 ReadGlobalSetting(Settings::values.bg_red); 717 ReadGlobalSetting(Settings::values.bg_red);
718 ReadGlobalSetting(Settings::values.bg_green); 718 ReadGlobalSetting(Settings::values.bg_green);
@@ -1161,7 +1161,7 @@ void Config::SaveCoreValues() {
1161 qt_config->beginGroup(QStringLiteral("Core")); 1161 qt_config->beginGroup(QStringLiteral("Core"));
1162 1162
1163 WriteGlobalSetting(Settings::values.use_multi_core); 1163 WriteGlobalSetting(Settings::values.use_multi_core);
1164 WriteGlobalSetting(Settings::values.use_extended_memory_layout); 1164 WriteGlobalSetting(Settings::values.use_unsafe_extended_memory_layout);
1165 1165
1166 qt_config->endGroup(); 1166 qt_config->endGroup();
1167} 1167}
@@ -1313,6 +1313,7 @@ void Config::SaveRendererValues() {
1313 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), 1313 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)),
1314 static_cast<u32>(Settings::values.renderer_backend.GetDefault()), 1314 static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
1315 Settings::values.renderer_backend.UsingGlobal()); 1315 Settings::values.renderer_backend.UsingGlobal());
1316 WriteGlobalSetting(Settings::values.async_presentation);
1316 WriteGlobalSetting(Settings::values.renderer_force_max_clock); 1317 WriteGlobalSetting(Settings::values.renderer_force_max_clock);
1317 WriteGlobalSetting(Settings::values.vulkan_device); 1318 WriteGlobalSetting(Settings::values.vulkan_device);
1318 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()), 1319 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
@@ -1357,7 +1358,6 @@ void Config::SaveRendererValues() {
1357 Settings::values.shader_backend.UsingGlobal()); 1358 Settings::values.shader_backend.UsingGlobal());
1358 WriteGlobalSetting(Settings::values.use_asynchronous_shaders); 1359 WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
1359 WriteGlobalSetting(Settings::values.use_fast_gpu_time); 1360 WriteGlobalSetting(Settings::values.use_fast_gpu_time);
1360 WriteGlobalSetting(Settings::values.use_pessimistic_flushes);
1361 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); 1361 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
1362 WriteGlobalSetting(Settings::values.bg_red); 1362 WriteGlobalSetting(Settings::values.bg_red);
1363 WriteGlobalSetting(Settings::values.bg_green); 1363 WriteGlobalSetting(Settings::values.bg_green);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 207bcdc4d..26258d744 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -35,9 +35,6 @@ void ConfigureGeneral::SetConfiguration() {
35 35
36 ui->use_multi_core->setEnabled(runtime_lock); 36 ui->use_multi_core->setEnabled(runtime_lock);
37 ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); 37 ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
38 ui->use_extended_memory_layout->setEnabled(runtime_lock);
39 ui->use_extended_memory_layout->setChecked(
40 Settings::values.use_extended_memory_layout.GetValue());
41 38
42 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); 39 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
43 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); 40 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
@@ -79,9 +76,6 @@ void ConfigureGeneral::ResetDefaults() {
79void ConfigureGeneral::ApplyConfiguration() { 76void ConfigureGeneral::ApplyConfiguration() {
80 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, 77 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core,
81 use_multi_core); 78 use_multi_core);
82 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_extended_memory_layout,
83 ui->use_extended_memory_layout,
84 use_extended_memory_layout);
85 79
86 if (Settings::IsConfiguringGlobal()) { 80 if (Settings::IsConfiguringGlobal()) {
87 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 81 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
@@ -141,9 +135,6 @@ void ConfigureGeneral::SetupPerGameUI() {
141 Settings::values.use_speed_limit, use_speed_limit); 135 Settings::values.use_speed_limit, use_speed_limit);
142 ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core, 136 ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
143 use_multi_core); 137 use_multi_core);
144 ConfigurationShared::SetColoredTristate(ui->use_extended_memory_layout,
145 Settings::values.use_extended_memory_layout,
146 use_extended_memory_layout);
147 138
148 connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() { 139 connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() {
149 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && 140 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index a090c1a3f..7ff63f425 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -47,7 +47,6 @@ private:
47 47
48 ConfigurationShared::CheckState use_speed_limit; 48 ConfigurationShared::CheckState use_speed_limit;
49 ConfigurationShared::CheckState use_multi_core; 49 ConfigurationShared::CheckState use_multi_core;
50 ConfigurationShared::CheckState use_extended_memory_layout;
51 50
52 const Core::System& system; 51 const Core::System& system;
53}; 52};
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index add110bb0..986a1625b 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -62,13 +62,6 @@
62 </widget> 62 </widget>
63 </item> 63 </item>
64 <item> 64 <item>
65 <widget class="QCheckBox" name="use_extended_memory_layout">
66 <property name="text">
67 <string>Extended memory layout (8GB DRAM)</string>
68 </property>
69 </widget>
70 </item>
71 <item>
72 <widget class="QCheckBox" name="toggle_check_exit"> 65 <widget class="QCheckBox" name="toggle_check_exit">
73 <property name="text"> 66 <property name="text">
74 <string>Confirm exit while emulation is running</string> 67 <string>Confirm exit while emulation is running</string>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 59fb1b334..ddda79983 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -22,17 +22,18 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
22void ConfigureGraphicsAdvanced::SetConfiguration() { 22void ConfigureGraphicsAdvanced::SetConfiguration() {
23 const bool runtime_lock = !system.IsPoweredOn(); 23 const bool runtime_lock = !system.IsPoweredOn();
24 ui->use_vsync->setEnabled(runtime_lock); 24 ui->use_vsync->setEnabled(runtime_lock);
25 ui->async_present->setEnabled(runtime_lock);
25 ui->renderer_force_max_clock->setEnabled(runtime_lock); 26 ui->renderer_force_max_clock->setEnabled(runtime_lock);
26 ui->async_astc->setEnabled(runtime_lock); 27 ui->async_astc->setEnabled(runtime_lock);
27 ui->use_asynchronous_shaders->setEnabled(runtime_lock); 28 ui->use_asynchronous_shaders->setEnabled(runtime_lock);
28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 29 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
29 30
31 ui->async_present->setChecked(Settings::values.async_presentation.GetValue());
30 ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue()); 32 ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue());
31 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); 33 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
32 ui->async_astc->setChecked(Settings::values.async_astc.GetValue()); 34 ui->async_astc->setChecked(Settings::values.async_astc.GetValue());
33 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 35 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
34 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 36 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
35 ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue());
36 ui->use_vulkan_driver_pipeline_cache->setChecked( 37 ui->use_vulkan_driver_pipeline_cache->setChecked(
37 Settings::values.use_vulkan_driver_pipeline_cache.GetValue()); 38 Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
38 39
@@ -54,6 +55,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
54 55
55void ConfigureGraphicsAdvanced::ApplyConfiguration() { 56void ConfigureGraphicsAdvanced::ApplyConfiguration() {
56 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy); 57 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy);
58 ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_presentation,
59 ui->async_present, async_present);
57 ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock, 60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock,
58 ui->renderer_force_max_clock, 61 ui->renderer_force_max_clock,
59 renderer_force_max_clock); 62 renderer_force_max_clock);
@@ -67,8 +70,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
67 use_asynchronous_shaders); 70 use_asynchronous_shaders);
68 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, 71 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
69 ui->use_fast_gpu_time, use_fast_gpu_time); 72 ui->use_fast_gpu_time, use_fast_gpu_time);
70 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes,
71 ui->use_pessimistic_flushes, use_pessimistic_flushes);
72 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache, 73 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache,
73 ui->use_vulkan_driver_pipeline_cache, 74 ui->use_vulkan_driver_pipeline_cache,
74 use_vulkan_driver_pipeline_cache); 75 use_vulkan_driver_pipeline_cache);
@@ -90,6 +91,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
90 // Disable if not global (only happens during game) 91 // Disable if not global (only happens during game)
91 if (Settings::IsConfiguringGlobal()) { 92 if (Settings::IsConfiguringGlobal()) {
92 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 93 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
94 ui->async_present->setEnabled(Settings::values.async_presentation.UsingGlobal());
93 ui->renderer_force_max_clock->setEnabled( 95 ui->renderer_force_max_clock->setEnabled(
94 Settings::values.renderer_force_max_clock.UsingGlobal()); 96 Settings::values.renderer_force_max_clock.UsingGlobal());
95 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 97 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
@@ -97,8 +99,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
97 ui->use_asynchronous_shaders->setEnabled( 99 ui->use_asynchronous_shaders->setEnabled(
98 Settings::values.use_asynchronous_shaders.UsingGlobal()); 100 Settings::values.use_asynchronous_shaders.UsingGlobal());
99 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); 101 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
100 ui->use_pessimistic_flushes->setEnabled(
101 Settings::values.use_pessimistic_flushes.UsingGlobal());
102 ui->use_vulkan_driver_pipeline_cache->setEnabled( 102 ui->use_vulkan_driver_pipeline_cache->setEnabled(
103 Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal()); 103 Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal());
104 ui->anisotropic_filtering_combobox->setEnabled( 104 ui->anisotropic_filtering_combobox->setEnabled(
@@ -107,6 +107,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
107 return; 107 return;
108 } 108 }
109 109
110 ConfigurationShared::SetColoredTristate(ui->async_present, Settings::values.async_presentation,
111 async_present);
110 ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock, 112 ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
111 Settings::values.renderer_force_max_clock, 113 Settings::values.renderer_force_max_clock,
112 renderer_force_max_clock); 114 renderer_force_max_clock);
@@ -118,9 +120,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
118 use_asynchronous_shaders); 120 use_asynchronous_shaders);
119 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, 121 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
120 Settings::values.use_fast_gpu_time, use_fast_gpu_time); 122 Settings::values.use_fast_gpu_time, use_fast_gpu_time);
121 ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes,
122 Settings::values.use_pessimistic_flushes,
123 use_pessimistic_flushes);
124 ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache, 123 ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache,
125 Settings::values.use_vulkan_driver_pipeline_cache, 124 Settings::values.use_vulkan_driver_pipeline_cache,
126 use_vulkan_driver_pipeline_cache); 125 use_vulkan_driver_pipeline_cache);
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index bf1b04749..ff5060957 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -36,12 +36,12 @@ private:
36 36
37 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 37 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
38 38
39 ConfigurationShared::CheckState async_present;
39 ConfigurationShared::CheckState renderer_force_max_clock; 40 ConfigurationShared::CheckState renderer_force_max_clock;
40 ConfigurationShared::CheckState use_vsync; 41 ConfigurationShared::CheckState use_vsync;
41 ConfigurationShared::CheckState async_astc; 42 ConfigurationShared::CheckState async_astc;
42 ConfigurationShared::CheckState use_asynchronous_shaders; 43 ConfigurationShared::CheckState use_asynchronous_shaders;
43 ConfigurationShared::CheckState use_fast_gpu_time; 44 ConfigurationShared::CheckState use_fast_gpu_time;
44 ConfigurationShared::CheckState use_pessimistic_flushes;
45 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; 45 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
46 46
47 const Core::System& system; 47 const Core::System& system;
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index a7dbdc18c..1234f695c 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>404</width> 9 <width>404</width>
10 <height>321</height> 10 <height>376</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -70,6 +70,13 @@
70 </widget> 70 </widget>
71 </item> 71 </item>
72 <item> 72 <item>
73 <widget class="QCheckBox" name="async_present">
74 <property name="text">
75 <string>Enable asynchronous presentation (Vulkan only)</string>
76 </property>
77 </widget>
78 </item>
79 <item>
73 <widget class="QCheckBox" name="renderer_force_max_clock"> 80 <widget class="QCheckBox" name="renderer_force_max_clock">
74 <property name="toolTip"> 81 <property name="toolTip">
75 <string>Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed.</string> 82 <string>Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed.</string>
@@ -112,7 +119,7 @@
112 <item> 119 <item>
113 <widget class="QCheckBox" name="use_fast_gpu_time"> 120 <widget class="QCheckBox" name="use_fast_gpu_time">
114 <property name="toolTip"> 121 <property name="toolTip">
115 <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string> 122 <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
116 </property> 123 </property>
117 <property name="text"> 124 <property name="text">
118 <string>Use Fast GPU Time (Hack)</string> 125 <string>Use Fast GPU Time (Hack)</string>
@@ -120,19 +127,9 @@
120 </widget> 127 </widget>
121 </item> 128 </item>
122 <item> 129 <item>
123 <widget class="QCheckBox" name="use_pessimistic_flushes">
124 <property name="toolTip">
125 <string>Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance.</string>
126 </property>
127 <property name="text">
128 <string>Use pessimistic buffer flushes (Hack)</string>
129 </property>
130 </widget>
131 </item>
132 <item>
133 <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache"> 130 <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache">
134 <property name="toolTip"> 131 <property name="toolTip">
135 <string>Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally.</string> 132 <string>Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally.</string>
136 </property> 133 </property>
137 <property name="text"> 134 <property name="text">
138 <string>Use Vulkan pipeline cache</string> 135 <string>Use Vulkan pipeline cache</string>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 6af34f793..286ccc5cd 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -111,6 +111,9 @@ void ConfigureSystem::SetConfiguration() {
111 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); 111 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time));
112 ui->device_name_edit->setText( 112 ui->device_name_edit->setText(
113 QString::fromUtf8(Settings::values.device_name.GetValue().c_str())); 113 QString::fromUtf8(Settings::values.device_name.GetValue().c_str()));
114 ui->use_unsafe_extended_memory_layout->setEnabled(enabled);
115 ui->use_unsafe_extended_memory_layout->setChecked(
116 Settings::values.use_unsafe_extended_memory_layout.GetValue());
114 117
115 if (Settings::IsConfiguringGlobal()) { 118 if (Settings::IsConfiguringGlobal()) {
116 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); 119 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
@@ -160,6 +163,9 @@ void ConfigureSystem::ApplyConfiguration() {
160 ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region); 163 ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region);
161 ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index, 164 ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index,
162 ui->combo_time_zone); 165 ui->combo_time_zone);
166 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_unsafe_extended_memory_layout,
167 ui->use_unsafe_extended_memory_layout,
168 use_unsafe_extended_memory_layout);
163 169
164 if (Settings::IsConfiguringGlobal()) { 170 if (Settings::IsConfiguringGlobal()) {
165 // Guard if during game and set to game-specific value 171 // Guard if during game and set to game-specific value
@@ -215,6 +221,10 @@ void ConfigureSystem::SetupPerGameUI() {
215 Settings::values.rng_seed.GetValue().has_value(), 221 Settings::values.rng_seed.GetValue().has_value(),
216 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed); 222 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
217 223
224 ConfigurationShared::SetColoredTristate(ui->use_unsafe_extended_memory_layout,
225 Settings::values.use_unsafe_extended_memory_layout,
226 use_unsafe_extended_memory_layout);
227
218 ui->custom_rtc_checkbox->setVisible(false); 228 ui->custom_rtc_checkbox->setVisible(false);
219 ui->custom_rtc_edit->setVisible(false); 229 ui->custom_rtc_edit->setVisible(false);
220} 230}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index ec28724a1..ce1a91601 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -41,6 +41,7 @@ private:
41 bool enabled = false; 41 bool enabled = false;
42 42
43 ConfigurationShared::CheckState use_rng_seed; 43 ConfigurationShared::CheckState use_rng_seed;
44 ConfigurationShared::CheckState use_unsafe_extended_memory_layout;
44 45
45 Core::System& system; 46 Core::System& system;
46}; 47};
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 9e7bc3b93..e0caecd5e 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -478,6 +478,13 @@
478 </property> 478 </property>
479 </widget> 479 </widget>
480 </item> 480 </item>
481 <item row="7" column="0">
482 <widget class="QCheckBox" name="use_unsafe_extended_memory_layout">
483 <property name="text">
484 <string>Unsafe extended memory layout (8GB DRAM)</string>
485 </property>
486 </widget>
487 </item>
481 </layout> 488 </layout>
482 </item> 489 </item>
483 </layout> 490 </layout>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 464da3231..605280949 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -274,7 +274,7 @@ void Config::ReadValues() {
274 274
275 // Core 275 // Core
276 ReadSetting("Core", Settings::values.use_multi_core); 276 ReadSetting("Core", Settings::values.use_multi_core);
277 ReadSetting("Core", Settings::values.use_extended_memory_layout); 277 ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout);
278 278
279 // Cpu 279 // Cpu
280 ReadSetting("Cpu", Settings::values.cpu_accuracy); 280 ReadSetting("Cpu", Settings::values.cpu_accuracy);
@@ -300,6 +300,7 @@ void Config::ReadValues() {
300 300
301 // Renderer 301 // Renderer
302 ReadSetting("Renderer", Settings::values.renderer_backend); 302 ReadSetting("Renderer", Settings::values.renderer_backend);
303 ReadSetting("Renderer", Settings::values.async_presentation);
303 ReadSetting("Renderer", Settings::values.renderer_force_max_clock); 304 ReadSetting("Renderer", Settings::values.renderer_force_max_clock);
304 ReadSetting("Renderer", Settings::values.renderer_debug); 305 ReadSetting("Renderer", Settings::values.renderer_debug);
305 ReadSetting("Renderer", Settings::values.renderer_shader_feedback); 306 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
@@ -326,7 +327,6 @@ void Config::ReadValues() {
326 ReadSetting("Renderer", Settings::values.accelerate_astc); 327 ReadSetting("Renderer", Settings::values.accelerate_astc);
327 ReadSetting("Renderer", Settings::values.async_astc); 328 ReadSetting("Renderer", Settings::values.async_astc);
328 ReadSetting("Renderer", Settings::values.use_fast_gpu_time); 329 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
329 ReadSetting("Renderer", Settings::values.use_pessimistic_flushes);
330 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache); 330 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
331 331
332 ReadSetting("Renderer", Settings::values.bg_red); 332 ReadSetting("Renderer", Settings::values.bg_red);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 209cfc28a..db6fba922 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -163,9 +163,9 @@ keyboard_enabled =
163# 0: Disabled, 1 (default): Enabled 163# 0: Disabled, 1 (default): Enabled
164use_multi_core = 164use_multi_core =
165 165
166# Enable extended guest system memory layout (8GB DRAM) 166# Enable unsafe extended guest system memory layout (8GB DRAM)
167# 0 (default): Disabled, 1: Enabled 167# 0 (default): Disabled, 1: Enabled
168use_extended_memory_layout = 168use_unsafe_extended_memory_layout =
169 169
170[Cpu] 170[Cpu]
171# Adjusts various optimizations. 171# Adjusts various optimizations.
@@ -264,6 +264,10 @@ cpuopt_unsafe_ignore_global_monitor =
264# 0: OpenGL, 1 (default): Vulkan 264# 0: OpenGL, 1 (default): Vulkan
265backend = 265backend =
266 266
267# Whether to enable asynchronous presentation (Vulkan only)
268# 0 (default): Off, 1: On
269async_presentation =
270
267# Enable graphics API debugging mode. 271# Enable graphics API debugging mode.
268# 0 (default): Disabled, 1: Enabled 272# 0 (default): Disabled, 1: Enabled
269debug = 273debug =
@@ -370,10 +374,6 @@ use_asynchronous_gpu_emulation =
370# 0: Off, 1 (default): On 374# 0: Off, 1 (default): On
371use_fast_gpu_time = 375use_fast_gpu_time =
372 376
373# Force unmodified buffers to be flushed, which can cost performance.
374# 0: Off (default), 1: On
375use_pessimistic_flushes =
376
377# Whether to use garbage collection or not for GPU caches. 377# Whether to use garbage collection or not for GPU caches.
378# 0 (default): Off, 1: On 378# 0 (default): Off, 1: On
379use_caches_gc = 379use_caches_gc =
diff --git a/vcpkg.json b/vcpkg.json
index 0352dab77..19f99e89e 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -49,7 +49,7 @@
49 "overrides": [ 49 "overrides": [
50 { 50 {
51 "name": "catch2", 51 "name": "catch2",
52 "version": "3.0.1" 52 "version": "3.3.1"
53 }, 53 },
54 { 54 {
55 "name": "fmt", 55 "name": "fmt",