From 27fc73a74cdb6f0617e2fea694d0195c82fe3498 Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 26 Oct 2026 17:44:59 +0000 Subject: [PATCH] Fix access invoke callbacks relying on replay thread * Some CaptureAccess uses like enumerating GPUs happen before the replay thread is running, in this case we can safely run on the UI thread as we know there will be nothing to conflict with. --- qrenderdoc/Code/Interface/QRDInterface.h | 23 ++------------ qrenderdoc/Code/ReplayManager.cpp | 31 +++++++++++++++++++ qrenderdoc/Code/ReplayManager.h | 3 ++ .../Code/pyrenderdoc/PythonInvokers.cpp | 7 +++++ 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index e0c6d4d79..32d2701d3 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -2107,28 +2107,9 @@ If no tag is present, the callback will be processed as normal. // // this is hidden from swig as for python the threading is less controlled there so we wrap the // ICaptureAccess with a force-blocking invoker - bool AccessInvoke(std::function callback, bool blocking, bool forceReplay, - rdcstr tag) - { - if(!GetCaptureAccess()) - return false; + virtual bool AccessInvoke(std::function callback, bool blocking, + bool forceReplay, rdcstr tag) = 0; - // some long running processes may be better to put on the replay thread even when local, so - // that we can keep the UI thread unblocked - if(CurrentRemote().IsConnected() || forceReplay) - { - if(blocking) - BlockInvoke([this, callback](IReplayController *) { callback(GetCaptureAccess()); }); - else - AsyncInvoke([this, callback](IReplayController *) { callback(GetCaptureAccess()); }, tag); - } - else - { - callback(GetCaptureAccess()); - } - - return true; - } // simple blocking access bool BlockAccessInvoke(std::function callback) { diff --git a/qrenderdoc/Code/ReplayManager.cpp b/qrenderdoc/Code/ReplayManager.cpp index 0a6e30448..388dad9cf 100644 --- a/qrenderdoc/Code/ReplayManager.cpp +++ b/qrenderdoc/Code/ReplayManager.cpp @@ -303,6 +303,37 @@ void ReplayManager::BlockInvoke(ReplayManager::ReplayInvokeCallback m) delete cmd; } +bool ReplayManager::AccessInvoke(std::function callback, bool blocking, + bool forceReplay, rdcstr tag) +{ + if(!GetCaptureAccess()) + return false; + + // some long running processes may be better to put on the replay thread even when local, so + // that we can keep the UI thread unblocked + if(CurrentRemote().IsConnected() || forceReplay) + { + if(!IsRunning()) + { + // if the replay thread isn't running, we won't be able to invoke so just treat this as-if + // it's a local capture and block + callback(GetCaptureAccess()); + return true; + } + + if(blocking) + BlockInvoke([this, callback](IReplayController *) { callback(GetCaptureAccess()); }); + else + AsyncInvoke([this, callback](IReplayController *) { callback(GetCaptureAccess()); }, tag); + } + else + { + callback(GetCaptureAccess()); + } + + return true; +} + void ReplayManager::CancelReplayLoop() { m_Renderer->CancelReplayLoop(); diff --git a/qrenderdoc/Code/ReplayManager.h b/qrenderdoc/Code/ReplayManager.h index d0d780d21..97f9aa023 100644 --- a/qrenderdoc/Code/ReplayManager.h +++ b/qrenderdoc/Code/ReplayManager.h @@ -68,6 +68,9 @@ public: using IReplayManager::AsyncInvoke; void BlockInvoke(ReplayInvokeCallback m); + bool AccessInvoke(std::function callback, bool blocking, bool forceReplay, + rdcstr tag); + void CancelReplayLoop(); void CloseThread(); diff --git a/qrenderdoc/Code/pyrenderdoc/PythonInvokers.cpp b/qrenderdoc/Code/pyrenderdoc/PythonInvokers.cpp index a965c2f6e..6d015d405 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonInvokers.cpp +++ b/qrenderdoc/Code/pyrenderdoc/PythonInvokers.cpp @@ -1027,6 +1027,13 @@ struct ReplayInvoker : IReplayManager } void BlockInvoke(ReplayInvokeCallback method) { return m_Replay.BlockInvoke(method); } + + // won't be available to python but we implement this for compile time matching + bool AccessInvoke(std::function callback, bool blocking, bool forceReplay, + rdcstr tag) + { + return m_Replay.AccessInvoke(callback, blocking, forceReplay, tag); + } }; struct IMainWindowInvoker : UIThreadInvoker