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 @@ -# SPDX-FileCopyrightText: 2020 yuzu Emulator Project -# SPDX-License-Identifier: GPL-2.0-or-later - -path_classifiers: - library: "externals" -extraction: - cpp: - prepare: - packages: - - "libsdl2-dev" - - "qtmultimedia5-dev" - - "libtbb-dev" - - "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() { log_setting("System_LanguageIndex", values.language_index.GetValue()); log_setting("System_RegionIndex", values.region_index.GetValue()); log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); + log_setting("System_UnsafeMemoryLayout", values.use_unsafe_extended_memory_layout.GetValue()); log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); @@ -191,7 +192,7 @@ void RestoreGlobalState(bool is_powered_on) { // Core values.use_multi_core.SetGlobal(true); - values.use_extended_memory_layout.SetGlobal(true); + values.use_unsafe_extended_memory_layout.SetGlobal(true); // CPU values.cpu_accuracy.SetGlobal(true); @@ -205,6 +206,7 @@ void RestoreGlobalState(bool is_powered_on) { // Renderer values.fsr_sharpening_slider.SetGlobal(true); values.renderer_backend.SetGlobal(true); + values.async_presentation.SetGlobal(true); values.renderer_force_max_clock.SetGlobal(true); values.vulkan_device.SetGlobal(true); values.fullscreen_mode.SetGlobal(true); @@ -225,7 +227,6 @@ void RestoreGlobalState(bool is_powered_on) { values.shader_backend.SetGlobal(true); values.use_asynchronous_shaders.SetGlobal(true); values.use_fast_gpu_time.SetGlobal(true); - values.use_pessimistic_flushes.SetGlobal(true); values.use_vulkan_driver_pipeline_cache.SetGlobal(true); values.bg_red.SetGlobal(true); 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 { // Core SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; - SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; + SwitchableSetting<bool> use_unsafe_extended_memory_layout{false, + "use_unsafe_extended_memory_layout"}; // Cpu SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, @@ -422,6 +423,7 @@ struct Values { // Renderer SwitchableSetting<RendererBackend, true> renderer_backend{ RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; + SwitchableSetting<bool> async_presentation{false, "async_presentation"}; SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"}; Setting<bool> renderer_debug{false, "debug"}; Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; @@ -459,7 +461,6 @@ struct Values { ShaderBackend::SPIRV, "shader_backend"}; SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; - SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"}; SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true, "use_vulkan_driver_pipeline_cache"}; 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 { device_memory = std::make_unique<Core::DeviceMemory>(); is_multicore = Settings::values.use_multi_core.GetValue(); - extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); + extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue(); core_timing.SetMulticore(is_multicore); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); @@ -169,7 +169,7 @@ struct System::Impl { void ReinitializeIfNecessary(System& system) { const bool must_reinitialize = is_multicore != Settings::values.use_multi_core.GetValue() || - extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue(); + extended_memory_layout != Settings::values.use_unsafe_extended_memory_layout.GetValue(); if (!must_reinitialize) { return; @@ -178,7 +178,7 @@ struct System::Impl { LOG_DEBUG(Kernel, "Re-initializing"); is_multicore = Settings::values.use_multi_core.GetValue(); - extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); + extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue(); Initialize(system); } @@ -293,6 +293,7 @@ struct System::Impl { ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", Kernel::KProcess::ProcessType::Userland, resource_limit) .IsSuccess()); + Kernel::KProcess::Register(system.Kernel(), main_process); kernel.MakeApplicationProcess(main_process); const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); 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 { using namespace Common::Literals; u32 GetMemorySizeForInit() { - return Settings::values.use_extended_memory_layout ? Smc::MemorySize_8GB : Smc::MemorySize_4GB; + return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemorySize_8GB + : Smc::MemorySize_4GB; } Smc::MemoryArrangement GetMemoryArrangeForInit() { - return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_8GB - : Smc::MemoryArrangement_4GB; + return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemoryArrangement_8GB + : Smc::MemoryArrangement_4GB; } } // namespace 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: explicit KAutoObjectWithList(KernelCore& kernel) : KAutoObject(kernel) {} static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) { - const u64 lid = lhs.GetId(); - const u64 rid = rhs.GetId(); + const uintptr_t lid = reinterpret_cast<uintptr_t>(std::addressof(lhs)); + const uintptr_t rid = reinterpret_cast<uintptr_t>(std::addressof(rhs)); if (lid < rid) { 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 { pt_heap_region.GetSize()); } - InitializeHackSharedMemory(); + InitializeHackSharedMemory(kernel); RegisterHostThread(nullptr); } @@ -216,10 +216,12 @@ struct KernelCore::Impl { auto* main_thread{Kernel::KThread::Create(system.Kernel())}; main_thread->SetCurrentCore(core); ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess()); + KThread::Register(system.Kernel(), main_thread); auto* idle_thread{Kernel::KThread::Create(system.Kernel())}; idle_thread->SetCurrentCore(core); ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess()); + KThread::Register(system.Kernel(), idle_thread); schedulers[i]->Initialize(main_thread, idle_thread, core); } @@ -230,6 +232,7 @@ struct KernelCore::Impl { const Core::Timing::CoreTiming& core_timing) { system_resource_limit = KResourceLimit::Create(system.Kernel()); system_resource_limit->Initialize(&core_timing); + KResourceLimit::Register(kernel, system_resource_limit); const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()}; const auto total_size{sizes.first}; @@ -355,6 +358,7 @@ struct KernelCore::Impl { ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {}, core_id) .IsSuccess()); + KThread::Register(system.Kernel(), shutdown_threads[core_id]); } } @@ -729,7 +733,7 @@ struct KernelCore::Impl { memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize()); } - void InitializeHackSharedMemory() { + void InitializeHackSharedMemory(KernelCore& kernel) { // Setup memory regions for emulated processes // TODO(bunnei): These should not be hardcoded regions initialized within the kernel constexpr std::size_t hid_size{0x40000}; @@ -746,14 +750,23 @@ struct KernelCore::Impl { hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, hid_size); + KSharedMemory::Register(kernel, hid_shared_mem); + font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, font_size); + KSharedMemory::Register(kernel, font_shared_mem); + irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, irs_size); + KSharedMemory::Register(kernel, irs_shared_mem); + time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, time_size); + KSharedMemory::Register(kernel, time_shared_mem); + hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, Svc::MemoryPermission::Read, hidbus_size); + KSharedMemory::Register(kernel, hidbus_shared_mem); } std::mutex registered_objects_lock; @@ -1072,12 +1085,15 @@ static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process, // Commit the thread reservation. thread_reservation.Commit(); + // Register the thread. + KThread::Register(kernel, thread); + return std::jthread( [&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] { // Set the thread name. Common::SetCurrentThreadName(thread_name.c_str()); - // Register the thread. + // Set the thread as current. kernel.RegisterHostThread(thread); // Run the callback. @@ -1099,6 +1115,9 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name, // Ensure that we don't hold onto any extra references. SCOPE_EXIT({ process->Close(); }); + // Register the new process. + KProcess::Register(*this, process); + // Run the host thread. return RunHostThreadFunc(*this, process, std::move(process_name), std::move(func)); } @@ -1124,6 +1143,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function // Ensure that we don't hold onto any extra references. SCOPE_EXIT({ process->Close(); }); + // Register the new process. + KProcess::Register(*this, process); + // Reserve a new thread from the process resource limit. KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax); ASSERT(thread_reservation.Succeeded()); @@ -1136,6 +1158,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function // Commit the thread reservation. thread_reservation.Commit(); + // Register the new thread. + KThread::Register(*this, thread); + // Begin running the thread. ASSERT(R_SUCCEEDED(thread->Run())); } 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: auto* session = Kernel::KSession::Create(kernel); session->Initialize(nullptr, 0); + Kernel::KSession::Register(kernel, session); auto next_manager = std::make_shared<Service::SessionRequestManager>( 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_) Kernel::KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit()) .IsSuccess()); + + // Register the process. + Kernel::KProcess::Register(kernel, process); process_created = true; } 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) { m_event = Kernel::KEvent::Create(system.Kernel()); m_event->Initialize(nullptr); + // Register the event. + Kernel::KEvent::Register(system.Kernel(), m_event); + ASSERT(R_SUCCEEDED(m_event->Signal())); } 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 // Initialize event. m_event = Kernel::KEvent::Create(system.Kernel()); m_event->Initialize(nullptr); + + // Register event. + Kernel::KEvent::Register(system.Kernel(), m_event); } ServerManager::~ServerManager() { @@ -160,6 +163,9 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) { // Initialize the event. m_deferral_event->Initialize(nullptr); + // Register the event. + Kernel::KEvent::Register(m_system.Kernel(), m_deferral_event); + // Set the output. *out_event = m_deferral_event; 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, auto* port = Kernel::KPort::Create(kernel); port->Initialize(ServerSessionCountMax, false, 0); + // Register the port. + Kernel::KPort::Register(kernel, port); + service_ports.emplace(name, port); registered_services.emplace(name, handler); 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) { // Commit the session reservation. session_reservation.Commit(); + // Register the session. + Kernel::KSession::Register(system.Kernel(), session); + // Register with server manager. session_manager->GetServerManager().RegisterSession(&session->GetServerSession(), 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 { } if (Settings::IsFastmemEnabled()) { - const bool is_read_enable = Settings::IsGPULevelHigh() || !cached; + const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached; system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); } 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 renderer_vulkan/vk_master_semaphore.h renderer_vulkan/vk_pipeline_cache.cpp renderer_vulkan/vk_pipeline_cache.h + renderer_vulkan/vk_present_manager.cpp + renderer_vulkan/vk_present_manager.h renderer_vulkan/vk_query_cache.cpp renderer_vulkan/vk_query_cache.h 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, .size = sub_size, }); total_size_bytes += sub_size; - largest_copy = std::max(largest_copy, sub_size); + largest_copy = std::max<u64>(largest_copy, sub_size); } const std::span<BufferCopy> copies_span(copies.data(), copies.size()); 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: std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS}; u64 page_offset{cpu_address & HIGHER_PAGE_MASK}; while (remaining_size > 0) { - const std::size_t copy_amount{std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; + const std::size_t copy_amount{ + std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; auto* manager{top_tier[page_index]}; if (manager) { if constexpr (BOOL_BREAK) { @@ -206,7 +207,8 @@ private: u64 begin = std::numeric_limits<u64>::max(); u64 end = 0; while (remaining_size > 0) { - const std::size_t copy_amount{std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; + const std::size_t copy_amount{ + std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)}; auto* manager{top_tier[page_index]}; const auto execute = [&] { 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{ PixelFormat::ASTC_2D_8X8_SRGB, }; -// Missing formats: -// PixelFormat::ASTC_2D_10X5_UNORM -// PixelFormat::ASTC_2D_10X5_SRGB - -// Missing formats: -// PixelFormat::ASTC_2D_10X6_SRGB +constexpr std::array VIEW_CLASS_ASTC_10x5_RGBA{ + PixelFormat::ASTC_2D_10X5_UNORM, + PixelFormat::ASTC_2D_10X5_SRGB, +}; constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{ PixelFormat::ASTC_2D_10X6_UNORM, + PixelFormat::ASTC_2D_10X6_SRGB, }; constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{ @@ -147,9 +146,10 @@ constexpr std::array VIEW_CLASS_ASTC_10x10_RGBA{ PixelFormat::ASTC_2D_10X10_SRGB, }; -// Missing formats -// ASTC_2D_12X10_UNORM, -// ASTC_2D_12X10_SRGB, +constexpr std::array VIEW_CLASS_ASTC_12x10_RGBA{ + PixelFormat::ASTC_2D_12X10_UNORM, + PixelFormat::ASTC_2D_12X10_SRGB, +}; constexpr std::array VIEW_CLASS_ASTC_12x12_RGBA{ PixelFormat::ASTC_2D_12X12_UNORM, @@ -229,9 +229,11 @@ constexpr Table MakeViewTable() { EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA); EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA); EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA); + EnableRange(view, VIEW_CLASS_ASTC_10x5_RGBA); EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA); EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA); EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA); + EnableRange(view, VIEW_CLASS_ASTC_12x10_RGBA); EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA); return view; } 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 @@ #pragma once #include <algorithm> +#include <condition_variable> #include <cstring> #include <deque> #include <functional> #include <memory> +#include <mutex> +#include <thread> #include <queue> #include "common/common_types.h" +#include "common/microprofile.h" +#include "common/scope_exit.h" +#include "common/settings.h" +#include "common/thread.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/gpu.h" #include "video_core/host1x/host1x.h" @@ -23,15 +30,26 @@ class FenceBase { public: explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {} + bool IsStubbed() const { + return is_stubbed; + } + protected: bool is_stubbed; }; -template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache> +template <typename Traits> class FenceManager { + using TFence = typename Traits::FenceType; + using TTextureCache = typename Traits::TextureCacheType; + using TBufferCache = typename Traits::BufferCacheType; + using TQueryCache = typename Traits::QueryCacheType; + static constexpr bool can_async_check = Traits::HAS_ASYNC_CHECK; + public: /// Notify the fence manager about a new frame void TickFrame() { + std::unique_lock lock(ring_guard); delayed_destruction_ring.Tick(); } @@ -46,17 +64,33 @@ public: } void SignalFence(std::function<void()>&& func) { - TryReleasePendingFences(); + rasterizer.InvalidateGPUCache(); + bool delay_fence = Settings::IsGPULevelHigh(); + if constexpr (!can_async_check) { + TryReleasePendingFences<false>(); + } const bool should_flush = ShouldFlush(); CommitAsyncFlushes(); - uncommitted_operations.emplace_back(std::move(func)); - CommitOperations(); TFence new_fence = CreateFence(!should_flush); - fences.push(new_fence); + if constexpr (can_async_check) { + guard.lock(); + } + if (delay_fence) { + uncommitted_operations.emplace_back(std::move(func)); + } + pending_operations.emplace_back(std::move(uncommitted_operations)); QueueFence(new_fence); + if (!delay_fence) { + func(); + } + fences.push(std::move(new_fence)); if (should_flush) { rasterizer.FlushCommands(); } + if constexpr (can_async_check) { + guard.unlock(); + cv.notify_all(); + } } void SignalSyncPoint(u32 value) { @@ -66,29 +100,30 @@ public: } void WaitPendingFences() { - while (!fences.empty()) { - TFence& current_fence = fences.front(); - if (ShouldWait()) { - WaitFence(current_fence); - } - PopAsyncFlushes(); - auto operations = std::move(pending_operations.front()); - pending_operations.pop_front(); - for (auto& operation : operations) { - operation(); - } - PopFence(); + if constexpr (!can_async_check) { + TryReleasePendingFences<true>(); } } protected: explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, - TTextureCache& texture_cache_, TTBufferCache& buffer_cache_, + TTextureCache& texture_cache_, TBufferCache& buffer_cache_, TQueryCache& query_cache_) : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()}, - texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {} + texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} { + if constexpr (can_async_check) { + fence_thread = + std::jthread([this](std::stop_token token) { ReleaseThreadFunc(token); }); + } + } - virtual ~FenceManager() = default; + virtual ~FenceManager() { + if constexpr (can_async_check) { + fence_thread.request_stop(); + cv.notify_all(); + fence_thread.join(); + } + } /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is /// true @@ -104,15 +139,20 @@ protected: Tegra::GPU& gpu; Tegra::Host1x::SyncpointManager& syncpoint_manager; TTextureCache& texture_cache; - TTBufferCache& buffer_cache; + TBufferCache& buffer_cache; TQueryCache& query_cache; private: + template <bool force_wait> void TryReleasePendingFences() { while (!fences.empty()) { TFence& current_fence = fences.front(); if (ShouldWait() && !IsFenceSignaled(current_fence)) { - return; + if constexpr (force_wait) { + WaitFence(current_fence); + } else { + return; + } } PopAsyncFlushes(); auto operations = std::move(pending_operations.front()); @@ -120,7 +160,49 @@ private: for (auto& operation : operations) { operation(); } - PopFence(); + { + std::unique_lock lock(ring_guard); + delayed_destruction_ring.Push(std::move(current_fence)); + } + fences.pop(); + } + } + + void ReleaseThreadFunc(std::stop_token stop_token) { + std::string name = "GPUFencingThread"; + MicroProfileOnThreadCreate(name.c_str()); + + // Cleanup + SCOPE_EXIT({ MicroProfileOnThreadExit(); }); + + Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); + + TFence current_fence; + std::deque<std::function<void()>> current_operations; + while (!stop_token.stop_requested()) { + { + std::unique_lock lock(guard); + cv.wait(lock, [&] { return stop_token.stop_requested() || !fences.empty(); }); + if (stop_token.stop_requested()) [[unlikely]] { + return; + } + current_fence = std::move(fences.front()); + current_operations = std::move(pending_operations.front()); + fences.pop(); + pending_operations.pop_front(); + } + if (!current_fence->IsStubbed()) { + WaitFence(current_fence); + } + PopAsyncFlushes(); + for (auto& operation : current_operations) { + operation(); + } + { + std::unique_lock lock(ring_guard); + delayed_destruction_ring.Push(std::move(current_fence)); + } } } @@ -154,19 +236,16 @@ private: query_cache.CommitAsyncFlushes(); } - void PopFence() { - delayed_destruction_ring.Push(std::move(fences.front())); - fences.pop(); - } - - void CommitOperations() { - pending_operations.emplace_back(std::move(uncommitted_operations)); - } - std::queue<TFence> fences; std::deque<std::function<void()>> uncommitted_operations; std::deque<std::deque<std::function<void()>>> pending_operations; + std::mutex guard; + std::mutex ring_guard; + std::condition_variable cv; + + std::jthread fence_thread; + DelayedDestructionRing<TFence, 6> delayed_destruction_ring; }; 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) { } PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const { + std::unique_lock<std::mutex> lock(guard); return kind_map.GetValueAt(gpu_addr); } @@ -160,7 +161,10 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr } remaining_size -= big_page_size; } - kind_map.Map(gpu_addr, gpu_addr + size, kind); + { + std::unique_lock<std::mutex> lock(guard); + kind_map.Map(gpu_addr, gpu_addr + size, kind); + } return gpu_addr; } @@ -553,6 +557,7 @@ size_t MemoryManager::MaxContinuousRange(GPUVAddr gpu_addr, size_t size) const { } size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const { + std::unique_lock<std::mutex> lock(guard); return kind_map.GetContinuousSizeFrom(gpu_addr); } @@ -745,10 +750,10 @@ void MemoryManager::FlushCaching() { return; } accumulator->Callback([this](GPUVAddr addr, size_t size) { - GetSubmappedRangeImpl<false>(addr, size, page_stash); + GetSubmappedRangeImpl<false>(addr, size, page_stash2); }); - rasterizer->InnerInvalidation(page_stash); - page_stash.clear(); + rasterizer->InnerInvalidation(page_stash2); + page_stash2.clear(); accumulator->Clear(); } 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 @@ #include <atomic> #include <map> +#include <mutex> #include <optional> #include <vector> @@ -215,6 +216,9 @@ private: std::vector<u64> big_page_continuous; std::vector<std::pair<VAddr, std::size_t>> page_stash{}; + std::vector<std::pair<VAddr, std::size_t>> page_stash2{}; + + mutable std::mutex guard; static constexpr size_t continuous_bits = 64; 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 @@ #include <algorithm> #include <array> #include <cstring> +#include <functional> #include <iterator> #include <list> #include <memory> @@ -17,13 +18,19 @@ #include "common/assert.h" #include "common/settings.h" +#include "core/memory.h" #include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" +#include "video_core/texture_cache/slot_vector.h" namespace VideoCommon { +using AsyncJobId = SlotId; + +static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0}; + template <class QueryCache, class HostCounter> class CounterStreamBase { public: @@ -93,9 +100,13 @@ private: template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter> class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> { public: - explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_) - : rasterizer{rasterizer_}, streams{{CounterStream{static_cast<QueryCache&>(*this), - VideoCore::QueryType::SamplesPassed}}} {} + explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_, + Core::Memory::Memory& cpu_memory_) + : rasterizer{rasterizer_}, + cpu_memory{cpu_memory_}, streams{{CounterStream{static_cast<QueryCache&>(*this), + VideoCore::QueryType::SamplesPassed}}} { + (void)slot_async_jobs.insert(); // Null value + } void InvalidateRegion(VAddr addr, std::size_t size) { std::unique_lock lock{mutex}; @@ -126,10 +137,15 @@ public: query = Register(type, *cpu_addr, host_ptr, timestamp.has_value()); } - query->BindCounter(Stream(type).Current(), timestamp); - if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { - AsyncFlushQuery(*cpu_addr); + auto result = query->BindCounter(Stream(type).Current(), timestamp); + if (result) { + auto async_job_id = query->GetAsyncJob(); + auto& async_job = slot_async_jobs[async_job_id]; + async_job.collected = true; + async_job.value = *result; + query->SetAsyncJob(NULL_ASYNC_JOB_ID); } + AsyncFlushQuery(query, timestamp, lock); } /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. @@ -173,15 +189,18 @@ public: } void CommitAsyncFlushes() { + std::unique_lock lock{mutex}; committed_flushes.push_back(uncommitted_flushes); uncommitted_flushes.reset(); } bool HasUncommittedFlushes() const { + std::unique_lock lock{mutex}; return uncommitted_flushes != nullptr; } bool ShouldWaitAsyncFlushes() const { + std::unique_lock lock{mutex}; if (committed_flushes.empty()) { return false; } @@ -189,6 +208,7 @@ public: } void PopAsyncFlushes() { + std::unique_lock lock{mutex}; if (committed_flushes.empty()) { return; } @@ -197,15 +217,25 @@ public: committed_flushes.pop_front(); return; } - for (VAddr query_address : *flush_list) { - FlushAndRemoveRegion(query_address, 4); + for (AsyncJobId async_job_id : *flush_list) { + AsyncJob& async_job = slot_async_jobs[async_job_id]; + if (!async_job.collected) { + FlushAndRemoveRegion(async_job.query_location, 2, true); + } } committed_flushes.pop_front(); } private: + struct AsyncJob { + bool collected = false; + u64 value = 0; + VAddr query_location = 0; + std::optional<u64> timestamp{}; + }; + /// Flushes a memory range to guest memory and removes it from the cache. - void FlushAndRemoveRegion(VAddr addr, std::size_t size) { + void FlushAndRemoveRegion(VAddr addr, std::size_t size, bool async = false) { const u64 addr_begin = addr; const u64 addr_end = addr_begin + size; const auto in_range = [addr_begin, addr_end](const CachedQuery& query) { @@ -226,7 +256,16 @@ private: continue; } rasterizer.UpdatePagesCachedCount(query.GetCpuAddr(), query.SizeInBytes(), -1); - query.Flush(); + AsyncJobId async_job_id = query.GetAsyncJob(); + auto flush_result = query.Flush(async); + if (async_job_id == NULL_ASYNC_JOB_ID) { + ASSERT_MSG(false, "This should not be reachable at all"); + continue; + } + AsyncJob& async_job = slot_async_jobs[async_job_id]; + async_job.collected = true; + async_job.value = flush_result; + query.SetAsyncJob(NULL_ASYNC_JOB_ID); } std::erase_if(contents, in_range); } @@ -253,26 +292,60 @@ private: return found != std::end(contents) ? &*found : nullptr; } - void AsyncFlushQuery(VAddr addr) { - if (!uncommitted_flushes) { - uncommitted_flushes = std::make_shared<std::vector<VAddr>>(); + void AsyncFlushQuery(CachedQuery* query, std::optional<u64> timestamp, + std::unique_lock<std::recursive_mutex>& lock) { + const AsyncJobId new_async_job_id = slot_async_jobs.insert(); + { + AsyncJob& async_job = slot_async_jobs[new_async_job_id]; + query->SetAsyncJob(new_async_job_id); + async_job.query_location = query->GetCpuAddr(); + async_job.collected = false; + + if (!uncommitted_flushes) { + uncommitted_flushes = std::make_shared<std::vector<AsyncJobId>>(); + } + uncommitted_flushes->push_back(new_async_job_id); } - uncommitted_flushes->push_back(addr); + lock.unlock(); + std::function<void()> operation([this, new_async_job_id, timestamp] { + std::unique_lock local_lock{mutex}; + AsyncJob& async_job = slot_async_jobs[new_async_job_id]; + u64 value = async_job.value; + VAddr address = async_job.query_location; + slot_async_jobs.erase(new_async_job_id); + local_lock.unlock(); + if (timestamp) { + u64 timestamp_value = *timestamp; + cpu_memory.WriteBlockUnsafe(address + sizeof(u64), ×tamp_value, sizeof(u64)); + cpu_memory.WriteBlockUnsafe(address, &value, sizeof(u64)); + rasterizer.InvalidateRegion(address, sizeof(u64) * 2, + VideoCommon::CacheType::NoQueryCache); + } else { + u32 small_value = static_cast<u32>(value); + cpu_memory.WriteBlockUnsafe(address, &small_value, sizeof(u32)); + rasterizer.InvalidateRegion(address, sizeof(u32), + VideoCommon::CacheType::NoQueryCache); + } + }); + rasterizer.SyncOperation(std::move(operation)); } static constexpr std::uintptr_t YUZU_PAGESIZE = 4096; static constexpr unsigned YUZU_PAGEBITS = 12; + SlotVector<AsyncJob> slot_async_jobs; + VideoCore::RasterizerInterface& rasterizer; + Core::Memory::Memory& cpu_memory; - std::recursive_mutex mutex; + mutable std::recursive_mutex mutex; std::unordered_map<u64, std::vector<CachedQuery>> cached_queries; std::array<CounterStream, VideoCore::NumQueryTypes> streams; - std::shared_ptr<std::vector<VAddr>> uncommitted_flushes{}; - std::list<std::shared_ptr<std::vector<VAddr>>> committed_flushes; + std::shared_ptr<std::vector<AsyncJobId>> uncommitted_flushes{}; + std::list<std::shared_ptr<std::vector<AsyncJobId>>> committed_flushes; }; template <class QueryCache, class HostCounter> @@ -291,12 +364,12 @@ public: virtual ~HostCounterBase() = default; /// Returns the current value of the query. - u64 Query() { + u64 Query(bool async = false) { if (result) { return *result; } - u64 value = BlockingQuery() + base_result; + u64 value = BlockingQuery(async) + base_result; if (dependency) { value += dependency->Query(); dependency = nullptr; @@ -317,7 +390,7 @@ public: protected: /// Returns the value of query from the backend API blocking as needed. - virtual u64 BlockingQuery() const = 0; + virtual u64 BlockingQuery(bool async = false) const = 0; private: std::shared_ptr<HostCounter> dependency; ///< Counter to add to this value. @@ -340,26 +413,33 @@ public: CachedQueryBase& operator=(const CachedQueryBase&) = delete; /// Flushes the query to guest memory. - virtual void Flush() { + virtual u64 Flush(bool async = false) { // When counter is nullptr it means that it's just been reset. We are supposed to write a // zero in these cases. - const u64 value = counter ? counter->Query() : 0; + const u64 value = counter ? counter->Query(async) : 0; + if (async) { + return value; + } std::memcpy(host_ptr, &value, sizeof(u64)); if (timestamp) { std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64)); } + return value; } /// Binds a counter to this query. - void BindCounter(std::shared_ptr<HostCounter> counter_, std::optional<u64> timestamp_) { + std::optional<u64> BindCounter(std::shared_ptr<HostCounter> counter_, + std::optional<u64> timestamp_) { + std::optional<u64> result{}; if (counter) { // If there's an old counter set it means the query is being rewritten by the game. // To avoid losing the data forever, flush here. - Flush(); + result = std::make_optional(Flush()); } counter = std::move(counter_); timestamp = timestamp_; + return result; } VAddr GetCpuAddr() const noexcept { @@ -374,6 +454,14 @@ public: return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE; } + void SetAsyncJob(AsyncJobId assigned_async_job_) { + assigned_async_job = assigned_async_job_; + } + + AsyncJobId GetAsyncJob() const { + return assigned_async_job; + } + protected: /// Returns true when querying the counter may potentially block. bool WaitPending() const noexcept { @@ -389,6 +477,7 @@ private: u8* host_ptr; ///< Writable host pointer. std::shared_ptr<HostCounter> counter; ///< Host counter to query, owns the dependency tree. std::optional<u64> timestamp; ///< Timestamp to flush to guest memory. + AsyncJobId assigned_async_job; }; } // 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: }; using Fence = std::shared_ptr<GLInnerFence>; -using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>; + +struct FenceManagerParams { + using FenceType = Fence; + using BufferCacheType = BufferCache; + using TextureCacheType = TextureCache; + using QueryCacheType = QueryCache; + + static constexpr bool HAS_ASYNC_CHECK = false; +}; + +using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>; class FenceManagerOpenGL final : public GenericFenceManager { 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) { } // Anonymous namespace -QueryCache::QueryCache(RasterizerOpenGL& rasterizer_) - : QueryCacheBase(rasterizer_), gl_rasterizer{rasterizer_} {} +QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_) + : QueryCacheBase(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {} QueryCache::~QueryCache() = default; @@ -74,7 +74,7 @@ void HostCounter::EndQuery() { glEndQuery(GetTarget(type)); } -u64 HostCounter::BlockingQuery() const { +u64 HostCounter::BlockingQuery([[maybe_unused]] bool async) const { GLint64 value; glGetQueryObjecti64v(query.handle, GL_QUERY_RESULT, &value); return static_cast<u64>(value); @@ -96,7 +96,7 @@ CachedQuery& CachedQuery::operator=(CachedQuery&& rhs) noexcept { return *this; } -void CachedQuery::Flush() { +u64 CachedQuery::Flush([[maybe_unused]] bool async) { // Waiting for a query while another query of the same target is enabled locks Nvidia's driver. // To avoid this disable and re-enable keeping the dependency stream. // But we only have to do this if we have pending waits to be done. @@ -106,11 +106,13 @@ void CachedQuery::Flush() { stream.Update(false); } - VideoCommon::CachedQueryBase<HostCounter>::Flush(); + auto result = VideoCommon::CachedQueryBase<HostCounter>::Flush(); if (slice_counter) { stream.Update(true); } + + return result; } } // 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>; class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { public: - explicit QueryCache(RasterizerOpenGL& rasterizer_); + explicit QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_); ~QueryCache(); OGLQuery AllocateQuery(VideoCore::QueryType type); @@ -51,7 +51,7 @@ public: void EndQuery(); private: - u64 BlockingQuery() const override; + u64 BlockingQuery(bool async = false) const override; QueryCache& cache; const VideoCore::QueryType type; @@ -70,7 +70,7 @@ public: CachedQuery(const CachedQuery&) = delete; CachedQuery& operator=(const CachedQuery&) = delete; - void Flush() override; + u64 Flush(bool async = false) override; private: 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 buffer_cache(*this, cpu_memory_, buffer_cache_runtime), shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, state_tracker, gpu.ShaderNotify()), - query_cache(*this), accelerate_dma(buffer_cache, texture_cache), + query_cache(*this, cpu_memory_), accelerate_dma(buffer_cache, texture_cache), fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), blit_image(program_manager_) {} 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 { case PixelFormat::ASTC_2D_8X5_SRGB: case PixelFormat::ASTC_2D_5X4_SRGB: case PixelFormat::ASTC_2D_5X5_SRGB: + case PixelFormat::ASTC_2D_10X5_SRGB: + case PixelFormat::ASTC_2D_10X6_SRGB: case PixelFormat::ASTC_2D_10X8_SRGB: case PixelFormat::ASTC_2D_6X6_SRGB: case PixelFormat::ASTC_2D_10X10_SRGB: + case PixelFormat::ASTC_2D_12X10_SRGB: case PixelFormat::ASTC_2D_12X12_SRGB: case PixelFormat::ASTC_2D_8X6_SRGB: 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 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM + {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR}, // ASTC_2D_10X6_SRGB {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}, // ASTC_2D_10X5_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}, // ASTC_2D_10X5_SRGB {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB + {GL_COMPRESSED_RGBA_ASTC_12x10_KHR}, // ASTC_2D_12X10_UNORM + {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR}, // ASTC_2D_12X10_SRGB {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB {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 { {VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM {VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB {VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM + {VK_FORMAT_ASTC_10x6_SRGB_BLOCK}, // ASTC_2D_10X6_SRGB {VK_FORMAT_ASTC_10x5_UNORM_BLOCK}, // ASTC_2D_10X5_UNORM {VK_FORMAT_ASTC_10x5_SRGB_BLOCK}, // ASTC_2D_10X5_SRGB {VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM {VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB + {VK_FORMAT_ASTC_12x10_UNORM_BLOCK}, // ASTC_2D_12X10_UNORM + {VK_FORMAT_ASTC_12x10_SRGB_BLOCK}, // ASTC_2D_12X10_SRGB {VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM {VK_FORMAT_ASTC_12x12_SRGB_BLOCK}, // ASTC_2D_12X12_SRGB {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_, state_tracker(), scheduler(device, state_tracker), swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, render_window.GetFramebufferLayout().height, false), - blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, - screen_info), + present_manager(render_window, device, memory_allocator, scheduler, swapchain), + blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager, + scheduler, screen_info), rasterizer(render_window, gpu, cpu_memory, screen_info, device, memory_allocator, state_tracker, scheduler) { if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { @@ -121,46 +122,19 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { return; } // Update screen info if the framebuffer size has changed. - if (screen_info.width != framebuffer->width || screen_info.height != framebuffer->height) { - screen_info.width = framebuffer->width; - screen_info.height = framebuffer->height; - } + screen_info.width = framebuffer->width; + screen_info.height = framebuffer->height; + const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; const bool use_accelerated = rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); const bool is_srgb = use_accelerated && screen_info.is_srgb; RenderScreenshot(*framebuffer, use_accelerated); - bool has_been_recreated = false; - const auto recreate_swapchain = [&](u32 width, u32 height) { - if (!has_been_recreated) { - has_been_recreated = true; - scheduler.Finish(); - } - swapchain.Create(width, height, is_srgb); - }; - - const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); - if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width || - swapchain.GetHeight() != layout.height) { - recreate_swapchain(layout.width, layout.height); - } - bool is_outdated; - do { - swapchain.AcquireNextImage(); - is_outdated = swapchain.IsOutDated(); - if (is_outdated) { - recreate_swapchain(layout.width, layout.height); - } - } while (is_outdated); - if (has_been_recreated) { - blit_screen.Recreate(); - } - const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated); - const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore(); - scheduler.Flush(render_semaphore, present_semaphore); - scheduler.WaitWorker(); - swapchain.Present(render_semaphore); + Frame* frame = present_manager.GetRenderFrame(); + blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb); + scheduler.Flush(*frame->render_ready); + present_manager.Present(frame); gpu.RendererFrameEndNotify(); rasterizer.TickFrame(); @@ -246,8 +220,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr }); const VkExtent2D render_area{.width = layout.width, .height = layout.height}; const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area); - // Since we're not rendering to the screen, ignore the render semaphore. - void(blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated)); + blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated); const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); const VkBufferCreateInfo dst_buffer_info{ @@ -270,7 +243,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr .pNext = nullptr, .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .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 @@ #include "common/dynamic_library.h" #include "video_core/renderer_base.h" #include "video_core/renderer_vulkan/vk_blit_screen.h" +#include "video_core/renderer_vulkan/vk_present_manager.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" @@ -76,6 +77,7 @@ private: StateTracker state_tracker; Scheduler scheduler; Swapchain swapchain; + PresentManager present_manager; BlitScreen blit_screen; RasterizerVulkan rasterizer; 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 { BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWindow& render_window_, const Device& device_, MemoryAllocator& memory_allocator_, - Swapchain& swapchain_, Scheduler& scheduler_, const ScreenInfo& screen_info_) + Swapchain& swapchain_, PresentManager& present_manager_, + Scheduler& scheduler_, const ScreenInfo& screen_info_) : cpu_memory{cpu_memory_}, render_window{render_window_}, device{device_}, - memory_allocator{memory_allocator_}, swapchain{swapchain_}, scheduler{scheduler_}, - image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { + memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_}, + scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_}, + current_srgb{swapchain.IsSrgb()}, image_view_format{swapchain.GetImageViewFormat()} { resource_ticks.resize(image_count); CreateStaticResources(); @@ -135,25 +137,20 @@ BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWin BlitScreen::~BlitScreen() = default; void BlitScreen::Recreate() { + present_manager.WaitPresent(); + scheduler.Finish(); + device.GetLogical().WaitIdle(); CreateDynamicResources(); } -VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, - const VkFramebuffer& host_framebuffer, - const Layout::FramebufferLayout layout, VkExtent2D render_area, - bool use_accelerated) { +void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, + const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout, + VkExtent2D render_area, bool use_accelerated) { RefreshResources(framebuffer); // Finish any pending renderpass scheduler.RequestOutsideRenderPassOperationContext(); - if (const auto swapchain_images = swapchain.GetImageCount(); swapchain_images != image_count) { - image_count = swapchain_images; - Recreate(); - } - - const std::size_t image_index = swapchain.GetImageIndex(); - scheduler.Wait(resource_ticks[image_index]); resource_ticks[image_index] = scheduler.CurrentTick(); @@ -169,7 +166,7 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, std::memcpy(mapped_span.data(), &data, sizeof(data)); if (!use_accelerated) { - const u64 image_offset = GetRawImageOffset(framebuffer, image_index); + const u64 image_offset = GetRawImageOffset(framebuffer); const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset; const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr); @@ -204,8 +201,8 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, .depth = 1, }, }; - scheduler.Record([this, copy, image_index](vk::CommandBuffer cmdbuf) { - const VkImage image = *raw_images[image_index]; + scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) { + const VkImage image = *raw_images[index]; const VkImageMemoryBarrier base_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, @@ -245,14 +242,15 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) { - UpdateAADescriptorSet(image_index, source_image_view, false); + UpdateAADescriptorSet(source_image_view, false); const u32 up_scale = Settings::values.resolution_info.up_scale; const u32 down_shift = Settings::values.resolution_info.down_shift; VkExtent2D size{ .width = (up_scale * framebuffer.width) >> down_shift, .height = (up_scale * framebuffer.height) >> down_shift, }; - scheduler.Record([this, image_index, size, anti_alias_pass](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, index = image_index, size, + anti_alias_pass](vk::CommandBuffer cmdbuf) { const VkImageMemoryBarrier base_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, @@ -326,7 +324,7 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0, - aa_descriptor_sets[image_index], {}); + aa_descriptor_sets[index], {}); cmdbuf.Draw(4, 1, 0, 0); cmdbuf.EndRenderPass(); @@ -369,81 +367,99 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, }; VkImageView fsr_image_view = fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); - UpdateDescriptorSet(image_index, fsr_image_view, true); + UpdateDescriptorSet(fsr_image_view, true); } else { const bool is_nn = Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor; - UpdateDescriptorSet(image_index, source_image_view, is_nn); + UpdateDescriptorSet(source_image_view, is_nn); } - scheduler.Record( - [this, host_framebuffer, image_index, size = render_area](vk::CommandBuffer cmdbuf) { - const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; - const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; - const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; - const VkClearValue clear_color{ - .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, - }; - const VkRenderPassBeginInfo renderpass_bi{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .pNext = nullptr, - .renderPass = *renderpass, - .framebuffer = host_framebuffer, - .renderArea = - { - .offset = {0, 0}, - .extent = size, - }, - .clearValueCount = 1, - .pClearValues = &clear_color, - }; - const VkViewport viewport{ - .x = 0.0f, - .y = 0.0f, - .width = static_cast<float>(size.width), - .height = static_cast<float>(size.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - const VkRect2D scissor{ - .offset = {0, 0}, - .extent = size, - }; - cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); - auto graphics_pipeline = [this]() { - switch (Settings::values.scaling_filter.GetValue()) { - case Settings::ScalingFilter::NearestNeighbor: - case Settings::ScalingFilter::Bilinear: - return *bilinear_pipeline; - case Settings::ScalingFilter::Bicubic: - return *bicubic_pipeline; - case Settings::ScalingFilter::Gaussian: - return *gaussian_pipeline; - case Settings::ScalingFilter::ScaleForce: - return *scaleforce_pipeline; - default: - return *bilinear_pipeline; - } - }(); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); - cmdbuf.SetViewport(0, viewport); - cmdbuf.SetScissor(0, scissor); - - cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, - descriptor_sets[image_index], {}); - cmdbuf.Draw(4, 1, 0, 0); - cmdbuf.EndRenderPass(); - }); - return *semaphores[image_index]; + scheduler.Record([this, host_framebuffer, index = image_index, + size = render_area](vk::CommandBuffer cmdbuf) { + const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; + const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; + const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; + const VkClearValue clear_color{ + .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, + }; + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = *renderpass, + .framebuffer = host_framebuffer, + .renderArea = + { + .offset = {0, 0}, + .extent = size, + }, + .clearValueCount = 1, + .pClearValues = &clear_color, + }; + const VkViewport viewport{ + .x = 0.0f, + .y = 0.0f, + .width = static_cast<float>(size.width), + .height = static_cast<float>(size.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + const VkRect2D scissor{ + .offset = {0, 0}, + .extent = size, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); + auto graphics_pipeline = [this]() { + switch (Settings::values.scaling_filter.GetValue()) { + case Settings::ScalingFilter::NearestNeighbor: + case Settings::ScalingFilter::Bilinear: + return *bilinear_pipeline; + case Settings::ScalingFilter::Bicubic: + return *bicubic_pipeline; + case Settings::ScalingFilter::Gaussian: + return *gaussian_pipeline; + case Settings::ScalingFilter::ScaleForce: + return *scaleforce_pipeline; + default: + return *bilinear_pipeline; + } + }(); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); + cmdbuf.SetViewport(0, viewport); + cmdbuf.SetScissor(0, scissor); + + cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, + descriptor_sets[index], {}); + cmdbuf.Draw(4, 1, 0, 0); + cmdbuf.EndRenderPass(); + }); } -VkSemaphore BlitScreen::DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer, - bool use_accelerated) { - const std::size_t image_index = swapchain.GetImageIndex(); - const VkExtent2D render_area = swapchain.GetSize(); +void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, + bool use_accelerated, bool is_srgb) { + // Recreate dynamic resources if the the image count or colorspace changed + if (const std::size_t swapchain_images = swapchain.GetImageCount(); + swapchain_images != image_count || current_srgb != is_srgb) { + current_srgb = is_srgb; + image_view_format = current_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM; + image_count = swapchain_images; + Recreate(); + } + + // Recreate the presentation frame if the dimensions of the window changed const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); - return Draw(framebuffer, *framebuffers[image_index], layout, render_area, use_accelerated); + if (layout.width != frame->width || layout.height != frame->height || + is_srgb != frame->is_srgb) { + Recreate(); + present_manager.RecreateFrame(frame, layout.width, layout.height, is_srgb, + image_view_format, *renderpass); + } + + const VkExtent2D render_area{frame->width, frame->height}; + Draw(framebuffer, *frame->framebuffer, layout, render_area, use_accelerated); + if (++image_index >= image_count) { + image_index = 0; + } } vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { @@ -471,13 +487,11 @@ void BlitScreen::CreateStaticResources() { } void BlitScreen::CreateDynamicResources() { - CreateSemaphores(); CreateDescriptorPool(); CreateDescriptorSetLayout(); CreateDescriptorSets(); CreatePipelineLayout(); CreateRenderPass(); - CreateFramebuffers(); CreateGraphicsPipeline(); fsr.reset(); smaa.reset(); @@ -525,11 +539,6 @@ void BlitScreen::CreateShaders() { } } -void BlitScreen::CreateSemaphores() { - semaphores.resize(image_count); - std::ranges::generate(semaphores, [this] { return device.GetLogical().CreateSemaphore(); }); -} - void BlitScreen::CreateDescriptorPool() { const std::array<VkDescriptorPoolSize, 2> pool_sizes{{ { @@ -571,10 +580,10 @@ void BlitScreen::CreateDescriptorPool() { } void BlitScreen::CreateRenderPass() { - renderpass = CreateRenderPassImpl(swapchain.GetImageViewFormat()); + renderpass = CreateRenderPassImpl(image_view_format); } -vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format, bool is_present) { +vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) { const VkAttachmentDescription color_attachment{ .flags = 0, .format = format, @@ -584,7 +593,7 @@ vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format, bool is_present .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = is_present ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_GENERAL, + .finalLayout = VK_IMAGE_LAYOUT_GENERAL, }; const VkAttachmentReference color_attachment_ref{ @@ -1052,16 +1061,6 @@ void BlitScreen::CreateSampler() { nn_sampler = device.GetLogical().CreateSampler(ci_nn); } -void BlitScreen::CreateFramebuffers() { - const VkExtent2D size{swapchain.GetSize()}; - framebuffers.resize(image_count); - - for (std::size_t i = 0; i < image_count; ++i) { - const VkImageView image_view{swapchain.GetImageViewIndex(i)}; - framebuffers[i] = CreateFramebuffer(image_view, size, renderpass); - } -} - void BlitScreen::ReleaseRawImages() { for (const u64 tick : resource_ticks) { scheduler.Wait(tick); @@ -1175,7 +1174,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); return; } - aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer), false); + aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer)); aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{ @@ -1319,8 +1318,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci); } -void BlitScreen::UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, - bool nn) const { +void BlitScreen::UpdateAADescriptorSet(VkImageView image_view, bool nn) const { const VkDescriptorImageInfo image_info{ .sampler = nn ? *nn_sampler : *sampler, .imageView = image_view, @@ -1356,8 +1354,7 @@ void BlitScreen::UpdateAADescriptorSet(std::size_t image_index, VkImageView imag device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {}); } -void BlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, - bool nn) const { +void BlitScreen::UpdateDescriptorSet(VkImageView image_view, bool nn) const { const VkDescriptorBufferInfo buffer_info{ .buffer = *buffer, .offset = offsetof(BufferData, uniform), @@ -1480,8 +1477,7 @@ u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count; } -u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, - std::size_t image_index) const { +u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const { constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData)); return first_image_offset + GetSizeInBytes(framebuffer) * image_index; } 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 @@ #include <memory> +#include "core/frontend/framebuffer_layout.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -42,6 +43,9 @@ class RasterizerVulkan; class Scheduler; class SMAA; class Swapchain; +class PresentManager; + +struct Frame; struct ScreenInfo { VkImage image{}; @@ -55,18 +59,17 @@ class BlitScreen { public: explicit BlitScreen(Core::Memory::Memory& cpu_memory, Core::Frontend::EmuWindow& render_window, const Device& device, MemoryAllocator& memory_manager, Swapchain& swapchain, - Scheduler& scheduler, const ScreenInfo& screen_info); + PresentManager& present_manager, Scheduler& scheduler, + const ScreenInfo& screen_info); ~BlitScreen(); void Recreate(); - [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer, - const VkFramebuffer& host_framebuffer, - const Layout::FramebufferLayout layout, VkExtent2D render_area, - bool use_accelerated); + void Draw(const Tegra::FramebufferConfig& framebuffer, const VkFramebuffer& host_framebuffer, + const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated); - [[nodiscard]] VkSemaphore DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer, - bool use_accelerated); + void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, + bool use_accelerated, bool is_srgb); [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent); @@ -79,10 +82,9 @@ private: void CreateStaticResources(); void CreateShaders(); - void CreateSemaphores(); void CreateDescriptorPool(); void CreateRenderPass(); - vk::RenderPass CreateRenderPassImpl(VkFormat, bool is_present = true); + vk::RenderPass CreateRenderPassImpl(VkFormat format); void CreateDescriptorSetLayout(); void CreateDescriptorSets(); void CreatePipelineLayout(); @@ -90,15 +92,14 @@ private: void CreateSampler(); void CreateDynamicResources(); - void CreateFramebuffers(); void RefreshResources(const Tegra::FramebufferConfig& framebuffer); void ReleaseRawImages(); void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); - void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const; - void UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const; + void UpdateDescriptorSet(VkImageView image_view, bool nn) const; + void UpdateAADescriptorSet(VkImageView image_view, bool nn) const; void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const; void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, const Layout::FramebufferLayout layout) const; @@ -107,16 +108,17 @@ private: void CreateFSR(); u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; - u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, - std::size_t image_index) const; + u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const; Core::Memory::Memory& cpu_memory; Core::Frontend::EmuWindow& render_window; const Device& device; MemoryAllocator& memory_allocator; Swapchain& swapchain; + PresentManager& present_manager; Scheduler& scheduler; std::size_t image_count; + std::size_t image_index{}; const ScreenInfo& screen_info; vk::ShaderModule vertex_shader; @@ -135,7 +137,6 @@ private: vk::Pipeline gaussian_pipeline; vk::Pipeline scaleforce_pipeline; vk::RenderPass renderpass; - std::vector<vk::Framebuffer> framebuffers; vk::DescriptorSets descriptor_sets; vk::Sampler nn_sampler; vk::Sampler sampler; @@ -145,7 +146,6 @@ private: std::vector<u64> resource_ticks; - std::vector<vk::Semaphore> semaphores; std::vector<vk::Image> raw_images; std::vector<vk::ImageView> raw_image_views; std::vector<MemoryCommit> raw_buffer_commits; @@ -164,6 +164,8 @@ private: u32 raw_width = 0; u32 raw_height = 0; Service::android::PixelFormat pixel_format{}; + bool current_srgb; + VkFormat image_view_format; std::unique_ptr<FSR> fsr; 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 @@ #include "video_core/renderer_vulkan/vk_buffer_cache.h" #include "video_core/renderer_vulkan/vk_fence_manager.h" +#include "video_core/renderer_vulkan/vk_query_cache.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_texture_cache.h" #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: }; using Fence = std::shared_ptr<InnerFence>; -using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>; +struct FenceManagerParams { + using FenceType = Fence; + using BufferCacheType = BufferCache; + using TextureCacheType = TextureCache; + using QueryCacheType = QueryCache; + + static constexpr bool HAS_ASYNC_CHECK = true; +}; + +using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>; class FenceManager final : public GenericFenceManager { 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 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/microprofile.h" +#include "common/settings.h" +#include "common/thread.h" +#include "video_core/renderer_vulkan/vk_present_manager.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_swapchain.h" +#include "video_core/vulkan_common/vulkan_device.h" + +namespace Vulkan { + +MICROPROFILE_DEFINE(Vulkan_WaitPresent, "Vulkan", "Wait For Present", MP_RGB(128, 128, 128)); +MICROPROFILE_DEFINE(Vulkan_CopyToSwapchain, "Vulkan", "Copy to swapchain", MP_RGB(192, 255, 192)); + +namespace { + +bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat format) { + const VkFormatProperties props{physical_device.GetFormatProperties(format)}; + return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT); +} + +[[nodiscard]] VkImageSubresourceLayers MakeImageSubresourceLayers() { + return VkImageSubresourceLayers{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }; +} + +[[nodiscard]] VkImageBlit MakeImageBlit(s32 frame_width, s32 frame_height, s32 swapchain_width, + s32 swapchain_height) { + return VkImageBlit{ + .srcSubresource = MakeImageSubresourceLayers(), + .srcOffsets = + { + { + .x = 0, + .y = 0, + .z = 0, + }, + { + .x = frame_width, + .y = frame_height, + .z = 1, + }, + }, + .dstSubresource = MakeImageSubresourceLayers(), + .dstOffsets = + { + { + .x = 0, + .y = 0, + .z = 0, + }, + { + .x = swapchain_width, + .y = swapchain_height, + .z = 1, + }, + }, + }; +} + +[[nodiscard]] VkImageCopy MakeImageCopy(u32 frame_width, u32 frame_height, u32 swapchain_width, + u32 swapchain_height) { + return VkImageCopy{ + .srcSubresource = MakeImageSubresourceLayers(), + .srcOffset = + { + .x = 0, + .y = 0, + .z = 0, + }, + .dstSubresource = MakeImageSubresourceLayers(), + .dstOffset = + { + .x = 0, + .y = 0, + .z = 0, + }, + .extent = + { + .width = std::min(frame_width, swapchain_width), + .height = std::min(frame_height, swapchain_height), + .depth = 1, + }, + }; +} + +} // Anonymous namespace + +PresentManager::PresentManager(Core::Frontend::EmuWindow& render_window_, const Device& device_, + MemoryAllocator& memory_allocator_, Scheduler& scheduler_, + Swapchain& swapchain_) + : render_window{render_window_}, device{device_}, + memory_allocator{memory_allocator_}, scheduler{scheduler_}, swapchain{swapchain_}, + blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())}, + use_present_thread{Settings::values.async_presentation.GetValue()}, + image_count{swapchain.GetImageCount()} { + + auto& dld = device.GetLogical(); + cmdpool = dld.CreateCommandPool({ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = + VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = device.GetGraphicsFamily(), + }); + auto cmdbuffers = cmdpool.Allocate(image_count); + + frames.resize(image_count); + for (u32 i = 0; i < frames.size(); i++) { + Frame& frame = frames[i]; + frame.cmdbuf = vk::CommandBuffer{cmdbuffers[i], device.GetDispatchLoader()}; + frame.render_ready = dld.CreateSemaphore({ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + }); + frame.present_done = dld.CreateFence({ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .pNext = nullptr, + .flags = VK_FENCE_CREATE_SIGNALED_BIT, + }); + free_queue.push(&frame); + } + + if (use_present_thread) { + present_thread = std::jthread([this](std::stop_token token) { PresentThread(token); }); + } +} + +PresentManager::~PresentManager() = default; + +Frame* PresentManager::GetRenderFrame() { + MICROPROFILE_SCOPE(Vulkan_WaitPresent); + + // Wait for free presentation frames + std::unique_lock lock{free_mutex}; + free_cv.wait(lock, [this] { return !free_queue.empty(); }); + + // Take the frame from the queue + Frame* frame = free_queue.front(); + free_queue.pop(); + + // Wait for the presentation to be finished so all frame resources are free + frame->present_done.Wait(); + frame->present_done.Reset(); + + return frame; +} + +void PresentManager::Present(Frame* frame) { + if (!use_present_thread) { + scheduler.WaitWorker(); + CopyToSwapchain(frame); + free_queue.push(frame); + return; + } + + scheduler.Record([this, frame](vk::CommandBuffer) { + std::unique_lock lock{queue_mutex}; + present_queue.push(frame); + frame_cv.notify_one(); + }); +} + +void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb, + VkFormat image_view_format, VkRenderPass rd) { + auto& dld = device.GetLogical(); + + frame->width = width; + frame->height = height; + frame->is_srgb = is_srgb; + + frame->image = dld.CreateImage({ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, + .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, + .imageType = VK_IMAGE_TYPE_2D, + .format = swapchain.GetImageFormat(), + .extent = + { + .width = width, + .height = height, + .depth = 1, + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }); + + frame->image_commit = memory_allocator.Commit(frame->image, MemoryUsage::DeviceLocal); + + frame->image_view = dld.CreateImageView({ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .image = *frame->image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = image_view_format, + .components = + { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }); + + const VkImageView image_view{*frame->image_view}; + frame->framebuffer = dld.CreateFramebuffer({ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .renderPass = rd, + .attachmentCount = 1, + .pAttachments = &image_view, + .width = width, + .height = height, + .layers = 1, + }); +} + +void PresentManager::WaitPresent() { + if (!use_present_thread) { + return; + } + + // Wait for the present queue to be empty + { + std::unique_lock queue_lock{queue_mutex}; + frame_cv.wait(queue_lock, [this] { return present_queue.empty(); }); + } + + // The above condition will be satisfied when the last frame is taken from the queue. + // To ensure that frame has been presented as well take hold of the swapchain + // mutex. + std::scoped_lock swapchain_lock{swapchain_mutex}; +} + +void PresentManager::PresentThread(std::stop_token token) { + Common::SetCurrentThreadName("VulkanPresent"); + while (!token.stop_requested()) { + std::unique_lock lock{queue_mutex}; + + // Wait for presentation frames + Common::CondvarWait(frame_cv, lock, token, [this] { return !present_queue.empty(); }); + if (token.stop_requested()) { + return; + } + + // Take the frame and notify anyone waiting + Frame* frame = present_queue.front(); + present_queue.pop(); + frame_cv.notify_one(); + + // By exchanging the lock ownership we take the swapchain lock + // before the queue lock goes out of scope. This way the swapchain + // lock in WaitPresent is guaranteed to occur after here. + std::exchange(lock, std::unique_lock{swapchain_mutex}); + + CopyToSwapchain(frame); + + // Free the frame for reuse + std::scoped_lock fl{free_mutex}; + free_queue.push(frame); + free_cv.notify_one(); + } +} + +void PresentManager::CopyToSwapchain(Frame* frame) { + MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain); + + const auto recreate_swapchain = [&] { + swapchain.Create(frame->width, frame->height, frame->is_srgb); + image_count = swapchain.GetImageCount(); + }; + + // If the size or colorspace of the incoming frames has changed, recreate the swapchain + // to account for that. + const bool srgb_changed = swapchain.NeedsRecreation(frame->is_srgb); + const bool size_changed = + swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height; + if (srgb_changed || size_changed) { + recreate_swapchain(); + } + + while (swapchain.AcquireNextImage()) { + recreate_swapchain(); + } + + const vk::CommandBuffer cmdbuf{frame->cmdbuf}; + cmdbuf.Begin({ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + .pInheritanceInfo = nullptr, + }); + + const VkImage image{swapchain.CurrentImage()}; + const VkExtent2D extent = swapchain.GetExtent(); + const std::array pre_barriers{ + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = *frame->image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }, + }; + const std::array post_barriers{ + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = *frame->image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }, + }; + + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, {}, + {}, {}, pre_barriers); + + if (blit_supported) { + cmdbuf.BlitImage(*frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + MakeImageBlit(frame->width, frame->height, extent.width, extent.height), + VK_FILTER_LINEAR); + } else { + cmdbuf.CopyImage(*frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + MakeImageCopy(frame->width, frame->height, extent.width, extent.height)); + } + + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, {}, + {}, {}, post_barriers); + + cmdbuf.End(); + + const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore(); + const VkSemaphore render_semaphore = swapchain.CurrentRenderSemaphore(); + const std::array wait_semaphores = {present_semaphore, *frame->render_ready}; + + static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{ + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + }; + + const VkSubmitInfo submit_info{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 2U, + .pWaitSemaphores = wait_semaphores.data(), + .pWaitDstStageMask = wait_stage_masks.data(), + .commandBufferCount = 1, + .pCommandBuffers = cmdbuf.address(), + .signalSemaphoreCount = 1U, + .pSignalSemaphores = &render_semaphore, + }; + + // Submit the image copy/blit to the swapchain + { + std::scoped_lock lock{scheduler.submit_mutex}; + switch (const VkResult result = + device.GetGraphicsQueue().Submit(submit_info, *frame->present_done)) { + case VK_SUCCESS: + break; + case VK_ERROR_DEVICE_LOST: + device.ReportLoss(); + [[fallthrough]]; + default: + vk::Check(result); + break; + } + } + + // Present + swapchain.Present(render_semaphore); +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <condition_variable> +#include <mutex> +#include <queue> + +#include "common/common_types.h" +#include "common/polyfill_thread.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Core::Frontend { +class EmuWindow; +} // namespace Core::Frontend + +namespace Vulkan { + +class Device; +class Scheduler; +class Swapchain; + +struct Frame { + u32 width; + u32 height; + bool is_srgb; + vk::Image image; + vk::ImageView image_view; + vk::Framebuffer framebuffer; + MemoryCommit image_commit; + vk::CommandBuffer cmdbuf; + vk::Semaphore render_ready; + vk::Fence present_done; +}; + +class PresentManager { +public: + PresentManager(Core::Frontend::EmuWindow& render_window, const Device& device, + MemoryAllocator& memory_allocator, Scheduler& scheduler, Swapchain& swapchain); + ~PresentManager(); + + /// Returns the last used presentation frame + Frame* GetRenderFrame(); + + /// Pushes a frame for presentation + void Present(Frame* frame); + + /// Recreates the present frame to match the provided parameters + void RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb, + VkFormat image_view_format, VkRenderPass rd); + + /// Waits for the present thread to finish presenting all queued frames. + void WaitPresent(); + +private: + void PresentThread(std::stop_token token); + + void CopyToSwapchain(Frame* frame); + +private: + Core::Frontend::EmuWindow& render_window; + const Device& device; + MemoryAllocator& memory_allocator; + Scheduler& scheduler; + Swapchain& swapchain; + vk::CommandPool cmdpool; + std::vector<Frame> frames; + std::queue<Frame*> present_queue; + std::queue<Frame*> free_queue; + std::condition_variable_any frame_cv; + std::condition_variable free_cv; + std::mutex swapchain_mutex; + std::mutex queue_mutex; + std::mutex free_mutex; + std::jthread present_thread; + bool blit_supported; + bool use_present_thread; + std::size_t image_count; +}; + +} // 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) { } } -QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, +QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, + Core::Memory::Memory& cpu_memory_, const Device& device_, Scheduler& scheduler_) - : QueryCacheBase{rasterizer_}, device{device_}, scheduler{scheduler_}, + : QueryCacheBase{rasterizer_, cpu_memory_}, device{device_}, scheduler{scheduler_}, query_pools{ QueryPool{device_, scheduler_, QueryType::SamplesPassed}, } {} @@ -98,8 +99,10 @@ HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr<HostCounter> depend query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} { const vk::Device* logical = &cache.GetDevice().GetLogical(); cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { + const bool use_precise = Settings::IsGPULevelHigh(); logical->ResetQueryPool(query.first, query.second, 1); - cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT); + cmdbuf.BeginQuery(query.first, query.second, + use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0); }); } @@ -112,8 +115,10 @@ void HostCounter::EndQuery() { [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); }); } -u64 HostCounter::BlockingQuery() const { - cache.GetScheduler().Wait(tick); +u64 HostCounter::BlockingQuery(bool async) const { + if (!async) { + cache.GetScheduler().Wait(tick); + } u64 data; const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults( 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: class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> { public: - explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_, + explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, + Core::Memory::Memory& cpu_memory_, const Device& device_, Scheduler& scheduler_); ~QueryCache(); @@ -83,7 +84,7 @@ public: void EndQuery(); private: - u64 BlockingQuery() const override; + u64 BlockingQuery(bool async = false) const override; QueryCache& cache; 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 buffer_cache(*this, cpu_memory_, buffer_cache_runtime), pipeline_cache(*this, device, scheduler, descriptor_pool, update_descriptor_queue, render_pass_cache, buffer_cache, texture_cache, gpu.ShaderNotify()), - query_cache{*this, device, scheduler}, accelerate_dma(buffer_cache, texture_cache, scheduler), + query_cache{*this, cpu_memory_, device, scheduler}, + accelerate_dma(buffer_cache, texture_cache, scheduler), fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), wfi_event(device.GetLogical().CreateEvent()) { scheduler.SetQueryCache(query_cache); @@ -675,7 +676,8 @@ bool RasterizerVulkan::AccelerateConditionalRendering() { const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()}; Maxwell::ReportSemaphore::Compare cmp; if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp), - VideoCommon::CacheType::BufferCache)) { + VideoCommon::CacheType::BufferCache | + VideoCommon::CacheType::QueryCache)) { return true; } 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_) Scheduler::~Scheduler() = default; -void Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { +u64 Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { // When flushing, we only send data to the worker thread; no waiting is necessary. - SubmitExecution(signal_semaphore, wait_semaphore); + const u64 signal_value = SubmitExecution(signal_semaphore, wait_semaphore); AllocateNewContext(); + return signal_value; } void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { @@ -205,7 +206,7 @@ void Scheduler::AllocateWorkerCommandBuffer() { }); } -void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { +u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { EndPendingOperations(); InvalidateState(); @@ -217,6 +218,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s on_submit(); } + std::scoped_lock lock{submit_mutex}; switch (const VkResult result = master_semaphore->SubmitQueue( cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { case VK_SUCCESS: @@ -231,6 +233,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s }); chunk->MarkSubmit(); DispatchWork(); + return signal_value; } 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: ~Scheduler(); /// Sends the current execution context to the GPU. - void Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); + u64 Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); /// Sends the current execution context to the GPU and waits for it to complete. void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); @@ -106,6 +106,8 @@ public: return *master_semaphore; } + std::mutex submit_mutex; + private: class Command { public: @@ -201,7 +203,7 @@ private: void AllocateWorkerCommandBuffer(); - void SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore); + u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore); void AllocateNewContext(); 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) { return; } - device.GetLogical().WaitIdle(); Destroy(); CreateSwapchain(capabilities, srgb); CreateSemaphores(); - CreateImageViews(); resource_ticks.clear(); resource_ticks.resize(image_count); } -void Swapchain::AcquireNextImage() { +bool Swapchain::AcquireNextImage() { const VkResult result = device.GetLogical().AcquireNextImageKHR( *swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index], VK_NULL_HANDLE, &image_index); @@ -127,8 +125,11 @@ void Swapchain::AcquireNextImage() { LOG_ERROR(Render_Vulkan, "vkAcquireNextImageKHR returned {}", vk::ToString(result)); break; } + scheduler.Wait(resource_ticks[image_index]); resource_ticks[image_index] = scheduler.CurrentTick(); + + return is_suboptimal || is_outdated; } void Swapchain::Present(VkSemaphore render_semaphore) { @@ -143,6 +144,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) { .pImageIndices = &image_index, .pResults = nullptr, }; + std::scoped_lock lock{scheduler.submit_mutex}; switch (const VkResult result = present_queue.Present(present_info)) { case VK_SUCCESS: break; @@ -168,7 +170,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)}; - const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)}; + surface_format = ChooseSwapSurfaceFormat(formats); present_mode = ChooseSwapPresentMode(present_modes); u32 requested_image_count{capabilities.minImageCount + 1}; @@ -193,7 +195,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo .imageColorSpace = surface_format.colorSpace, .imageExtent = {}, .imageArrayLayers = 1, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -241,45 +243,14 @@ void Swapchain::CreateSemaphores() { present_semaphores.resize(image_count); std::ranges::generate(present_semaphores, [this] { return device.GetLogical().CreateSemaphore(); }); -} - -void Swapchain::CreateImageViews() { - VkImageViewCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .image = {}, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = image_view_format, - .components = - { - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - - image_views.resize(image_count); - for (std::size_t i = 0; i < image_count; i++) { - ci.image = images[i]; - image_views[i] = device.GetLogical().CreateImageView(ci); - } + render_semaphores.resize(image_count); + std::ranges::generate(render_semaphores, + [this] { return device.GetLogical().CreateSemaphore(); }); } void Swapchain::Destroy() { frame_index = 0; present_semaphores.clear(); - framebuffers.clear(); - image_views.clear(); swapchain.reset(); } 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: void Create(u32 width, u32 height, bool srgb); /// Acquires the next image in the swapchain, waits as needed. - void AcquireNextImage(); + bool AcquireNextImage(); /// Presents the rendered image to the swapchain. void Present(VkSemaphore render_semaphore); @@ -52,6 +52,11 @@ public: return is_suboptimal; } + /// Returns true when the swapchain format is in the srgb color space + bool IsSrgb() const { + return current_srgb; + } + VkExtent2D GetSize() const { return extent; } @@ -64,22 +69,34 @@ public: return image_index; } + std::size_t GetFrameIndex() const { + return frame_index; + } + VkImage GetImageIndex(std::size_t index) const { return images[index]; } - VkImageView GetImageViewIndex(std::size_t index) const { - return *image_views[index]; + VkImage CurrentImage() const { + return images[image_index]; } VkFormat GetImageViewFormat() const { return image_view_format; } + VkFormat GetImageFormat() const { + return surface_format.format; + } + VkSemaphore CurrentPresentSemaphore() const { return *present_semaphores[frame_index]; } + VkSemaphore CurrentRenderSemaphore() const { + return *render_semaphores[frame_index]; + } + u32 GetWidth() const { return width; } @@ -88,6 +105,10 @@ public: return height; } + VkExtent2D GetExtent() const { + return extent; + } + private: void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb); void CreateSemaphores(); @@ -107,10 +128,9 @@ private: std::size_t image_count{}; std::vector<VkImage> images; - std::vector<vk::ImageView> image_views; - std::vector<vk::Framebuffer> framebuffers; std::vector<u64> resource_ticks; std::vector<vk::Semaphore> present_semaphores; + std::vector<vk::Semaphore> render_semaphores; u32 width; u32 height; @@ -121,6 +141,7 @@ private: VkFormat image_view_format{}; VkExtent2D extent{}; VkPresentModeKHR present_mode{}; + VkSurfaceFormatKHR surface_format{}; bool current_srgb{}; 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 { UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_) : device{device_}, scheduler{scheduler_} { + payload_start = payload.data(); payload_cursor = payload.data(); } UpdateDescriptorQueue::~UpdateDescriptorQueue() = default; void UpdateDescriptorQueue::TickFrame() { - payload_cursor = payload.data(); + if (++frame_index >= FRAMES_IN_FLIGHT) { + frame_index = 0; + } + payload_start = payload.data() + frame_index * FRAME_PAYLOAD_SIZE; + payload_cursor = payload_start; } void UpdateDescriptorQueue::Acquire() { @@ -28,10 +33,10 @@ void UpdateDescriptorQueue::Acquire() { // This is the maximum number of entries a single draw call might use. static constexpr size_t MIN_ENTRIES = 0x400; - if (std::distance(payload.data(), payload_cursor) + MIN_ENTRIES >= payload.max_size()) { + if (std::distance(payload_start, payload_cursor) + MIN_ENTRIES >= FRAME_PAYLOAD_SIZE) { LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); scheduler.WaitWorker(); - payload_cursor = payload.data(); + payload_cursor = payload_start; } upload_start = payload_cursor; } 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 { }; class UpdateDescriptorQueue final { + // This should be plenty for the vast majority of cases. Most desktop platforms only + // provide up to 3 swapchain images. + static constexpr size_t FRAMES_IN_FLIGHT = 5; + static constexpr size_t FRAME_PAYLOAD_SIZE = 0x10000; + static constexpr size_t PAYLOAD_SIZE = FRAME_PAYLOAD_SIZE * FRAMES_IN_FLIGHT; + public: explicit UpdateDescriptorQueue(const Device& device_, Scheduler& scheduler_); ~UpdateDescriptorQueue(); @@ -73,9 +79,11 @@ private: const Device& device; Scheduler& scheduler; + size_t frame_index{0}; DescriptorUpdateEntry* payload_cursor = nullptr; + DescriptorUpdateEntry* payload_start = nullptr; const DescriptorUpdateEntry* upload_start = nullptr; - std::array<DescriptorUpdateEntry, 0x10000> payload; + std::array<DescriptorUpdateEntry, PAYLOAD_SIZE> payload; }; } // 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) { case PixelFormat::ASTC_2D_6X6_UNORM: case PixelFormat::ASTC_2D_6X6_SRGB: case PixelFormat::ASTC_2D_10X6_UNORM: + case PixelFormat::ASTC_2D_10X6_SRGB: case PixelFormat::ASTC_2D_10X5_UNORM: case PixelFormat::ASTC_2D_10X5_SRGB: case PixelFormat::ASTC_2D_10X10_UNORM: case PixelFormat::ASTC_2D_10X10_SRGB: + case PixelFormat::ASTC_2D_12X10_UNORM: + case PixelFormat::ASTC_2D_12X10_SRGB: case PixelFormat::ASTC_2D_12X12_UNORM: case PixelFormat::ASTC_2D_12X12_SRGB: case PixelFormat::ASTC_2D_8X6_UNORM: @@ -279,11 +282,13 @@ bool IsPixelFormatSRGB(PixelFormat format) { case PixelFormat::ASTC_2D_8X5_SRGB: case PixelFormat::ASTC_2D_5X4_SRGB: case PixelFormat::ASTC_2D_5X5_SRGB: + case PixelFormat::ASTC_2D_10X6_SRGB: case PixelFormat::ASTC_2D_10X8_SRGB: case PixelFormat::ASTC_2D_6X6_SRGB: case PixelFormat::ASTC_2D_10X5_SRGB: case PixelFormat::ASTC_2D_10X10_SRGB: case PixelFormat::ASTC_2D_12X12_SRGB: + case PixelFormat::ASTC_2D_12X10_SRGB: case PixelFormat::ASTC_2D_8X6_SRGB: case PixelFormat::ASTC_2D_6X5_SRGB: 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 { ASTC_2D_6X6_UNORM, ASTC_2D_6X6_SRGB, ASTC_2D_10X6_UNORM, + ASTC_2D_10X6_SRGB, ASTC_2D_10X5_UNORM, ASTC_2D_10X5_SRGB, ASTC_2D_10X10_UNORM, ASTC_2D_10X10_SRGB, + ASTC_2D_12X10_UNORM, + ASTC_2D_12X10_SRGB, ASTC_2D_12X12_UNORM, ASTC_2D_12X12_SRGB, ASTC_2D_8X6_UNORM, @@ -232,10 +235,13 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{ 6, // ASTC_2D_6X6_UNORM 6, // ASTC_2D_6X6_SRGB 10, // ASTC_2D_10X6_UNORM + 10, // ASTC_2D_10X6_SRGB 10, // ASTC_2D_10X5_UNORM 10, // ASTC_2D_10X5_SRGB 10, // ASTC_2D_10X10_UNORM 10, // ASTC_2D_10X10_SRGB + 12, // ASTC_2D_12X10_UNORM + 12, // ASTC_2D_12X10_SRGB 12, // ASTC_2D_12X12_UNORM 12, // ASTC_2D_12X12_SRGB 8, // ASTC_2D_8X6_UNORM @@ -338,10 +344,13 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{ 6, // ASTC_2D_6X6_UNORM 6, // ASTC_2D_6X6_SRGB 6, // ASTC_2D_10X6_UNORM + 6, // ASTC_2D_10X6_SRGB 5, // ASTC_2D_10X5_UNORM 5, // ASTC_2D_10X5_SRGB 10, // ASTC_2D_10X10_UNORM 10, // ASTC_2D_10X10_SRGB + 10, // ASTC_2D_12X10_UNORM + 10, // ASTC_2D_12X10_SRGB 12, // ASTC_2D_12X12_UNORM 12, // ASTC_2D_12X12_SRGB 6, // ASTC_2D_8X6_UNORM @@ -444,10 +453,13 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{ 128, // ASTC_2D_6X6_UNORM 128, // ASTC_2D_6X6_SRGB 128, // ASTC_2D_10X6_UNORM + 128, // ASTC_2D_10X6_SRGB 128, // ASTC_2D_10X5_UNORM 128, // ASTC_2D_10X5_SRGB 128, // ASTC_2D_10X10_UNORM 128, // ASTC_2D_10X10_SRGB + 128, // ASTC_2D_12X10_UNORM + 128, // ASTC_2D_12X10_SRGB 128, // ASTC_2D_12X12_UNORM 128, // ASTC_2D_12X12_SRGB 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, return PixelFormat::ASTC_2D_6X6_SRGB; case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR): return PixelFormat::ASTC_2D_10X6_UNORM; + case Hash(TextureFormat::ASTC_2D_10X6, UNORM, SRGB): + return PixelFormat::ASTC_2D_10X6_SRGB; case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR): return PixelFormat::ASTC_2D_10X5_UNORM; case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB): @@ -218,6 +220,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::ASTC_2D_10X10_UNORM; case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB): return PixelFormat::ASTC_2D_10X10_SRGB; + case Hash(TextureFormat::ASTC_2D_12X10, UNORM, LINEAR): + return PixelFormat::ASTC_2D_12X10_UNORM; + case Hash(TextureFormat::ASTC_2D_12X10, UNORM, SRGB): + return PixelFormat::ASTC_2D_12X10_SRGB; case Hash(TextureFormat::ASTC_2D_12X12, UNORM, LINEAR): return PixelFormat::ASTC_2D_12X12_UNORM; 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 return "ASTC_2D_6X6_SRGB"; case PixelFormat::ASTC_2D_10X6_UNORM: return "ASTC_2D_10X6_UNORM"; + case PixelFormat::ASTC_2D_10X6_SRGB: + return "ASTC_2D_10X6_SRGB"; case PixelFormat::ASTC_2D_10X5_UNORM: return "ASTC_2D_10X5_UNORM"; case PixelFormat::ASTC_2D_10X5_SRGB: @@ -187,6 +189,10 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str return "ASTC_2D_10X10_UNORM"; case PixelFormat::ASTC_2D_10X10_SRGB: return "ASTC_2D_10X10_SRGB"; + case PixelFormat::ASTC_2D_12X10_UNORM: + return "ASTC_2D_12X10_UNORM"; + case PixelFormat::ASTC_2D_12X10_SRGB: + return "ASTC_2D_12X10_SRGB"; case PixelFormat::ASTC_2D_12X12_UNORM: return "ASTC_2D_12X12_UNORM"; 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 buffer, download_map.buffer, }; - std::array buffer_offsets{ + std::array<u64, 2> buffer_offsets{ buffer_offset, download_map.offset, }; 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 { const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; - return validated_driver && !is_steam_deck; + const bool is_debugging = this->HasDebuggingToolAttached(); + + return validated_driver && !is_steam_deck && !is_debugging; } 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() { qt_config->beginGroup(QStringLiteral("Core")); ReadGlobalSetting(Settings::values.use_multi_core); - ReadGlobalSetting(Settings::values.use_extended_memory_layout); + ReadGlobalSetting(Settings::values.use_unsafe_extended_memory_layout); qt_config->endGroup(); } @@ -692,6 +692,7 @@ void Config::ReadRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); ReadGlobalSetting(Settings::values.renderer_backend); + ReadGlobalSetting(Settings::values.async_presentation); ReadGlobalSetting(Settings::values.renderer_force_max_clock); ReadGlobalSetting(Settings::values.vulkan_device); ReadGlobalSetting(Settings::values.fullscreen_mode); @@ -712,7 +713,6 @@ void Config::ReadRendererValues() { ReadGlobalSetting(Settings::values.shader_backend); ReadGlobalSetting(Settings::values.use_asynchronous_shaders); ReadGlobalSetting(Settings::values.use_fast_gpu_time); - ReadGlobalSetting(Settings::values.use_pessimistic_flushes); ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); ReadGlobalSetting(Settings::values.bg_red); ReadGlobalSetting(Settings::values.bg_green); @@ -1161,7 +1161,7 @@ void Config::SaveCoreValues() { qt_config->beginGroup(QStringLiteral("Core")); WriteGlobalSetting(Settings::values.use_multi_core); - WriteGlobalSetting(Settings::values.use_extended_memory_layout); + WriteGlobalSetting(Settings::values.use_unsafe_extended_memory_layout); qt_config->endGroup(); } @@ -1313,6 +1313,7 @@ void Config::SaveRendererValues() { static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), static_cast<u32>(Settings::values.renderer_backend.GetDefault()), Settings::values.renderer_backend.UsingGlobal()); + WriteGlobalSetting(Settings::values.async_presentation); WriteGlobalSetting(Settings::values.renderer_force_max_clock); WriteGlobalSetting(Settings::values.vulkan_device); WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()), @@ -1357,7 +1358,6 @@ void Config::SaveRendererValues() { Settings::values.shader_backend.UsingGlobal()); WriteGlobalSetting(Settings::values.use_asynchronous_shaders); WriteGlobalSetting(Settings::values.use_fast_gpu_time); - WriteGlobalSetting(Settings::values.use_pessimistic_flushes); WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); WriteGlobalSetting(Settings::values.bg_red); 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() { ui->use_multi_core->setEnabled(runtime_lock); ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); - ui->use_extended_memory_layout->setEnabled(runtime_lock); - ui->use_extended_memory_layout->setChecked( - Settings::values.use_extended_memory_layout.GetValue()); ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); @@ -79,9 +76,6 @@ void ConfigureGeneral::ResetDefaults() { void ConfigureGeneral::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, use_multi_core); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_extended_memory_layout, - ui->use_extended_memory_layout, - use_extended_memory_layout); if (Settings::IsConfiguringGlobal()) { UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); @@ -141,9 +135,6 @@ void ConfigureGeneral::SetupPerGameUI() { Settings::values.use_speed_limit, use_speed_limit); ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core, use_multi_core); - ConfigurationShared::SetColoredTristate(ui->use_extended_memory_layout, - Settings::values.use_extended_memory_layout, - use_extended_memory_layout); connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() { 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: ConfigurationShared::CheckState use_speed_limit; ConfigurationShared::CheckState use_multi_core; - ConfigurationShared::CheckState use_extended_memory_layout; const Core::System& system; }; 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 @@ </widget> </item> <item> - <widget class="QCheckBox" name="use_extended_memory_layout"> - <property name="text"> - <string>Extended memory layout (8GB DRAM)</string> - </property> - </widget> - </item> - <item> <widget class="QCheckBox" name="toggle_check_exit"> <property name="text"> <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; void ConfigureGraphicsAdvanced::SetConfiguration() { const bool runtime_lock = !system.IsPoweredOn(); ui->use_vsync->setEnabled(runtime_lock); + ui->async_present->setEnabled(runtime_lock); ui->renderer_force_max_clock->setEnabled(runtime_lock); ui->async_astc->setEnabled(runtime_lock); ui->use_asynchronous_shaders->setEnabled(runtime_lock); ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); + ui->async_present->setChecked(Settings::values.async_presentation.GetValue()); ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue()); ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); ui->async_astc->setChecked(Settings::values.async_astc.GetValue()); ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); - ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue()); ui->use_vulkan_driver_pipeline_cache->setChecked( Settings::values.use_vulkan_driver_pipeline_cache.GetValue()); @@ -54,6 +55,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { void ConfigureGraphicsAdvanced::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_presentation, + ui->async_present, async_present); ConfigurationShared::ApplyPerGameSetting(&Settings::values.renderer_force_max_clock, ui->renderer_force_max_clock, renderer_force_max_clock); @@ -67,8 +70,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { use_asynchronous_shaders); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, ui->use_fast_gpu_time, use_fast_gpu_time); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes, - ui->use_pessimistic_flushes, use_pessimistic_flushes); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache, ui->use_vulkan_driver_pipeline_cache, use_vulkan_driver_pipeline_cache); @@ -90,6 +91,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { // Disable if not global (only happens during game) if (Settings::IsConfiguringGlobal()) { ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); + ui->async_present->setEnabled(Settings::values.async_presentation.UsingGlobal()); ui->renderer_force_max_clock->setEnabled( Settings::values.renderer_force_max_clock.UsingGlobal()); ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); @@ -97,8 +99,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { ui->use_asynchronous_shaders->setEnabled( Settings::values.use_asynchronous_shaders.UsingGlobal()); ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); - ui->use_pessimistic_flushes->setEnabled( - Settings::values.use_pessimistic_flushes.UsingGlobal()); ui->use_vulkan_driver_pipeline_cache->setEnabled( Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal()); ui->anisotropic_filtering_combobox->setEnabled( @@ -107,6 +107,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { return; } + ConfigurationShared::SetColoredTristate(ui->async_present, Settings::values.async_presentation, + async_present); ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock, Settings::values.renderer_force_max_clock, renderer_force_max_clock); @@ -118,9 +120,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { use_asynchronous_shaders); ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, Settings::values.use_fast_gpu_time, use_fast_gpu_time); - ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes, - Settings::values.use_pessimistic_flushes, - use_pessimistic_flushes); ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache, Settings::values.use_vulkan_driver_pipeline_cache, 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: std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; + ConfigurationShared::CheckState async_present; ConfigurationShared::CheckState renderer_force_max_clock; ConfigurationShared::CheckState use_vsync; ConfigurationShared::CheckState async_astc; ConfigurationShared::CheckState use_asynchronous_shaders; ConfigurationShared::CheckState use_fast_gpu_time; - ConfigurationShared::CheckState use_pessimistic_flushes; ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; 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 @@ <x>0</x> <y>0</y> <width>404</width> - <height>321</height> + <height>376</height> </rect> </property> <property name="windowTitle"> @@ -70,6 +70,13 @@ </widget> </item> <item> + <widget class="QCheckBox" name="async_present"> + <property name="text"> + <string>Enable asynchronous presentation (Vulkan only)</string> + </property> + </widget> + </item> + <item> <widget class="QCheckBox" name="renderer_force_max_clock"> <property name="toolTip"> <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 @@ <item> <widget class="QCheckBox" name="use_fast_gpu_time"> <property name="toolTip"> - <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string> + <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string> </property> <property name="text"> <string>Use Fast GPU Time (Hack)</string> @@ -120,19 +127,9 @@ </widget> </item> <item> - <widget class="QCheckBox" name="use_pessimistic_flushes"> - <property name="toolTip"> - <string>Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance.</string> - </property> - <property name="text"> - <string>Use pessimistic buffer flushes (Hack)</string> - </property> - </widget> - </item> - <item> <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache"> <property name="toolTip"> - <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> + <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> </property> <property name="text"> <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() { ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); ui->device_name_edit->setText( QString::fromUtf8(Settings::values.device_name.GetValue().c_str())); + ui->use_unsafe_extended_memory_layout->setEnabled(enabled); + ui->use_unsafe_extended_memory_layout->setChecked( + Settings::values.use_unsafe_extended_memory_layout.GetValue()); if (Settings::IsConfiguringGlobal()) { ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); @@ -160,6 +163,9 @@ void ConfigureSystem::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region); ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index, ui->combo_time_zone); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_unsafe_extended_memory_layout, + ui->use_unsafe_extended_memory_layout, + use_unsafe_extended_memory_layout); if (Settings::IsConfiguringGlobal()) { // Guard if during game and set to game-specific value @@ -215,6 +221,10 @@ void ConfigureSystem::SetupPerGameUI() { Settings::values.rng_seed.GetValue().has_value(), Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed); + ConfigurationShared::SetColoredTristate(ui->use_unsafe_extended_memory_layout, + Settings::values.use_unsafe_extended_memory_layout, + use_unsafe_extended_memory_layout); + ui->custom_rtc_checkbox->setVisible(false); ui->custom_rtc_edit->setVisible(false); } 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: bool enabled = false; ConfigurationShared::CheckState use_rng_seed; + ConfigurationShared::CheckState use_unsafe_extended_memory_layout; Core::System& system; }; 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 @@ </property> </widget> </item> + <item row="7" column="0"> + <widget class="QCheckBox" name="use_unsafe_extended_memory_layout"> + <property name="text"> + <string>Unsafe extended memory layout (8GB DRAM)</string> + </property> + </widget> + </item> </layout> </item> </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() { // Core ReadSetting("Core", Settings::values.use_multi_core); - ReadSetting("Core", Settings::values.use_extended_memory_layout); + ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout); // Cpu ReadSetting("Cpu", Settings::values.cpu_accuracy); @@ -300,6 +300,7 @@ void Config::ReadValues() { // Renderer ReadSetting("Renderer", Settings::values.renderer_backend); + ReadSetting("Renderer", Settings::values.async_presentation); ReadSetting("Renderer", Settings::values.renderer_force_max_clock); ReadSetting("Renderer", Settings::values.renderer_debug); ReadSetting("Renderer", Settings::values.renderer_shader_feedback); @@ -326,7 +327,6 @@ void Config::ReadValues() { ReadSetting("Renderer", Settings::values.accelerate_astc); ReadSetting("Renderer", Settings::values.async_astc); ReadSetting("Renderer", Settings::values.use_fast_gpu_time); - ReadSetting("Renderer", Settings::values.use_pessimistic_flushes); ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache); 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 = # 0: Disabled, 1 (default): Enabled use_multi_core = -# Enable extended guest system memory layout (8GB DRAM) +# Enable unsafe extended guest system memory layout (8GB DRAM) # 0 (default): Disabled, 1: Enabled -use_extended_memory_layout = +use_unsafe_extended_memory_layout = [Cpu] # Adjusts various optimizations. @@ -264,6 +264,10 @@ cpuopt_unsafe_ignore_global_monitor = # 0: OpenGL, 1 (default): Vulkan backend = +# Whether to enable asynchronous presentation (Vulkan only) +# 0 (default): Off, 1: On +async_presentation = + # Enable graphics API debugging mode. # 0 (default): Disabled, 1: Enabled debug = @@ -370,10 +374,6 @@ use_asynchronous_gpu_emulation = # 0: Off, 1 (default): On use_fast_gpu_time = -# Force unmodified buffers to be flushed, which can cost performance. -# 0: Off (default), 1: On -use_pessimistic_flushes = - # Whether to use garbage collection or not for GPU caches. # 0 (default): Off, 1: On 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 @@ "overrides": [ { "name": "catch2", - "version": "3.0.1" + "version": "3.3.1" }, { "name": "fmt", |