diff --git a/dxvk.conf b/dxvk.conf index 46edbb9c0..3543876a5 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -312,6 +312,20 @@ # d3d11.exposeDriverCommandLists = True +# Reproducible Command Stream +# +# Ensure that for the same D3D commands the output VK commands +# don't change between runs. Useful for comparative benchmarking, +# can negatively affect performance and can break some games +# that don't use queries correctly. +# +# Supported values: +# - True/False + +# d3d11.reproducibleCommandStream = False +# d3d9.reproducibleCommandStream = False + + # Sets number of pipeline compiler threads. # # If the graphics pipeline library feature is enabled, the given diff --git a/src/d3d11/d3d11_context_imm.cpp b/src/d3d11/d3d11_context_imm.cpp index 466e9a967..aa379d41d 100644 --- a/src/d3d11/d3d11_context_imm.cpp +++ b/src/d3d11/d3d11_context_imm.cpp @@ -19,6 +19,7 @@ namespace dxvk { m_csThread(Device, Device->createContext(DxvkContextType::Primary)), m_maxImplicitDiscardSize(pParent->GetOptions()->maxImplicitDiscardSize), m_submissionFence(new sync::CallbackFence()), + m_flushTracker(pParent->GetOptions()->reproducibleCommandStream), m_multithread(this, false, pParent->GetOptions()->enableContextLock), m_videoContext(this, Device) { EmitCs([ diff --git a/src/d3d11/d3d11_options.cpp b/src/d3d11/d3d11_options.cpp index ddbfe0a9a..c6b41ecb7 100644 --- a/src/d3d11/d3d11_options.cpp +++ b/src/d3d11/d3d11_options.cpp @@ -33,6 +33,7 @@ namespace dxvk { this->maxFrameRate = config.getOption("dxgi.maxFrameRate", 0); this->exposeDriverCommandLists = config.getOption("d3d11.exposeDriverCommandLists", true); this->longMad = config.getOption("d3d11.longMad", false); + this->reproducibleCommandStream = config.getOption("d3d11.reproducibleCommandStream", false); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d11/d3d11_options.h b/src/d3d11/d3d11_options.h index 5cd548cd5..a5e21e17e 100644 --- a/src/d3d11/d3d11_options.h +++ b/src/d3d11/d3d11_options.h @@ -123,6 +123,11 @@ namespace dxvk { /// Should we make our Mads a FFma or do it the long way with an FMul and an FAdd? bool longMad; + + /// Ensure that for the same D3D commands the output VK commands + /// don't change between runs. Useful for comparative benchmarking, + /// can negatively affect performance. + bool reproducibleCommandStream; }; } \ No newline at end of file diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 87d39eeb7..2a4a7b32c 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -56,6 +56,7 @@ namespace dxvk { , m_csThread ( dxvkDevice, dxvkDevice->createContext(DxvkContextType::Primary) ) , m_csChunk ( AllocCsChunk() ) , m_submissionFence (new sync::Fence()) + , m_flushTracker (m_d3d9Options.reproducibleCommandStream) , m_d3d9Interop ( this ) , m_d3d9On12 ( this ) , m_d3d8Bridge ( this ) { diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 1845daabd..0884819f1 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -77,6 +77,7 @@ namespace dxvk { this->samplerLodBias = config.getOption ("d3d9.samplerLodBias", 0.0f); this->clampNegativeLodBias = config.getOption ("d3d9.clampNegativeLodBias", false); this->countLosableResources = config.getOption ("d3d9.countLosableResources", true); + this->reproducibleCommandStream = config.getOption ("d3d9.reproducibleCommandStream", false); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index 034c85d3a..824ba076c 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -155,6 +155,11 @@ namespace dxvk { /// Disable counting losable resources and rejecting calls to Reset() if any are still alive bool countLosableResources; + + /// Ensure that for the same D3D commands the output VK commands + /// don't change between runs. Useful for comparative benchmarking, + /// can negatively affect performance. + bool reproducibleCommandStream; }; } diff --git a/src/util/util_flush.cpp b/src/util/util_flush.cpp index a4d91bd43..c2b1ce6a6 100644 --- a/src/util/util_flush.cpp +++ b/src/util/util_flush.cpp @@ -2,6 +2,12 @@ namespace dxvk { + GpuFlushTracker::GpuFlushTracker( + bool ensureReproducibleHeuristic) + : m_ensureReproducibleHeuristic(ensureReproducibleHeuristic) { + + } + bool GpuFlushTracker::considerFlush( GpuFlushType flushType, uint64_t chunkId, @@ -17,6 +23,9 @@ namespace dxvk { if (!chunkCount) return false; + if (m_ensureReproducibleHeuristic && flushType != GpuFlushType::ExplicitFlush) + return false; + // Take any earlier missed flush with a stronger hint into account, so // that we still flush those as soon as possible. Ignore synchronization // commands since they will either perform a flush or not need it at all. diff --git a/src/util/util_flush.h b/src/util/util_flush.h index 138aa992b..c3e71ebcb 100644 --- a/src/util/util_flush.h +++ b/src/util/util_flush.h @@ -34,6 +34,8 @@ namespace dxvk { public: + GpuFlushTracker(bool ensureReproducibleHeuristic); + /** * \brief Checks whether a context flush should be performed * @@ -61,6 +63,8 @@ namespace dxvk { private: + bool m_ensureReproducibleHeuristic; + GpuFlushType m_lastMissedType = GpuFlushType::ImplicitWeakHint; uint64_t m_lastFlushChunkId = 0ull;