From 20be9f3d52adcfcd4b1bec9aa2b1be201583036b Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 26 Aug 2019 19:09:50 +0100 Subject: [PATCH] Add ReplayOptions struct to contain replay-time configuration * Not currently exposed to the UI or used by the drivers, we just pass the default object through --- qrenderdoc/Code/CaptureContext.cpp | 17 +++--- qrenderdoc/Code/CaptureContext.h | 8 +-- qrenderdoc/Code/Interface/QRDInterface.h | 5 +- qrenderdoc/Code/ReplayManager.cpp | 13 ++-- qrenderdoc/Code/ReplayManager.h | 6 +- qrenderdoc/Windows/Dialogs/LiveCapture.cpp | 2 +- qrenderdoc/Windows/MainWindow.cpp | 11 ++-- qrenderdoc/Windows/MainWindow.h | 2 +- qrenderdoc/Windows/PythonShell.cpp | 6 +- renderdoc/android/android.cpp | 5 +- renderdoc/api/replay/control_types.h | 71 ++++++++++++++++++++++ renderdoc/api/replay/renderdoc_replay.h | 8 ++- renderdoc/api/replay/renderdoc_tostr.inl | 13 ++++ renderdoc/api/replay/replay_enums.h | 35 +++++++++++ renderdoc/core/core.cpp | 37 ++++++++--- renderdoc/core/core.h | 11 ++-- renderdoc/core/remote_server.cpp | 12 +++- renderdoc/core/remote_server.h | 1 + renderdoc/driver/d3d11/d3d11_replay.cpp | 2 +- renderdoc/driver/d3d12/d3d12_replay.cpp | 2 +- renderdoc/driver/gl/gl_replay.cpp | 19 +++--- renderdoc/driver/vulkan/vk_replay.cpp | 2 +- renderdoc/replay/capture_file.cpp | 10 ++- renderdoc/replay/renderdoc_serialise.inl | 13 ++++ renderdoc/replay/replay_controller.cpp | 4 +- renderdoc/replay/replay_controller.h | 2 +- renderdoccmd/renderdoccmd.cpp | 4 +- 27 files changed, 250 insertions(+), 71 deletions(-) diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index bd375c8da..d79571275 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -681,8 +681,8 @@ void CaptureContext::CleanMenu(QAction *action) delete menu; } -void CaptureContext::LoadCapture(const rdcstr &captureFile, const rdcstr &origFilename, - bool temporary, bool local) +void CaptureContext::LoadCapture(const rdcstr &captureFile, const ReplayOptions &opts, + const rdcstr &origFilename, bool temporary, bool local) { CloseCapture(); @@ -696,9 +696,10 @@ void CaptureContext::LoadCapture(const rdcstr &captureFile, const rdcstr &origFi bool newCapture = (!temporary && !Config().RecentCaptureFiles.contains(origFilename)); - LambdaThread *thread = new LambdaThread([this, captureFile, origFilename, temporary, local]() { - LoadCaptureThreaded(captureFile, origFilename, temporary, local); - }); + LambdaThread *thread = + new LambdaThread([this, captureFile, opts, origFilename, temporary, local]() { + LoadCaptureThreaded(captureFile, opts, origFilename, temporary, local); + }); thread->selfDelete(true); thread->start(); @@ -775,8 +776,8 @@ float CaptureContext::UpdateLoadProgress() return val; } -void CaptureContext::LoadCaptureThreaded(const QString &captureFile, const QString &origFilename, - bool temporary, bool local) +void CaptureContext::LoadCaptureThreaded(const QString &captureFile, const ReplayOptions &opts, + const QString &origFilename, bool temporary, bool local) { m_CaptureFile = origFilename; @@ -788,7 +789,7 @@ void CaptureContext::LoadCaptureThreaded(const QString &captureFile, const QStri m_PostloadProgress = 0.0f; // this function call will block until the capture is either loaded, or there's some failure - m_Replay.OpenCapture(captureFile, [this](float p) { m_LoadProgress = p; }); + m_Replay.OpenCapture(captureFile, opts, [this](float p) { m_LoadProgress = p; }); // if the renderer isn't running, we hit a failure case so display an error message if(!m_Replay.IsRunning()) diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index f95b56185..680814fc7 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -102,8 +102,8 @@ public: ////////////////////////////////////////////////////////////////////////////// // Control functions - void LoadCapture(const rdcstr &captureFile, const rdcstr &origFilename, bool temporary, - bool local) override; + void LoadCapture(const rdcstr &captureFile, const ReplayOptions &opts, const rdcstr &origFilename, + bool temporary, bool local) override; bool SaveCaptureTo(const rdcstr &captureFile) override; void RecompressCapture() override; void CloseCapture() override; @@ -305,8 +305,8 @@ private: float m_PostloadProgress = 0.0f; float UpdateLoadProgress(); - void LoadCaptureThreaded(const QString &captureFile, const QString &origFilename, bool temporary, - bool local); + void LoadCaptureThreaded(const QString &captureFile, const ReplayOptions &opts, + const QString &origFilename, bool temporary, bool local); void AddSortedMenuItem(QMenu *menu, bool rootMenu, const rdcarray &items, std::function callback); diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index 3e601e761..b8c040dba 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -1083,13 +1083,14 @@ data. DOCUMENT(R"(Open a capture file for replay. :param str captureFile: The actual path to the capture file. +:param ReplayOptions opts: The options controlling how the capture should be replayed. :param str origFilename: The original filename, if the capture was copied remotely for replay. :param bool temporary: ``True`` if this is a temporary capture which should prompt the user for either save or delete on close. :param bool local: ``True`` if ``captureFile`` refers to a file on the local machine. )"); - virtual void LoadCapture(const rdcstr &captureFile, const rdcstr &origFilename, bool temporary, - bool local) = 0; + virtual void LoadCapture(const rdcstr &captureFile, const ReplayOptions &opts, + const rdcstr &origFilename, bool temporary, bool local) = 0; DOCUMENT(R"(Saves the current capture file to a given path. diff --git a/qrenderdoc/Code/ReplayManager.cpp b/qrenderdoc/Code/ReplayManager.cpp index 59ee959a3..b7637e12d 100644 --- a/qrenderdoc/Code/ReplayManager.cpp +++ b/qrenderdoc/Code/ReplayManager.cpp @@ -42,7 +42,8 @@ ReplayManager::~ReplayManager() RENDERDOC_UnregisterMemoryRegion(this); } -void ReplayManager::OpenCapture(const QString &capturefile, RENDERDOC_ProgressCallback progress) +void ReplayManager::OpenCapture(const QString &capturefile, const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress) { if(m_Running) return; @@ -50,8 +51,8 @@ void ReplayManager::OpenCapture(const QString &capturefile, RENDERDOC_ProgressCa // TODO maybe we could expose this choice to the user? int proxyRenderer = -1; - m_Thread = new LambdaThread([this, proxyRenderer, capturefile, progress]() { - run(proxyRenderer, capturefile, progress); + m_Thread = new LambdaThread([this, proxyRenderer, capturefile, opts, progress]() { + run(proxyRenderer, capturefile, opts, progress); }); m_Thread->start(QThread::HighestPriority); @@ -416,7 +417,7 @@ void ReplayManager::PushInvoke(ReplayManager::InvokeHandle *cmd) m_RenderCondition.wakeAll(); } -void ReplayManager::run(int proxyRenderer, const QString &capturefile, +void ReplayManager::run(int proxyRenderer, const QString &capturefile, const ReplayOptions &opts, RENDERDOC_ProgressCallback progress) { m_Renderer = NULL; @@ -424,7 +425,7 @@ void ReplayManager::run(int proxyRenderer, const QString &capturefile, if(m_Remote) { rdctie(m_CreateStatus, m_Renderer) = - m_Remote->OpenCapture(proxyRenderer, capturefile.toUtf8().data(), progress); + m_Remote->OpenCapture(proxyRenderer, capturefile.toUtf8().data(), opts, progress); } else { @@ -433,7 +434,7 @@ void ReplayManager::run(int proxyRenderer, const QString &capturefile, m_CreateStatus = m_CaptureFile->OpenFile(capturefile.toUtf8().data(), "rdc", NULL); if(m_CreateStatus == ReplayStatus::Succeeded) - rdctie(m_CreateStatus, m_Renderer) = m_CaptureFile->OpenCapture(progress); + rdctie(m_CreateStatus, m_Renderer) = m_CaptureFile->OpenCapture(opts, progress); } if(m_Renderer == NULL) diff --git a/qrenderdoc/Code/ReplayManager.h b/qrenderdoc/Code/ReplayManager.h index c4fc2fec8..e5e283cb6 100644 --- a/qrenderdoc/Code/ReplayManager.h +++ b/qrenderdoc/Code/ReplayManager.h @@ -48,7 +48,8 @@ public: ReplayManager(); ~ReplayManager(); - void OpenCapture(const QString &capturefile, RENDERDOC_ProgressCallback progress); + void OpenCapture(const QString &capturefile, const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress); void DeleteCapture(const rdcstr &capturefile, bool local); bool IsRunning(); @@ -113,7 +114,8 @@ private: bool selfdelete; }; - void run(int proxyRenderer, const QString &capturefile, RENDERDOC_ProgressCallback progress); + void run(int proxyRenderer, const QString &capturefile, const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress); QMutex m_TimerLock; QElapsedTimer m_CommandTimer; diff --git a/qrenderdoc/Windows/Dialogs/LiveCapture.cpp b/qrenderdoc/Windows/Dialogs/LiveCapture.cpp index d3ac59f89..010eb8627 100644 --- a/qrenderdoc/Windows/Dialogs/LiveCapture.cpp +++ b/qrenderdoc/Windows/Dialogs/LiveCapture.cpp @@ -760,7 +760,7 @@ void LiveCapture::openCapture(Capture *cap) return; } - m_Main->LoadCapture(cap->path, !cap->saved, cap->local); + m_Main->LoadCapture(cap->path, ReplayOptions(), !cap->saved, cap->local); } bool LiveCapture::saveCapture(Capture *cap, QString path) diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index daf197823..10ac5c884 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -487,7 +487,7 @@ void MainWindow::LoadFromFilename(const QString &filename, bool temporary) if(ext == lit("rdc")) { - LoadCapture(filename, temporary, true); + LoadCapture(filename, ReplayOptions(), temporary, true); } else if(ext == lit("cap")) { @@ -500,7 +500,7 @@ void MainWindow::LoadFromFilename(const QString &filename, bool temporary) else { // not a recognised filetype, see if we can load it anyway - LoadCapture(filename, temporary, true); + LoadCapture(filename, ReplayOptions(), temporary, true); } } @@ -633,7 +633,8 @@ void MainWindow::OnInjectTrigger(uint32_t PID, const rdcarraydeleteLater(); } -void MainWindow::LoadCapture(const QString &filename, bool temporary, bool local) +void MainWindow::LoadCapture(const QString &filename, const ReplayOptions &opts, bool temporary, + bool local) { if(PromptCloseCapture()) { @@ -800,7 +801,7 @@ void MainWindow::LoadCapture(const QString &filename, bool temporary, bool local ANALYTIC_SET(UIFeatures.ImageViewer, true); } - m_Ctx.LoadCapture(fileToLoad, origFilename, temporary, local); + m_Ctx.LoadCapture(fileToLoad, opts, origFilename, temporary, local); } if(local && !temporary) @@ -1506,7 +1507,7 @@ void MainWindow::recentCaptureFile(const QString &filename) { if(QFileInfo::exists(filename)) { - LoadCapture(filename, false, true); + LoadCapture(filename, ReplayOptions(), false, true); } else { diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index 8e7965a2d..2de863256 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -77,7 +77,7 @@ public: void takeCaptureOwnership() { m_OwnTempCapture = true; } void captureModified(); void LoadFromFilename(const QString &filename, bool temporary); - void LoadCapture(const QString &filename, bool temporary, bool local); + void LoadCapture(const QString &filename, const ReplayOptions &opts, bool temporary, bool local); void CloseCapture(); QString GetSavePath(QString title = QString(), QString filter = QString()); diff --git a/qrenderdoc/Windows/PythonShell.cpp b/qrenderdoc/Windows/PythonShell.cpp index 49903967f..7547ad0c5 100644 --- a/qrenderdoc/Windows/PythonShell.cpp +++ b/qrenderdoc/Windows/PythonShell.cpp @@ -173,10 +173,10 @@ struct CaptureContextInvoker : ICaptureContext { return InvokeRetFunction(&ICaptureContext::CreateWindowingData, window); } - virtual void LoadCapture(const rdcstr &capture, const rdcstr &origFilename, bool temporary, - bool local) override + virtual void LoadCapture(const rdcstr &capture, const ReplayOptions &opts, + const rdcstr &origFilename, bool temporary, bool local) override { - InvokeVoidFunction(&ICaptureContext::LoadCapture, capture, origFilename, temporary, local); + InvokeVoidFunction(&ICaptureContext::LoadCapture, capture, opts, origFilename, temporary, local); } virtual bool SaveCaptureTo(const rdcstr &capture) override { diff --git a/renderdoc/android/android.cpp b/renderdoc/android/android.cpp index 2008f1a8e..6c2390e3d 100644 --- a/renderdoc/android/android.cpp +++ b/renderdoc/android/android.cpp @@ -461,13 +461,14 @@ struct AndroidRemoteServer : public RemoteServer } virtual rdcpair OpenCapture( - uint32_t proxyid, const char *filename, RENDERDOC_ProgressCallback progress) override + uint32_t proxyid, const char *filename, const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress) override { ResetAndroidSettings(); LazilyStartLogcatThread(); - return RemoteServer::OpenCapture(proxyid, filename, progress); + return RemoteServer::OpenCapture(proxyid, filename, opts, progress); } virtual rdcstr GetHomeFolder() override { return ""; } diff --git a/renderdoc/api/replay/control_types.h b/renderdoc/api/replay/control_types.h index 443b1f4a1..2feefb812 100644 --- a/renderdoc/api/replay/control_types.h +++ b/renderdoc/api/replay/control_types.h @@ -698,3 +698,74 @@ struct GPUDevice }; DECLARE_REFLECTION_STRUCT(GPUDevice); + +DOCUMENT("The options controlling how replay of a capture should be performed"); +struct ReplayOptions +{ + DOCUMENT(""); + ReplayOptions() = default; + ReplayOptions(const ReplayOptions &) = default; + + DOCUMENT(R"(Replay with API validation enabled and use debug messages from there, ignoring any +that may be contained in the capture. + +The default is not to do any validation. + +.. note:: RenderDoc does not handle invalid API use in the general case so validation should still + be performed at runtime in your program for ground truth results. +)"); + bool apiValidation = false; + + DOCUMENT(R"(Force the selection of a GPU by vendor ID. This allows overriding which GPU is used to +replay on, even if a different GPU would be the best match for the capture. + +When set to :data:`GPUVendor.Unknown`, specifies no particular vendor. + +See also :data:`forceGPUDeviceID` and :data:`forceGPUDriverName`. Available GPUs can be enumerated +using :meth:`CaptureAccess.GetAvailableGPUs`. + +The default is not to do any override. The capture contains information about what GPU was used, and +the closest matching GPU is used on replay. + +.. note:: + If a GPU is forced that is not available or not supported for a given capture, such as when GPUs + are only available for some APIs and not others, the default GPU selection will be used. If a GPU + is available for a capture but fails to open however then there is no fallback to a default GPU. + + OpenGL does not support GPU selection so the default method (which effectively does nothing) will + always be used. +)"); + GPUVendor forceGPUVendor = GPUVendor::Unknown; + + DOCUMENT(R"(Force the selection of a GPU by device ID. This allows overriding which GPU is used to +replay on. + +When set to 0, specifies no particular device. + +See :data:`forceGPUDeviceID` for a full explanation of GPU selection override. +)"); + uint32_t forceGPUDeviceID = 0; + + DOCUMENT(R"(Force the selection of a GPU by driver name. This allows overriding which GPU is used +to replay on. + +When set to an empty string, specifies no particular driver. + +See :data:`forceGPUDeviceID` for a full explanation of GPU selection override. +)"); + rdcstr forceGPUDriverName; + + DOCUMENT(R"(How much optimisation should be done, potentially at the cost of correctness. + +The default is :data:`ReplayOptimisationLevel.Balanced`. +)"); + ReplayOptimisationLevel optimisation = ReplayOptimisationLevel::Balanced; + +// helpers for Qt, define constructor and cast. These will be defined in Qt code +#if defined(RENDERDOC_QT_COMPAT) + ReplayOptions(const QVariant &var); + operator QVariant() const; +#endif +}; + +DECLARE_REFLECTION_STRUCT(ReplayOptions); diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 7d59b5b86..bcdd5fb1e 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -1690,6 +1690,7 @@ or an error has occurred. or :data:`NoPreference` to indicate no preference for any proxy. :param str logfile: The path on the remote system where the file is. If the file is only available locally you can use :meth:`CopyCaptureToRemote` to transfer it over the remote connection. +:param ReplayOptions opts: The options controlling how the capture should be replayed. :param ProgressCallback progress: A callback that will be repeatedly called with an updated progress value for the opening. Can be ``None`` if no progress is desired. :return: A tuple containing the status of opening the capture, whether success or failure, and the @@ -1697,7 +1698,8 @@ or an error has occurred. :rtype: ``tuple`` of :class:`ReplayStatus` and :class:`ReplayController` )"); virtual rdcpair OpenCapture( - uint32_t proxyid, const char *logfile, RENDERDOC_ProgressCallback progress) = 0; + uint32_t proxyid, const char *logfile, const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress) = 0; DOCUMENT(R"(Close a capture analysis handle previously opened by :meth:`OpenCapture`. @@ -1854,13 +1856,15 @@ This function will block until the capture is fully loaded and ready. Once the replay is created, this :class:`CaptureFile` can be shut down, there is no dependency on it by the :class:`ReplayController`. +:param ReplayOptions opts: The options controlling how the capture should be replayed. :param ProgressCallback progress: A callback that will be repeatedly called with an updated progress value for the opening. Can be ``None`` if no progress is desired. :return: A tuple containing the status of opening the capture, whether success or failure, and the resulting :class:`ReplayController` handle if successful. :rtype: ``tuple`` of :class:`ReplayStatus` and :class:`ReplayController`. )"); - virtual rdcpair OpenCapture(RENDERDOC_ProgressCallback progress) = 0; + virtual rdcpair OpenCapture( + const ReplayOptions &opts, RENDERDOC_ProgressCallback progress) = 0; DOCUMENT(R"(Returns the structured data for this capture. diff --git a/renderdoc/api/replay/renderdoc_tostr.inl b/renderdoc/api/replay/renderdoc_tostr.inl index 674ecf1b8..d192b6e01 100644 --- a/renderdoc/api/replay/renderdoc_tostr.inl +++ b/renderdoc/api/replay/renderdoc_tostr.inl @@ -948,6 +948,19 @@ rdcstr DoStringise(const SectionType &el) END_ENUM_STRINGISE(); } +template <> +rdcstr DoStringise(const ReplayOptimisationLevel &el) +{ + BEGIN_ENUM_STRINGISE(ReplayOptimisationLevel); + { + STRINGISE_ENUM_CLASS_NAMED(NoOptimisation, "No Optimisation"); + STRINGISE_ENUM_CLASS(Conservative); + STRINGISE_ENUM_CLASS(Balanced); + STRINGISE_ENUM_CLASS(Fastest); + } + END_ENUM_STRINGISE(); +} + template <> rdcstr DoStringise(const D3DBufferViewFlags &el) { diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index b4d7f8dbe..2f6c06c29 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -3436,6 +3436,41 @@ DECLARE_REFLECTION_ENUM(LogType); ITERABLE_OPERATORS(LogType); +DOCUMENT(R"(The level of optimisation used in + +.. data:: NoOptimisation + + Completely disabled, no optimisation will be used at all. + +.. data:: Conservative + + Optimisation is used when it doesn't interfere with replay correctness. + +.. data:: Balanced + + Optimisation is used when it has minimal impact on replay correctness. This could include e.g. + resources appearing cleared instead of containing contents from prior frames where those resources + are written to before being read. + +.. data:: Fastest + + All possible optimisations are enabled as long as they do not cause invalid/incorrect replay. + This could result in side-effects like data from one replay being visible early in another replay, + if it's known that the data will be overwritten before being used. +)"); +enum class ReplayOptimisationLevel : int32_t +{ + NoOptimisation, + First = NoOptimisation, + Conservative, + Balanced, + Fastest, + Count, +}; + +DECLARE_REFLECTION_ENUM(ReplayOptimisationLevel); +ITERABLE_OPERATORS(ReplayOptimisationLevel); + #if defined(ENABLE_PYTHON_FLAG_ENUMS) ENABLE_PYTHON_FLAG_ENUMS; diff --git a/renderdoc/core/core.cpp b/renderdoc/core/core.cpp index c7d3f3899..445c857d6 100644 --- a/renderdoc/core/core.cpp +++ b/renderdoc/core/core.cpp @@ -43,6 +43,25 @@ #include "replay/renderdoc_serialise.inl" +void LogReplayOptions(const ReplayOptions &opts) +{ + RDCLOG("%s API validation during replay", (opts.apiValidation ? "Enabling" : "Not enabling")); + + if(opts.forceGPUVendor == GPUVendor::Unknown && opts.forceGPUDeviceID == 0 && + opts.forceGPUDriverName.empty()) + { + RDCLOG("Using default GPU replay selection algorithm"); + } + else + { + RDCLOG("Overriding GPU replay selection:"); + RDCLOG(" Vendor %s, device %u, driver \"%s\"", ToStr(opts.forceGPUVendor).c_str(), + opts.forceGPUDeviceID, opts.forceGPUDriverName.c_str()); + } + + RDCLOG("Replay optimisation level: %s", ToStr(opts.optimisation).c_str()); +} + // this one is done by hand as we format it template <> rdcstr DoStringise(const ResourceId &el) @@ -1260,17 +1279,18 @@ ReplayStatus RenderDoc::CreateProxyReplayDriver(RDCDriver proxyDriver, IReplayDr if(proxyDriver == RDCDriver::Unknown) { if(!m_ReplayDriverProviders.empty()) - return m_ReplayDriverProviders.begin()->second(NULL, driver); + return m_ReplayDriverProviders.begin()->second(NULL, ReplayOptions(), driver); } if(m_ReplayDriverProviders.find(proxyDriver) != m_ReplayDriverProviders.end()) - return m_ReplayDriverProviders[proxyDriver](NULL, driver); + return m_ReplayDriverProviders[proxyDriver](NULL, ReplayOptions(), driver); RDCERR("Unsupported replay driver requested: %s", ToStr(proxyDriver).c_str()); return ReplayStatus::APIUnsupported; } -ReplayStatus RenderDoc::CreateReplayDriver(RDCFile *rdc, IReplayDriver **driver) +ReplayStatus RenderDoc::CreateReplayDriver(RDCFile *rdc, const ReplayOptions &opts, + IReplayDriver **driver) { if(driver == NULL) return ReplayStatus::InternalError; @@ -1279,7 +1299,7 @@ ReplayStatus RenderDoc::CreateReplayDriver(RDCFile *rdc, IReplayDriver **driver) if(rdc == NULL) { if(!m_ReplayDriverProviders.empty()) - return m_ReplayDriverProviders.begin()->second(NULL, driver); + return m_ReplayDriverProviders.begin()->second(NULL, opts, driver); RDCERR("Request for proxy replay device, but no replay providers are available."); return ReplayStatus::InternalError; @@ -1292,13 +1312,14 @@ ReplayStatus RenderDoc::CreateReplayDriver(RDCFile *rdc, IReplayDriver **driver) return IMG_CreateReplayDevice(rdc, driver); if(m_ReplayDriverProviders.find(driverType) != m_ReplayDriverProviders.end()) - return m_ReplayDriverProviders[driverType](rdc, driver); + return m_ReplayDriverProviders[driverType](rdc, opts, driver); RDCERR("Unsupported replay driver requested: %s", ToStr(driverType).c_str()); return ReplayStatus::APIUnsupported; } -ReplayStatus RenderDoc::CreateRemoteDriver(RDCFile *rdc, IRemoteDriver **driver) +ReplayStatus RenderDoc::CreateRemoteDriver(RDCFile *rdc, const ReplayOptions &opts, + IRemoteDriver **driver) { if(rdc == NULL || driver == NULL) return ReplayStatus::InternalError; @@ -1306,13 +1327,13 @@ ReplayStatus RenderDoc::CreateRemoteDriver(RDCFile *rdc, IRemoteDriver **driver) RDCDriver driverType = rdc->GetDriver(); if(m_RemoteDriverProviders.find(driverType) != m_RemoteDriverProviders.end()) - return m_RemoteDriverProviders[driverType](rdc, driver); + return m_RemoteDriverProviders[driverType](rdc, opts, driver); // replay drivers are remote drivers, fall back and try them if(m_ReplayDriverProviders.find(driverType) != m_ReplayDriverProviders.end()) { IReplayDriver *dr = NULL; - ReplayStatus status = m_ReplayDriverProviders[driverType](rdc, &dr); + ReplayStatus status = m_ReplayDriverProviders[driverType](rdc, opts, &dr); if(status == ReplayStatus::Succeeded) *driver = (IRemoteDriver *)dr; diff --git a/renderdoc/core/core.h b/renderdoc/core/core.h index 833bfef20..aa06fcd87 100644 --- a/renderdoc/core/core.h +++ b/renderdoc/core/core.h @@ -43,6 +43,7 @@ struct RDCThumb; // not provided by tinyexr, just do by hand bool is_exr_file(FILE *f); +void LogReplayOptions(const ReplayOptions &opts); struct ICrashHandler { @@ -336,8 +337,10 @@ class IReplayDriver; class StreamReader; class RDCFile; -typedef ReplayStatus (*RemoteDriverProvider)(RDCFile *rdc, IRemoteDriver **driver); -typedef ReplayStatus (*ReplayDriverProvider)(RDCFile *rdc, IReplayDriver **driver); +typedef ReplayStatus (*RemoteDriverProvider)(RDCFile *rdc, const ReplayOptions &opts, + IRemoteDriver **driver); +typedef ReplayStatus (*ReplayDriverProvider)(RDCFile *rdc, const ReplayOptions &opts, + IReplayDriver **driver); typedef void (*StructuredProcessor)(RDCFile *rdc, SDFile &structData); @@ -522,8 +525,8 @@ public: bool IsDarkTheme() { return m_DarkTheme; } void SetDarkTheme(bool dark) { m_DarkTheme = dark; } ReplayStatus CreateProxyReplayDriver(RDCDriver proxyDriver, IReplayDriver **driver); - ReplayStatus CreateReplayDriver(RDCFile *rdc, IReplayDriver **driver); - ReplayStatus CreateRemoteDriver(RDCFile *rdc, IRemoteDriver **driver); + ReplayStatus CreateReplayDriver(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver); + ReplayStatus CreateRemoteDriver(RDCFile *rdc, const ReplayOptions &opts, IRemoteDriver **driver); bool HasReplaySupport(RDCDriver driverType); diff --git a/renderdoc/core/remote_server.cpp b/renderdoc/core/remote_server.cpp index 973c5879c..f0af62c3e 100644 --- a/renderdoc/core/remote_server.cpp +++ b/renderdoc/core/remote_server.cpp @@ -397,10 +397,12 @@ static void ActiveRemoteClientThread(ClientThread *threadData, else if(type == eRemoteServer_OpenLog) { std::string path; + ReplayOptions opts; { READ_DATA_SCOPE(); SERIALISE_ELEMENT(path); + SERIALISE_ELEMENT(opts); } reader.EndChunk(); @@ -450,13 +452,13 @@ static void ActiveRemoteClientThread(ClientThread *threadData, // if we have a replay driver, try to create it so we can display a local preview e.g. if(RenderDoc::Inst().HasReplayDriver(rdc->GetDriver())) { - status = RenderDoc::Inst().CreateReplayDriver(rdc, &replayDriver); + status = RenderDoc::Inst().CreateReplayDriver(rdc, opts, &replayDriver); if(replayDriver) remoteDriver = replayDriver; } else { - status = RenderDoc::Inst().CreateRemoteDriver(rdc, &remoteDriver); + status = RenderDoc::Inst().CreateRemoteDriver(rdc, opts, &remoteDriver); } if(status != ReplayStatus::Succeeded || remoteDriver == NULL) @@ -1434,7 +1436,8 @@ void RemoteServer::TakeOwnershipCapture(const char *filename) } rdcpair RemoteServer::OpenCapture( - uint32_t proxyid, const char *filename, RENDERDOC_ProgressCallback progress) + uint32_t proxyid, const char *filename, const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress) { rdcpair ret; ret.first = ReplayStatus::InternalError; @@ -1449,6 +1452,8 @@ rdcpair RemoteServer::OpenCapture( RDCLOG("Opening capture remotely"); + LogReplayOptions(opts); + // if the proxy id is ~0U, then we just don't care so let RenderDoc pick the most // appropriate supported proxy for the current platform. RDCDriver proxydrivertype = proxyid == ~0U ? RDCDriver::Unknown : m_Proxies[proxyid].first; @@ -1457,6 +1462,7 @@ rdcpair RemoteServer::OpenCapture( WRITE_DATA_SCOPE(); SCOPED_SERIALISE_CHUNK(eRemoteServer_OpenLog); SERIALISE_ELEMENT(filename); + SERIALISE_ELEMENT(opts); } RemoteServerPacket type = eRemoteServer_Noop; diff --git a/renderdoc/core/remote_server.h b/renderdoc/core/remote_server.h index 42e9a679b..fac2048e2 100644 --- a/renderdoc/core/remote_server.h +++ b/renderdoc/core/remote_server.h @@ -71,6 +71,7 @@ public: virtual rdcpair OpenCapture(uint32_t proxyid, const char *filename, + const ReplayOptions &opts, RENDERDOC_ProgressCallback progress); virtual void CloseCapture(IReplayController *rend); diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index 631fc2a06..9e2b4159e 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -3625,7 +3625,7 @@ void D3D11Replay::SetProxyBufferData(ResourceId bufid, byte *data, size_t dataSi ID3DDevice *GetD3D11DeviceIfAlloc(IUnknown *dev); -ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) +ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { RDCDEBUG("Creating a D3D11 replay device"); diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index cc7f62348..2befbdc62 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -3617,7 +3617,7 @@ void D3D12Replay::SetProxyBufferData(ResourceId bufid, byte *data, size_t dataSi #pragma endregion -ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) +ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { RDCDEBUG("Creating a D3D12 replay device"); diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 3235c3ee7..9ded3b554 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -3363,8 +3363,8 @@ void GLReplay::CloseReplayContext() m_pDriver->m_Platform.DeleteReplayContext(m_ReplayCtx); } -ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, GLPlatform &platform, - IReplayDriver **&driver) +ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayOptions &opts, + GLPlatform &platform, IReplayDriver **&driver) { GLInitParams initParams; uint64_t ver = GLInitParams::CurrentVersion; @@ -3512,7 +3512,7 @@ std::vector GetReplayVersions(RDCDriver api) #if defined(RENDERDOC_SUPPORT_GL) -ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) +ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { RDCDEBUG("Creating an OpenGL replay device"); @@ -3524,7 +3524,8 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) return ReplayStatus::APIInitFailed; } - return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGL, rdc, GetGLPlatform(), driver); + return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGL, rdc, opts, GetGLPlatform(), + driver); } static DriverRegistration GLDriverRegistration(RDCDriver::OpenGL, &GL_CreateReplayDevice); @@ -3533,7 +3534,7 @@ static DriverRegistration GLDriverRegistration(RDCDriver::OpenGL, &GL_CreateRepl #if defined(RENDERDOC_SUPPORT_GLES) -ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) +ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { RDCLOG("Creating an OpenGL ES replay device"); @@ -3551,8 +3552,8 @@ ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) RDCLOG("Initialising GLES replay via libEGL"); - return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGLES, rdc, GetEGLPlatform(), - driver); + return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGLES, rdc, opts, + GetEGLPlatform(), driver); } #if defined(RENDERDOC_SUPPORT_GL) else if(GetGLPlatform().CanCreateGLESContext()) @@ -3567,8 +3568,8 @@ ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) return ReplayStatus::APIInitFailed; } - return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGLES, rdc, GetGLPlatform(), - driver); + return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGLES, rdc, opts, + GetGLPlatform(), driver); } RDCERR( diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 68f413c77..8195c0f1d 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -4129,7 +4129,7 @@ void VulkanReplay::SetProxyBufferData(ResourceId bufid, byte *data, size_t dataS VULKANNOTIMP("SetProxyTextureData"); } -ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) +ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { RDCDEBUG("Creating a VulkanReplay replay device"); diff --git a/renderdoc/replay/capture_file.cpp b/renderdoc/replay/capture_file.cpp index 7ae14639d..1dceff128 100644 --- a/renderdoc/replay/capture_file.cpp +++ b/renderdoc/replay/capture_file.cpp @@ -122,7 +122,8 @@ public: ReplaySupport LocalReplaySupport() { return m_Support; } rdcstr DriverName() { return m_DriverName; } const char *RecordedMachineIdent() { return m_Ident.c_str(); } - rdcpair OpenCapture(RENDERDOC_ProgressCallback progress); + rdcpair OpenCapture(const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress); void SetMetadata(const char *driverName, uint64_t machineIdent, FileType thumbType, uint32_t thumbWidth, uint32_t thumbHeight, const bytebuf &thumbData); @@ -353,7 +354,8 @@ void CaptureFile::InitStructuredData(RENDERDOC_ProgressCallback progress /*= REN } } -rdcpair CaptureFile::OpenCapture(RENDERDOC_ProgressCallback progress) +rdcpair CaptureFile::OpenCapture(const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress) { if(!m_RDC || m_RDC->ErrorCode() != ContainerError::NoError) return rdcpair(ReplayStatus::InternalError, NULL); @@ -361,9 +363,11 @@ rdcpair CaptureFile::OpenCapture(RENDERDOC_Pr ReplayController *render = new ReplayController(); ReplayStatus ret; + LogReplayOptions(opts); + RenderDoc::Inst().SetProgressCallback(progress); - ret = render->CreateDevice(m_RDC); + ret = render->CreateDevice(m_RDC, opts); RenderDoc::Inst().SetProgressCallback(RENDERDOC_ProgressCallback()); diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 7ac0cecf5..738ccd7f9 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -891,6 +891,18 @@ void DoSerialise(SerialiserType &ser, GPUDevice &el) SIZE_CHECK(80); } +template +void DoSerialise(SerialiserType &ser, ReplayOptions &el) +{ + SERIALISE_MEMBER(apiValidation); + SERIALISE_MEMBER(forceGPUVendor); + SERIALISE_MEMBER(forceGPUDeviceID); + SERIALISE_MEMBER(forceGPUDriverName); + SERIALISE_MEMBER(optimisation); + + SIZE_CHECK(48); +} + #pragma region Common pipeline state template @@ -2256,6 +2268,7 @@ INSTANTIATE_SERIALISE_TYPE(EventUsage) INSTANTIATE_SERIALISE_TYPE(CounterResult) INSTANTIATE_SERIALISE_TYPE(CounterValue) INSTANTIATE_SERIALISE_TYPE(GPUDevice) +INSTANTIATE_SERIALISE_TYPE(ReplayOptions) INSTANTIATE_SERIALISE_TYPE(D3D11Pipe::Layout) INSTANTIATE_SERIALISE_TYPE(D3D11Pipe::InputAssembly) INSTANTIATE_SERIALISE_TYPE(D3D11Pipe::View) diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index f41a3e161..c0b9011a1 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -2032,12 +2032,12 @@ void ReplayController::RemoveReplacement(ResourceId id) m_Outputs[i]->Display(); } -ReplayStatus ReplayController::CreateDevice(RDCFile *rdc) +ReplayStatus ReplayController::CreateDevice(RDCFile *rdc, const ReplayOptions &opts) { CHECK_REPLAY_THREAD(); IReplayDriver *driver = NULL; - ReplayStatus status = RenderDoc::Inst().CreateReplayDriver(rdc, &driver); + ReplayStatus status = RenderDoc::Inst().CreateReplayDriver(rdc, opts, &driver); if(driver && status == ReplayStatus::Succeeded) { diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 865ef2332..f690076d1 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -137,7 +137,7 @@ public: APIProperties GetAPIProperties(); - ReplayStatus CreateDevice(RDCFile *rdc); + ReplayStatus CreateDevice(RDCFile *rdc, const ReplayOptions &opts); ReplayStatus SetDevice(IReplayDriver *device); void FileChanged(); diff --git a/renderdoccmd/renderdoccmd.cpp b/renderdoccmd/renderdoccmd.cpp index 03198ba02..2dfec0d00 100644 --- a/renderdoccmd/renderdoccmd.cpp +++ b/renderdoccmd/renderdoccmd.cpp @@ -566,7 +566,7 @@ struct ReplayCommand : public Command rdcstr remotePath = remote->CopyCaptureToRemote(filename.c_str(), NULL); IReplayController *renderer = NULL; - rdctie(status, renderer) = remote->OpenCapture(~0U, remotePath.c_str(), NULL); + rdctie(status, renderer) = remote->OpenCapture(~0U, remotePath.c_str(), ReplayOptions(), NULL); if(status == ReplayStatus::Succeeded) { @@ -596,7 +596,7 @@ struct ReplayCommand : public Command IReplayController *renderer = NULL; ReplayStatus status = ReplayStatus::InternalError; - rdctie(status, renderer) = file->OpenCapture(NULL); + rdctie(status, renderer) = file->OpenCapture(ReplayOptions(), NULL); file->Shutdown();