From 8855290cfa4c760eba438cdb696bd2fbe9d2908a Mon Sep 17 00:00:00 2001 From: Bruno <56844494+fab918@users.noreply.github.com> Date: Sun, 27 Sep 2026 13:22:05 +0200 Subject: [PATCH] Fix SMO synchronization (#300) --- lang/english/css/main_window.css | 4 +- lang/english/html/main_window.html | 2 +- lang/english/html/system_config_graphics.html | 4 +- src/nxemu-video/video_settings.cpp | 14 ++-- src/nxemu-video/video_settings.h | 4 +- src/nxemu-video/video_settings_identifiers.h | 2 +- .../user_interface/sciter_main_window.cpp | 24 +++--- .../settings/system_config_graphics.cpp | 2 +- src/yuzu_video_core/dma_pusher.cpp | 84 ++++++++++++++++++- src/yuzu_video_core/dma_pusher.h | 12 +++ src/yuzu_video_core/fence_manager.h | 9 ++ src/yuzu_video_core/rasterizer_interface.h | 3 + .../renderer_null/null_rasterizer.cpp | 3 + .../renderer_null/null_rasterizer.h | 1 + .../renderer_opengl/gl_rasterizer.cpp | 4 + .../renderer_opengl/gl_rasterizer.h | 1 + .../renderer_vulkan/vk_rasterizer.cpp | 4 + .../renderer_vulkan/vk_rasterizer.h | 1 + 18 files changed, 148 insertions(+), 30 deletions(-) diff --git a/lang/english/css/main_window.css b/lang/english/css/main_window.css index 3c2f60be..8c540a01 100644 --- a/lang/english/css/main_window.css +++ b/lang/english/css/main_window.css @@ -36,7 +36,7 @@ #StatusBar #volume, #StatusBar #renderer, #StatusBar #dockedMode, -#StatusBar #syncMemoryOperations, +#StatusBar #gpuCommandSynchronization, #StatusBar #volumePopupBtn { border: 1px solid transparent; @@ -48,7 +48,7 @@ #StatusBar #volume:hover, #StatusBar #renderer:hover, #StatusBar #dockedMode:hover, -#StatusBar #syncMemoryOperations:hover, +#StatusBar #gpuCommandSynchronization:hover, #StatusBar #volumePopupBtn:hover { border: 1px solid color(color-statusbar-hover-border); diff --git a/lang/english/html/main_window.html b/lang/english/html/main_window.html index 1df278c0..cb83d814 100644 --- a/lang/english/html/main_window.html +++ b/lang/english/html/main_window.html @@ -43,7 +43,7 @@
- + diff --git a/lang/english/html/system_config_graphics.html b/lang/english/html/system_config_graphics.html index 1e192316..bd8e347b 100644 --- a/lang/english/html/system_config_graphics.html +++ b/lang/english/html/system_config_graphics.html @@ -71,8 +71,8 @@
-
-
+
+
diff --git a/src/nxemu-video/video_settings.cpp b/src/nxemu-video/video_settings.cpp index 7427017e..52f94769 100644 --- a/src/nxemu-video/video_settings.cpp +++ b/src/nxemu-video/video_settings.cpp @@ -16,7 +16,7 @@ namespace { // Configuration storage stays on the settings thread; the GPU reads these snapshots. std::atomic runtime_dma_accuracy{0}; -std::atomic runtime_sync_memory_operations{true}; +std::atomic runtime_gpu_command_synchronization{true}; } namespace Settings @@ -54,9 +54,9 @@ bool UseSafeDMAReads() return accuracy == 0 ? IsGPULevelHigh() : accuracy == 2; } -bool IsSyncMemoryOperationsEnabled() +bool IsGpuCommandSynchronizationEnabled() { - return runtime_sync_memory_operations.load(std::memory_order_relaxed); + return runtime_gpu_command_synchronization.load(std::memory_order_relaxed); } void TranslateResolutionInfo(ResolutionSetup setup, ResolutionScalingInfo & info) @@ -243,7 +243,7 @@ static VideoSetting settings[] = { {NXVideoSetting::AnisotropicFiltering, "video", "anisotropic_filtering", &videoSettings.max_anisotropy, AnisotropyMode::Automatic}, #endif {NXVideoSetting::DMAAccuracy, "video", "dma_accuracy", &videoSettings.dma_accuracy, 0, 0, 2}, - {NXVideoSetting::SyncMemoryOperations, "video", "sync_memory_operations", &videoSettings.sync_memory_operations, true}, + {NXVideoSetting::GpuCommandSynchronization, "video", "gpu_command_synchronization", &videoSettings.gpu_command_synchronization, true}, {NXVideoSetting::VSyncMode, "video", "vsync_mode", &videoSettings.vsync_mode, VSyncMode::Fifo}, {NXVideoSetting::NvdecEmulation, "video", "nvdec_emulation", &videoSettings.nvdec_emulation, NvdecEmulation::Gpu}, #ifdef _WIN32 @@ -345,9 +345,9 @@ void VideoSettingChanged(const char * setting, void * /*userData*/) runtime_dma_accuracy.store(videoSettings.dma_accuracy, std::memory_order_relaxed); return; } - if (strcmp(setting, NXVideoSetting::SyncMemoryOperations) == 0) + if (strcmp(setting, NXVideoSetting::GpuCommandSynchronization) == 0) { - runtime_sync_memory_operations.store(videoSettings.sync_memory_operations, std::memory_order_relaxed); + runtime_gpu_command_synchronization.store(videoSettings.gpu_command_synchronization, std::memory_order_relaxed); return; } } @@ -618,7 +618,7 @@ void SetupVideoSetting(void) g_settings->RegisterCallback(videoSetting.identifier, VideoSettingChanged, nullptr); } runtime_dma_accuracy.store(videoSettings.dma_accuracy, std::memory_order_relaxed); - runtime_sync_memory_operations.store(videoSettings.sync_memory_operations, std::memory_order_relaxed); + runtime_gpu_command_synchronization.store(videoSettings.gpu_command_synchronization, std::memory_order_relaxed); Settings::UpdateRescalingInfo(); Settings::UpdateGPUAccuracy(); } diff --git a/src/nxemu-video/video_settings.h b/src/nxemu-video/video_settings.h index 7d5ccc81..91f538a4 100644 --- a/src/nxemu-video/video_settings.h +++ b/src/nxemu-video/video_settings.h @@ -41,7 +41,7 @@ bool IsGPULevelHigh(); bool IsDMALevelDefault(); bool IsDMALevelSafe(); bool UseSafeDMAReads(); -bool IsSyncMemoryOperationsEnabled(); +bool IsGpuCommandSynchronizationEnabled(); void TranslateResolutionInfo(ResolutionSetup setup, ResolutionScalingInfo & info); void UpdateRescalingInfo(); @@ -80,7 +80,7 @@ struct VideoSettings AnisotropyMode max_anisotropy; bool async_presentation; bool use_reactive_flushing; - bool sync_memory_operations; + bool gpu_command_synchronization; bool use_asynchronous_shaders; bool use_fast_gpu_time; diff --git a/src/nxemu-video/video_settings_identifiers.h b/src/nxemu-video/video_settings_identifiers.h index 32668e27..40fe5f9c 100644 --- a/src/nxemu-video/video_settings_identifiers.h +++ b/src/nxemu-video/video_settings_identifiers.h @@ -19,7 +19,7 @@ namespace NXVideoSetting constexpr const char * EnableAsynchronousPresentation = "nxvideo:EnableAsynchronousPresentation"; constexpr const char * ForceMaximumClocks = "nxvideo:ForceMaximumClocks"; constexpr const char * EnableReactiveFlushing = "nxvideo:EnableReactiveFlushing"; - constexpr const char * SyncMemoryOperations = "nxvideo:SyncMemoryOperations"; + constexpr const char * GpuCommandSynchronization = "nxvideo:GpuCommandSynchronization"; constexpr const char * UseAsynchronousShaderBuilding = "nxvideo:UseAsynchronousShaderBuilding"; constexpr const char * FastGPUTime = "nxvideo:FastGPUTime"; constexpr const char * UseVulkanPipelineCache = "nxvideo:UseVulkanPipelineCache"; diff --git a/src/nxemu/user_interface/sciter_main_window.cpp b/src/nxemu/user_interface/sciter_main_window.cpp index 1e809fc4..8cfa383c 100644 --- a/src/nxemu/user_interface/sciter_main_window.cpp +++ b/src/nxemu/user_interface/sciter_main_window.cpp @@ -287,7 +287,7 @@ SciterMainWindow::SciterMainWindow(ISciterUI & sciterUI, const char * windowTitl settings.RegisterCallback(NXOsSetting::DockedMode, SciterMainWindow::SettingChanged, this); settings.RegisterCallback(NXUISetting::Hotkeys, SciterMainWindow::HotKeysChanged, this); settings.RegisterCallback(NXVideoSetting::ResolutionUpFactor, SciterMainWindow::SettingChanged, this); - settings.RegisterCallback(NXVideoSetting::SyncMemoryOperations, SciterMainWindow::SettingChanged, this); + settings.RegisterCallback(NXVideoSetting::GpuCommandSynchronization, SciterMainWindow::SettingChanged, this); settings.RegisterCallback(NXUISetting::HideMouseOnInactivity, SciterMainWindow::SettingChanged, this); settings.RegisterCallback(NXUISetting::EnableDiscordPresence, SciterMainWindow::SettingChanged, this); @@ -384,7 +384,7 @@ SciterMainWindow::~SciterMainWindow() settings.UnregisterCallback(NXOsSetting::DockedMode, SciterMainWindow::SettingChanged, this); settings.UnregisterCallback(NXUISetting::Hotkeys, SciterMainWindow::HotKeysChanged, this); settings.UnregisterCallback(NXVideoSetting::ResolutionUpFactor, SciterMainWindow::SettingChanged, this); - settings.UnregisterCallback(NXVideoSetting::SyncMemoryOperations, SciterMainWindow::SettingChanged, this); + settings.UnregisterCallback(NXVideoSetting::GpuCommandSynchronization, SciterMainWindow::SettingChanged, this); settings.UnregisterCallback(NXUISetting::HideMouseOnInactivity, SciterMainWindow::SettingChanged, this); settings.UnregisterCallback(NXUISetting::EnableDiscordPresence, SciterMainWindow::SettingChanged, this); @@ -656,7 +656,7 @@ bool SciterMainWindow::Show() m_sciterUI.AttachHandler(m_rootElement.GetElementByID("dockedMode"), IID_ICLICKSINK, (IClickSink *)this); m_sciterUI.AttachHandler(m_rootElement.GetElementByID("renderer"), IID_ICLICKSINK, (IClickSink *)this); - m_sciterUI.AttachHandler(m_rootElement.GetElementByID("syncMemoryOperations"), IID_ICLICKSINK, (IClickSink *)this); + m_sciterUI.AttachHandler(m_rootElement.GetElementByID("gpuCommandSynchronization"), IID_ICLICKSINK, (IClickSink *)this); m_sciterUI.AttachHandler(m_rootElement.GetElementByID("volume"), IID_ICLICKSINK, (IClickSink *)this); m_sciterUI.AttachHandler(m_rootElement.GetElementByID("volumePopupBtn"), IID_ICLICKSINK, (IClickSink *)this); m_sciterUI.AttachHandler(m_rootElement.GetElementByID("audioVolume"), IID_ISTATECHANGESINK, (IStateChangeSink *)this); @@ -847,12 +847,12 @@ void SciterMainWindow::UpdateStatusWidgets() renderer.SetHTML((const uint8_t *)text.c_str(), text.size()); } - SciterElement syncMemoryOperations(m_rootElement.GetElementByID("syncMemoryOperations")); - if (syncMemoryOperations.IsValid()) + SciterElement gpuCommandSynchronization(m_rootElement.GetElementByID("gpuCommandSynchronization")); + if (gpuCommandSynchronization.IsValid()) { - const bool enabled = settings.GetBool(NXVideoSetting::SyncMemoryOperations); - stdstr_f text("SMO: %s", enabled ? "ON" : "OFF"); - syncMemoryOperations.SetHTML((const uint8_t *)text.c_str(), text.size()); + const bool enabled = settings.GetBool(NXVideoSetting::GpuCommandSynchronization); + stdstr_f text("GPU Sync: %s", enabled ? "ON" : "OFF"); + gpuCommandSynchronization.SetHTML((const uint8_t *)text.c_str(), text.size()); } SciterElement volume(m_rootElement.GetElementByID("volume")); @@ -2400,11 +2400,11 @@ bool SciterMainWindow::OnClick(SCITER_ELEMENT element, SCITER_ELEMENT source, ui m_modules.FlushSettings(); } } - else if (source == rootElement.GetElementByID("syncMemoryOperations")) + else if (source == rootElement.GetElementByID("gpuCommandSynchronization")) { SettingsStore & settings = SettingsStore::GetInstance(); - const bool enabled = settings.GetBool(NXVideoSetting::SyncMemoryOperations); - settings.SetBool(NXVideoSetting::SyncMemoryOperations, !enabled); + const bool enabled = settings.GetBool(NXVideoSetting::GpuCommandSynchronization); + settings.SetBool(NXVideoSetting::GpuCommandSynchronization, !enabled); UpdateStatusWidgets(); } else if (source == rootElement.GetElementByID("volume")) @@ -2464,7 +2464,7 @@ void SciterMainWindow::SettingChanged(const char * setting, void * userData) { impl->m_resolutionUpFactor = SettingsStore::GetInstance().GetFloat(NXVideoSetting::ResolutionUpFactor); } - else if (strcmp(setting, NXVideoSetting::SyncMemoryOperations) == 0) + else if (strcmp(setting, NXVideoSetting::GpuCommandSynchronization) == 0) { impl->UpdateStatusWidgets(); } diff --git a/src/nxemu/user_interface/settings/system_config_graphics.cpp b/src/nxemu/user_interface/settings/system_config_graphics.cpp index 96f91536..81036b35 100644 --- a/src/nxemu/user_interface/settings/system_config_graphics.cpp +++ b/src/nxemu/user_interface/settings/system_config_graphics.cpp @@ -28,7 +28,7 @@ namespace static ConfigSetting advancedSettings[] = { ConfigSetting(ConfigSetting::ComboBox, "AccuracyLevel", true, SystemConfig::TranslationType::GpuAccuracy, NXVideoSetting::AccuracyLevel), ConfigSetting(ConfigSetting::ComboBox, "DMAAccuracy", true, SystemConfig::TranslationType::DmaAccuracy, NXVideoSetting::DMAAccuracy), - ConfigSetting(ConfigSetting::CheckBox, "SyncMemoryOperations", true, NXVideoSetting::SyncMemoryOperations), + ConfigSetting(ConfigSetting::CheckBox, "GpuCommandSynchronization", true, NXVideoSetting::GpuCommandSynchronization), ConfigSetting(ConfigSetting::ComboBox, "AnisotropicFiltering", false, SystemConfig::TranslationType::AnisotropyMode, NXVideoSetting::AnisotropicFiltering), ConfigSetting(ConfigSetting::ComboBox, "ASTCRecompressionMethod", false, SystemConfig::TranslationType::AstcRecompression, NXVideoSetting::ASTCRecompressionMethod), ConfigSetting(ConfigSetting::ComboBox, "VRAMUsageMode", false, SystemConfig::TranslationType::VramUsageMode, NXVideoSetting::VRAMUsageMode), diff --git a/src/yuzu_video_core/dma_pusher.cpp b/src/yuzu_video_core/dma_pusher.cpp index f9161517..d20d5bea 100644 --- a/src/yuzu_video_core/dma_pusher.cpp +++ b/src/yuzu_video_core/dma_pusher.cpp @@ -41,6 +41,16 @@ void DmaPusher::DispatchCalls() break; } } + + if (memory_operation_sync_state == MemoryOperationSyncState::Armed) + { + WaitForArmedMemoryOperationSync(); + } + else + { + memory_operation_sync_state = MemoryOperationSyncState::None; + } + gpu.FlushCommands(); gpu.OnCommandListEnd(); } @@ -75,9 +85,27 @@ bool DmaPusher::Step() dma_state.dma_get = header.Address(); // SYNC_WAIT applies to the current GP entry, including zero-length entries. - if (header.sync && Settings::IsSyncMemoryOperationsEnabled()) + if (header.sync) { - SynchronizeMemoryOperations(); + switch (memory_operation_sync_state) + { + case MemoryOperationSyncState::Skip: + memory_operation_sync_state = MemoryOperationSyncState::None; + break; + case MemoryOperationSyncState::Armed: + WaitForArmedMemoryOperationSync(); + break; + case MemoryOperationSyncState::None: + if (Settings::IsGpuCommandSynchronizationEnabled()) + { + SynchronizeMemoryOperations(); + } + break; + } + } + else + { + ASSERT(memory_operation_sync_state == MemoryOperationSyncState::None); } if (header.size > 0 && Settings::IsGPULevelHigh() && @@ -104,6 +132,26 @@ bool DmaPusher::Step() } } + // Prepare a following SYNC_WAIT at the current GP-entry boundary. Clean pushbuffers can be + // fetched directly because DmaPusher already processes GP entries in order. + const std::size_t next_entry_index = dma_pushbuffer_subindex + 1; + if (next_entry_index < command_list_size && Settings::IsGpuCommandSynchronizationEnabled()) + { + const CommandListHeader & next_header = command_list.command_lists[next_entry_index]; + if (next_header.sync) + { + ASSERT(memory_operation_sync_state == MemoryOperationSyncState::None); + if (CanSkipCleanMemoryOperationSync(next_header)) + { + memory_operation_sync_state = MemoryOperationSyncState::Skip; + } + else + { + ArmMemoryOperationSync(); + } + } + } + if (++dma_pushbuffer_subindex >= command_list_size) { dma_pushbuffer.pop(); @@ -119,6 +167,38 @@ void DmaPusher::SynchronizeMemoryOperations() rasterizer->WaitForFence(); } +void DmaPusher::ArmMemoryOperationSync() +{ + ASSERT(rasterizer != nullptr); + ASSERT(memory_operation_sync_state == MemoryOperationSyncState::None); + + memory_operation_sync_ready.store(false, std::memory_order_relaxed); + memory_operation_sync_state = MemoryOperationSyncState::Armed; + rasterizer->SignalMemoryOperationFence([this]() { + memory_operation_sync_ready.store(true, std::memory_order_release); + memory_operation_sync_ready.notify_one(); + }); +} + +void DmaPusher::WaitForArmedMemoryOperationSync() +{ + ASSERT(memory_operation_sync_state == MemoryOperationSyncState::Armed); + + memory_operation_sync_ready.wait(false, std::memory_order_acquire); + memory_operation_sync_state = MemoryOperationSyncState::None; +} + +bool DmaPusher::CanSkipCleanMemoryOperationSync(const CommandListHeader & header) const +{ + // The actual fetch must still resolve writes that can appear after this look-ahead probe. + if (!Settings::UseSafeDMAReads() || header.size == 0 || header.conditional_fetch) + { + return false; + } + + return !memory_manager.IsMemoryDirty(header.Address(), header.size * sizeof(u32)); +} + void DmaPusher::ProcessCommands(std::span commands) { for (std::size_t index = 0; index < commands.size();) diff --git a/src/yuzu_video_core/dma_pusher.h b/src/yuzu_video_core/dma_pusher.h index ade271de..e6594a77 100644 --- a/src/yuzu_video_core/dma_pusher.h +++ b/src/yuzu_video_core/dma_pusher.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -147,8 +148,17 @@ public: private: static constexpr u32 non_puller_methods = 0x40; static constexpr u32 max_subchannels = 8; + enum class MemoryOperationSyncState : u8 { + None, + Skip, + Armed, + }; + bool Step(); void SynchronizeMemoryOperations(); + void ArmMemoryOperationSync(); + void WaitForArmedMemoryOperationSync(); + bool CanSkipCleanMemoryOperationSync(const CommandListHeader& header) const; void ProcessCommands(std::span commands); void SetState(const CommandHeader& command_header); @@ -175,6 +185,8 @@ private: DmaState dma_state{}; bool dma_increment_once{}; + MemoryOperationSyncState memory_operation_sync_state{MemoryOperationSyncState::None}; + std::atomic memory_operation_sync_ready{}; const bool ib_enable{true}; ///< IB mode enabled diff --git a/src/yuzu_video_core/fence_manager.h b/src/yuzu_video_core/fence_manager.h index 055cf9dc..d95bfa94 100644 --- a/src/yuzu_video_core/fence_manager.h +++ b/src/yuzu_video_core/fence_manager.h @@ -76,6 +76,15 @@ public: (void)SubmitFence(std::move(func), force_delay, false, nullptr); } + void SignalMemoryOperationFence(std::function&& func) { + const bool completed_inline = SubmitFence(std::move(func), true, true, nullptr); + if constexpr (!can_async_check) { + if (!completed_inline) { + TryReleasePendingFences(); + } + } + } + void SignalSyncPoint(u32 value) { syncpoint_manager.IncrementGuest(value); std::function func([this, value] { syncpoint_manager.IncrementHost(value); }); diff --git a/src/yuzu_video_core/rasterizer_interface.h b/src/yuzu_video_core/rasterizer_interface.h index d5448d2d..b74519a2 100644 --- a/src/yuzu_video_core/rasterizer_interface.h +++ b/src/yuzu_video_core/rasterizer_interface.h @@ -68,6 +68,9 @@ public: /// Signal a GPU based semaphore as a fence virtual void SignalFence(std::function&& func) = 0; + /// Arm an explicit memory-operation synchronization fence without blocking the DMA pusher. + virtual void SignalMemoryOperationFence(std::function&& func) = 0; + /// Signal a fence and wait for its deferred synchronization operations to complete. virtual void WaitForFence() = 0; diff --git a/src/yuzu_video_core/renderer_null/null_rasterizer.cpp b/src/yuzu_video_core/renderer_null/null_rasterizer.cpp index 6ac639ba..4c1da9e3 100644 --- a/src/yuzu_video_core/renderer_null/null_rasterizer.cpp +++ b/src/yuzu_video_core/renderer_null/null_rasterizer.cpp @@ -66,6 +66,9 @@ void RasterizerNull::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {} void RasterizerNull::SignalFence(std::function&& func) { func(); } +void RasterizerNull::SignalMemoryOperationFence(std::function&& func) { + func(); +} void RasterizerNull::WaitForFence() {} void RasterizerNull::SyncOperation(std::function&& func) { func(); diff --git a/src/yuzu_video_core/renderer_null/null_rasterizer.h b/src/yuzu_video_core/renderer_null/null_rasterizer.h index 67c250b5..492c3e1d 100644 --- a/src/yuzu_video_core/renderer_null/null_rasterizer.h +++ b/src/yuzu_video_core/renderer_null/null_rasterizer.h @@ -60,6 +60,7 @@ public: void UnmapMemory(DAddr addr, u64 size) override; void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; void SignalFence(std::function&& func) override; + void SignalMemoryOperationFence(std::function&& func) override; void WaitForFence() override; void SyncOperation(std::function&& func) override; void SignalSyncPoint(u32 value) override; diff --git a/src/yuzu_video_core/renderer_opengl/gl_rasterizer.cpp b/src/yuzu_video_core/renderer_opengl/gl_rasterizer.cpp index 5740df9a..889c805f 100644 --- a/src/yuzu_video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/yuzu_video_core/renderer_opengl/gl_rasterizer.cpp @@ -615,6 +615,10 @@ void RasterizerOpenGL::SignalFence(std::function&& func) { fence_manager.SignalFence(std::move(func)); } +void RasterizerOpenGL::SignalMemoryOperationFence(std::function&& func) { + fence_manager.SignalMemoryOperationFence(std::move(func)); +} + void RasterizerOpenGL::WaitForFence() { fence_manager.WaitForFence(); } diff --git a/src/yuzu_video_core/renderer_opengl/gl_rasterizer.h b/src/yuzu_video_core/renderer_opengl/gl_rasterizer.h index 0865c100..440d20c9 100644 --- a/src/yuzu_video_core/renderer_opengl/gl_rasterizer.h +++ b/src/yuzu_video_core/renderer_opengl/gl_rasterizer.h @@ -105,6 +105,7 @@ public: void UnmapMemory(DAddr addr, u64 size) override; void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; void SignalFence(std::function&& func) override; + void SignalMemoryOperationFence(std::function&& func) override; void WaitForFence() override; void SyncOperation(std::function&& func) override; void SignalSyncPoint(u32 value) override; diff --git a/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.cpp b/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.cpp index d46a7066..318d10b6 100644 --- a/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.cpp @@ -685,6 +685,10 @@ void RasterizerVulkan::SignalFence(std::function&& func) { fence_manager.SignalFence(std::move(func)); } +void RasterizerVulkan::SignalMemoryOperationFence(std::function&& func) { + fence_manager.SignalMemoryOperationFence(std::move(func)); +} + void RasterizerVulkan::WaitForFence() { fence_manager.WaitForFence(); } diff --git a/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.h b/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.h index 79fffe06..b9ba2355 100644 --- a/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/yuzu_video_core/renderer_vulkan/vk_rasterizer.h @@ -107,6 +107,7 @@ public: void UnmapMemory(DAddr addr, u64 size) override; void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; void SignalFence(std::function&& func) override; + void SignalMemoryOperationFence(std::function&& func) override; void WaitForFence() override; void SyncOperation(std::function&& func) override; void SignalSyncPoint(u32 value) override;