diff options
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 | |||
| 4 | path_classifiers: | ||
| 5 | library: "externals" | ||
| 6 | extraction: | ||
| 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 { | |||
| 35 | using namespace Common::Literals; | 35 | using namespace Common::Literals; |
| 36 | 36 | ||
| 37 | u32 GetMemorySizeForInit() { | 37 | u32 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 | ||
| 41 | Smc::MemoryArrangement GetMemoryArrangeForInit() { | 42 | Smc::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 | ||
| 38 | ServerManager::~ServerManager() { | 41 | ServerManager::~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: | 129 | constexpr 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 | ||
| 136 | constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{ | 134 | constexpr 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 | ||
| 140 | constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{ | 139 | constexpr 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 | 149 | constexpr 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 | ||
| 154 | constexpr std::array VIEW_CLASS_ASTC_12x12_RGBA{ | 154 | constexpr 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 { | |||
| 23 | public: | 30 | public: |
| 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 | |||
| 26 | protected: | 37 | protected: |
| 27 | bool is_stubbed; | 38 | bool is_stubbed; |
| 28 | }; | 39 | }; |
| 29 | 40 | ||
| 30 | template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache> | 41 | template <typename Traits> |
| 31 | class FenceManager { | 42 | class 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 | |||
| 32 | public: | 49 | public: |
| 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 | ||
| 84 | protected: | 108 | protected: |
| 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 | ||
| 110 | private: | 145 | private: |
| 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 | ||
| 84 | PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { | 84 | PTEKind 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 | ||
| 555 | size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const { | 559 | size_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 | ||
| 25 | namespace VideoCommon { | 28 | namespace VideoCommon { |
| 26 | 29 | ||
| 30 | using AsyncJobId = SlotId; | ||
| 31 | |||
| 32 | static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0}; | ||
| 33 | |||
| 27 | template <class QueryCache, class HostCounter> | 34 | template <class QueryCache, class HostCounter> |
| 28 | class CounterStreamBase { | 35 | class CounterStreamBase { |
| 29 | public: | 36 | public: |
| @@ -93,9 +100,13 @@ private: | |||
| 93 | template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter> | 100 | template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter> |
| 94 | class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> { | 101 | class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> { |
| 95 | public: | 102 | public: |
| 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 | ||
| 206 | private: | 229 | private: |
| 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), ×tamp_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 | ||
| 278 | template <class QueryCache, class HostCounter> | 351 | template <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 | ||
| 318 | protected: | 391 | protected: |
| 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 | ||
| 322 | private: | 395 | private: |
| 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 | |||
| 377 | protected: | 465 | protected: |
| 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 | ||
| 32 | using Fence = std::shared_ptr<GLInnerFence>; | 32 | using Fence = std::shared_ptr<GLInnerFence>; |
| 33 | using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>; | 33 | |
| 34 | struct 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 | |||
| 43 | using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>; | ||
| 34 | 44 | ||
| 35 | class FenceManagerOpenGL final : public GenericFenceManager { | 45 | class FenceManagerOpenGL final : public GenericFenceManager { |
| 36 | public: | 46 | public: |
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 | ||
| 29 | QueryCache::QueryCache(RasterizerOpenGL& rasterizer_) | 29 | QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_) |
| 30 | : QueryCacheBase(rasterizer_), gl_rasterizer{rasterizer_} {} | 30 | : QueryCacheBase(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {} |
| 31 | 31 | ||
| 32 | QueryCache::~QueryCache() = default; | 32 | QueryCache::~QueryCache() = default; |
| 33 | 33 | ||
| @@ -74,7 +74,7 @@ void HostCounter::EndQuery() { | |||
| 74 | glEndQuery(GetTarget(type)); | 74 | glEndQuery(GetTarget(type)); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | u64 HostCounter::BlockingQuery() const { | 77 | u64 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 | ||
| 99 | void CachedQuery::Flush() { | 99 | u64 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>; | |||
| 28 | class QueryCache final | 28 | class QueryCache final |
| 29 | : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { | 29 | : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { |
| 30 | public: | 30 | public: |
| 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 | ||
| 53 | private: | 53 | private: |
| 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 | ||
| 75 | private: | 75 | private: |
| 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 | ||
| 123 | BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWindow& render_window_, | 123 | BlitScreen::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 | |||
| 135 | BlitScreen::~BlitScreen() = default; | 137 | BlitScreen::~BlitScreen() = default; |
| 136 | 138 | ||
| 137 | void BlitScreen::Recreate() { | 139 | void BlitScreen::Recreate() { |
| 140 | present_manager.WaitPresent(); | ||
| 141 | scheduler.Finish(); | ||
| 142 | device.GetLogical().WaitIdle(); | ||
| 138 | CreateDynamicResources(); | 143 | CreateDynamicResources(); |
| 139 | } | 144 | } |
| 140 | 145 | ||
| 141 | VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, | 146 | void 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 | ||
| 441 | VkSemaphore BlitScreen::DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer, | 438 | void 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 | ||
| 449 | vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { | 465 | vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { |
| @@ -471,13 +487,11 @@ void BlitScreen::CreateStaticResources() { | |||
| 471 | } | 487 | } |
| 472 | 488 | ||
| 473 | void BlitScreen::CreateDynamicResources() { | 489 | void 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 | ||
| 528 | void BlitScreen::CreateSemaphores() { | ||
| 529 | semaphores.resize(image_count); | ||
| 530 | std::ranges::generate(semaphores, [this] { return device.GetLogical().CreateSemaphore(); }); | ||
| 531 | } | ||
| 532 | |||
| 533 | void BlitScreen::CreateDescriptorPool() { | 542 | void 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 | ||
| 573 | void BlitScreen::CreateRenderPass() { | 582 | void BlitScreen::CreateRenderPass() { |
| 574 | renderpass = CreateRenderPassImpl(swapchain.GetImageViewFormat()); | 583 | renderpass = CreateRenderPassImpl(image_view_format); |
| 575 | } | 584 | } |
| 576 | 585 | ||
| 577 | vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format, bool is_present) { | 586 | vk::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 | ||
| 1055 | void 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 | |||
| 1065 | void BlitScreen::ReleaseRawImages() { | 1064 | void 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 | ||
| 1322 | void BlitScreen::UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, | 1321 | void 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 | ||
| 1359 | void BlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, | 1357 | void 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 | ||
| 1483 | u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, | 1480 | u64 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; | |||
| 42 | class Scheduler; | 43 | class Scheduler; |
| 43 | class SMAA; | 44 | class SMAA; |
| 44 | class Swapchain; | 45 | class Swapchain; |
| 46 | class PresentManager; | ||
| 47 | |||
| 48 | struct Frame; | ||
| 45 | 49 | ||
| 46 | struct ScreenInfo { | 50 | struct ScreenInfo { |
| 47 | VkImage image{}; | 51 | VkImage image{}; |
| @@ -55,18 +59,17 @@ class BlitScreen { | |||
| 55 | public: | 59 | public: |
| 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 | }; |
| 41 | using Fence = std::shared_ptr<InnerFence>; | 41 | using Fence = std::shared_ptr<InnerFence>; |
| 42 | 42 | ||
| 43 | using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>; | 43 | struct 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 | |||
| 52 | using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>; | ||
| 44 | 53 | ||
| 45 | class FenceManager final : public GenericFenceManager { | 54 | class FenceManager final : public GenericFenceManager { |
| 46 | public: | 55 | public: |
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 | |||
| 12 | namespace Vulkan { | ||
| 13 | |||
| 14 | MICROPROFILE_DEFINE(Vulkan_WaitPresent, "Vulkan", "Wait For Present", MP_RGB(128, 128, 128)); | ||
| 15 | MICROPROFILE_DEFINE(Vulkan_CopyToSwapchain, "Vulkan", "Copy to swapchain", MP_RGB(192, 255, 192)); | ||
| 16 | |||
| 17 | namespace { | ||
| 18 | |||
| 19 | bool 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 | |||
| 95 | PresentManager::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 | |||
| 136 | PresentManager::~PresentManager() = default; | ||
| 137 | |||
| 138 | Frame* 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 | |||
| 156 | void 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 | |||
| 171 | void 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 | |||
| 242 | void 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 | |||
| 259 | void 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 | |||
| 289 | void 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 | |||
| 15 | namespace Core::Frontend { | ||
| 16 | class EmuWindow; | ||
| 17 | } // namespace Core::Frontend | ||
| 18 | |||
| 19 | namespace Vulkan { | ||
| 20 | |||
| 21 | class Device; | ||
| 22 | class Scheduler; | ||
| 23 | class Swapchain; | ||
| 24 | |||
| 25 | struct 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 | |||
| 38 | class PresentManager { | ||
| 39 | public: | ||
| 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 | |||
| 57 | private: | ||
| 58 | void PresentThread(std::stop_token token); | ||
| 59 | |||
| 60 | void CopyToSwapchain(Frame* frame); | ||
| 61 | |||
| 62 | private: | ||
| 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 | ||
| 69 | QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, | 69 | QueryCache::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 | ||
| 115 | u64 HostCounter::BlockingQuery() const { | 118 | u64 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: | |||
| 52 | class QueryCache final | 52 | class QueryCache final |
| 53 | : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { | 53 | : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { |
| 54 | public: | 54 | public: |
| 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 | ||
| 85 | private: | 86 | private: |
| 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 | ||
| 47 | Scheduler::~Scheduler() = default; | 47 | Scheduler::~Scheduler() = default; |
| 48 | 48 | ||
| 49 | void Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { | 49 | u64 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 | ||
| 55 | void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { | 56 | void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { |
| @@ -205,7 +206,7 @@ void Scheduler::AllocateWorkerCommandBuffer() { | |||
| 205 | }); | 206 | }); |
| 206 | } | 207 | } |
| 207 | 208 | ||
| 208 | void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { | 209 | u64 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 | ||
| 236 | void Scheduler::AllocateNewContext() { | 239 | void 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 | |||
| 109 | private: | 111 | private: |
| 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 | ||
| 113 | void Swapchain::AcquireNextImage() { | 111 | bool 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 | ||
| 134 | void Swapchain::Present(VkSemaphore render_semaphore) { | 135 | void 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, | |
| 246 | void 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 | ||
| 278 | void Swapchain::Destroy() { | 251 | void 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 | |||
| 91 | private: | 112 | private: |
| 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 | ||
| 15 | UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_) | 15 | UpdateDescriptorQueue::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 | ||
| 20 | UpdateDescriptorQueue::~UpdateDescriptorQueue() = default; | 21 | UpdateDescriptorQueue::~UpdateDescriptorQueue() = default; |
| 21 | 22 | ||
| 22 | void UpdateDescriptorQueue::TickFrame() { | 23 | void 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 | ||
| 26 | void UpdateDescriptorQueue::Acquire() { | 31 | void 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 | ||
| 31 | class UpdateDescriptorQueue final { | 31 | class 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 | |||
| 32 | public: | 38 | public: |
| 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 | ||
| 623 | bool Device::GetSuitability(bool requires_swapchain) { | 625 | bool 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() { | |||
| 79 | void ConfigureGeneral::ApplyConfiguration() { | 76 | void 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; | |||
| 22 | void ConfigureGraphicsAdvanced::SetConfiguration() { | 22 | void 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 | ||
| 55 | void ConfigureGraphicsAdvanced::ApplyConfiguration() { | 56 | void 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 |
| 164 | use_multi_core = | 164 | use_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 |
| 168 | use_extended_memory_layout = | 168 | use_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 |
| 265 | backend = | 265 | backend = |
| 266 | 266 | ||
| 267 | # Whether to enable asynchronous presentation (Vulkan only) | ||
| 268 | # 0 (default): Off, 1: On | ||
| 269 | async_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 |
| 269 | debug = | 273 | debug = |
| @@ -370,10 +374,6 @@ use_asynchronous_gpu_emulation = | |||
| 370 | # 0: Off, 1 (default): On | 374 | # 0: Off, 1 (default): On |
| 371 | use_fast_gpu_time = | 375 | use_fast_gpu_time = |
| 372 | 376 | ||
| 373 | # Force unmodified buffers to be flushed, which can cost performance. | ||
| 374 | # 0: Off (default), 1: On | ||
| 375 | use_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 |
| 379 | use_caches_gc = | 379 | use_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", |