From d7a4ddb5d06bb7ef0807540fdd6d245f330c6835 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 17 Jan 2023 12:33:19 +0100 Subject: [PATCH] [d3d11] Implement more accurate resource tracking on deferred contexts Allows us to track chunks that access any given tracked resource more accurately and flush as needed, e.g. if a staging buffer is written at the start of a long command list. --- src/d3d11/d3d11_cmdlist.cpp | 86 +++++++++++++++++++++------------ src/d3d11/d3d11_cmdlist.h | 30 ++++++------ src/d3d11/d3d11_context_def.cpp | 21 +++++--- src/d3d11/d3d11_context_def.h | 5 ++ src/d3d11/d3d11_context_imm.cpp | 8 +-- 5 files changed, 93 insertions(+), 57 deletions(-) diff --git a/src/d3d11/d3d11_cmdlist.cpp b/src/d3d11/d3d11_cmdlist.cpp index c3475781..e15f322a 100644 --- a/src/d3d11/d3d11_cmdlist.cpp +++ b/src/d3d11/d3d11_cmdlist.cpp @@ -41,44 +41,64 @@ namespace dxvk { } - void D3D11CommandList::AddChunk(DxvkCsChunkRef&& Chunk) { - m_chunks.push_back(std::move(Chunk)); - } - - void D3D11CommandList::AddQuery(D3D11Query* pQuery) { m_queries.emplace_back(pQuery); } - void D3D11CommandList::EmitToCommandList(ID3D11CommandList* pCommandList) { - auto cmdList = static_cast(pCommandList); - - for (const auto& chunk : m_chunks) - cmdList->m_chunks.push_back(chunk); - - for (const auto& query : m_queries) - cmdList->m_queries.push_back(query); - - for (const auto& resource : m_resources) - cmdList->m_resources.push_back(resource); - - MarkSubmitted(); + uint64_t D3D11CommandList::AddChunk(DxvkCsChunkRef&& Chunk) { + m_chunks.push_back(std::move(Chunk)); + return m_chunks.size() - 1; } + uint64_t D3D11CommandList::AddCommandList( + D3D11CommandList* pCommandList) { + // This will be the chunk ID of the first chunk + // added, for the purpose of resource tracking. + uint64_t baseChunkId = m_chunks.size(); + + for (const auto& chunk : pCommandList->m_chunks) + m_chunks.push_back(chunk); + + for (const auto& query : pCommandList->m_queries) + m_queries.push_back(query); + + for (const auto& resource : pCommandList->m_resources) { + TrackedResource entry = resource; + entry.chunkId += baseChunkId; + + m_resources.push_back(std::move(entry)); + } + + pCommandList->MarkSubmitted(); + + // Return ID of the last chunk added. The command list + // added can never be empty, so do not handle zero. + return m_chunks.size() - 1; + } + + void D3D11CommandList::EmitToCsThread( const D3D11ChunkDispatchProc& DispatchProc) { - uint64_t seq = 0; - for (const auto& query : m_queries) query->DoDeferredEnd(); - for (const auto& chunk : m_chunks) - seq = DispatchProc(DxvkCsChunkRef(chunk)); - - for (const auto& resource : m_resources) - TrackResourceSequenceNumber(resource, seq); + for (size_t i = 0, j = 0; i < m_chunks.size(); i++) { + // If there are resources to track for the current chunk, + // use a strong flush hint to dispatch GPU work quickly. + GpuFlushType flushType = GpuFlushType::ImplicitWeakHint; + + if (j < m_resources.size() && m_resources[j].chunkId == i) + flushType = GpuFlushType::ImplicitStrongHint; + + // Dispatch the chunk and capture its sequence number + uint64_t seq = DispatchProc(DxvkCsChunkRef(m_chunks[i]), flushType); + + // Track resource sequence numbers for the added chunk + while (j < m_resources.size() && m_resources[j].chunkId == i) + TrackResourceSequenceNumber(m_resources[j++].ref, seq); + } MarkSubmitted(); } @@ -87,8 +107,13 @@ namespace dxvk { void D3D11CommandList::TrackResourceUsage( ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION ResourceType, - UINT Subresource) { - m_resources.emplace_back(pResource, Subresource, ResourceType); + UINT Subresource, + uint64_t ChunkId) { + TrackedResource entry; + entry.ref = D3D11ResourceRef(pResource, Subresource, ResourceType); + entry.chunkId = ChunkId; + + m_resources.push_back(std::move(entry)); } @@ -96,7 +121,6 @@ namespace dxvk { const D3D11ResourceRef& Resource, uint64_t Seq) { ID3D11Resource* iface = Resource.Get(); - UINT subresource = Resource.GetSubresource(); switch (Resource.GetType()) { case D3D11_RESOURCE_DIMENSION_UNKNOWN: @@ -109,17 +133,17 @@ namespace dxvk { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { auto impl = static_cast(iface)->GetCommonTexture(); - impl->TrackSequenceNumber(subresource, Seq); + impl->TrackSequenceNumber(Resource.GetSubresource(), Seq); } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { auto impl = static_cast(iface)->GetCommonTexture(); - impl->TrackSequenceNumber(subresource, Seq); + impl->TrackSequenceNumber(Resource.GetSubresource(), Seq); } break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { auto impl = static_cast(iface)->GetCommonTexture(); - impl->TrackSequenceNumber(subresource, Seq); + impl->TrackSequenceNumber(Resource.GetSubresource(), Seq); } break; } } diff --git a/src/d3d11/d3d11_cmdlist.h b/src/d3d11/d3d11_cmdlist.h index 95330b15..45253ecf 100644 --- a/src/d3d11/d3d11_cmdlist.h +++ b/src/d3d11/d3d11_cmdlist.h @@ -6,7 +6,7 @@ namespace dxvk { - using D3D11ChunkDispatchProc = std::function; + using D3D11ChunkDispatchProc = std::function; class D3D11CommandList : public D3D11DeviceChild { @@ -24,34 +24,36 @@ namespace dxvk { UINT STDMETHODCALLTYPE GetContextFlags() final; - void AddChunk( - DxvkCsChunkRef&& Chunk); - void AddQuery( D3D11Query* pQuery); - void EmitToCommandList( - ID3D11CommandList* pCommandList); - + uint64_t AddChunk( + DxvkCsChunkRef&& Chunk); + + uint64_t AddCommandList( + D3D11CommandList* pCommandList); + void EmitToCsThread( const D3D11ChunkDispatchProc& DispatchProc); void TrackResourceUsage( ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION ResourceType, - UINT Subresource); - - bool HasTrackedResources() const { - return !m_resources.empty(); - } + UINT Subresource, + uint64_t ChunkId); private: - UINT const m_contextFlags; + struct TrackedResource { + D3D11ResourceRef ref; + uint64_t chunkId; + }; + + UINT m_contextFlags; std::vector m_chunks; std::vector> m_queries; - std::vector m_resources; + std::vector m_resources; std::atomic m_submitted = { false }; std::atomic m_warned = { false }; diff --git a/src/d3d11/d3d11_context_def.cpp b/src/d3d11/d3d11_context_def.cpp index b12f5b85..a3f740f4 100644 --- a/src/d3d11/d3d11_context_def.cpp +++ b/src/d3d11/d3d11_context_def.cpp @@ -148,8 +148,9 @@ namespace dxvk { // Record any chunks from the given command list into the // current command list and deal with context state auto commandList = static_cast(pCommandList); - commandList->EmitToCommandList(m_commandList.ptr()); + m_chunkId = m_commandList->AddCommandList(commandList); + // Restore deferred context state if (RestoreContextState) RestoreCommandListState(); else @@ -166,7 +167,8 @@ namespace dxvk { FinalizeQueries(); // Clean up command list state so that the any state changed - // by this command list does not affect the calling context + // by this command list does not affect the calling context. + // This also ensures that the command list is never empty. ResetCommandListState(); // Make sure all commands are visible to the command list @@ -180,6 +182,7 @@ namespace dxvk { // Any use of ExecuteCommandList will reset command list state // before the command list is actually executed. m_commandList = CreateCommandList(); + m_chunkId = 0; if (RestoreDeferredContextState) RestoreCommandListState(); @@ -388,7 +391,12 @@ namespace dxvk { void D3D11DeferredContext::EmitCsChunk(DxvkCsChunkRef&& chunk) { - m_commandList->AddChunk(std::move(chunk)); + m_chunkId = m_commandList->AddChunk(std::move(chunk)); + } + + + uint64_t D3D11DeferredContext::GetCurrentChunkId() const { + return m_csChunk->empty() ? m_chunkId : m_chunkId + 1; } @@ -398,14 +406,15 @@ namespace dxvk { m_commandList->TrackResourceUsage( pResource->GetInterface(), pResource->GetDimension(), - Subresource); + Subresource, GetCurrentChunkId()); } void D3D11DeferredContext::TrackBufferSequenceNumber( D3D11Buffer* pResource) { - m_commandList->TrackResourceUsage( - pResource, D3D11_RESOURCE_DIMENSION_BUFFER, 0); + m_commandList->TrackResourceUsage(pResource, + D3D11_RESOURCE_DIMENSION_BUFFER, 0, + GetCurrentChunkId()); } diff --git a/src/d3d11/d3d11_context_def.h b/src/d3d11/d3d11_context_def.h index 869ae09e..7f1c4eee 100644 --- a/src/d3d11/d3d11_context_def.h +++ b/src/d3d11/d3d11_context_def.h @@ -96,6 +96,9 @@ namespace dxvk { // Begun and ended queries, will also be stored in command list std::vector> m_queriesBegun; + // Chunk ID within the current command list + uint64_t m_chunkId = 0ull; + HRESULT MapBuffer( ID3D11Resource* pResource, D3D11_MAPPED_SUBRESOURCE* pMappedResource); @@ -118,6 +121,8 @@ namespace dxvk { void EmitCsChunk(DxvkCsChunkRef&& chunk); + uint64_t GetCurrentChunkId() const; + void TrackTextureSequenceNumber( D3D11CommonTexture* pResource, UINT Subresource); diff --git a/src/d3d11/d3d11_context_imm.cpp b/src/d3d11/d3d11_context_imm.cpp index e6cfd1d7..e356c77b 100644 --- a/src/d3d11/d3d11_context_imm.cpp +++ b/src/d3d11/d3d11_context_imm.cpp @@ -238,7 +238,7 @@ namespace dxvk { ConsiderFlush(GpuFlushType::ImplicitWeakHint); // Dispatch command list to the CS thread - commandList->EmitToCsThread([this] (DxvkCsChunkRef&& chunk) { + commandList->EmitToCsThread([this] (DxvkCsChunkRef&& chunk, GpuFlushType flushType) { EmitCsChunk(std::move(chunk)); // Return the sequence number from before the flush since @@ -247,14 +247,10 @@ namespace dxvk { // Consider a flush after every chunk in case the app // submits a very large command list or the GPU is idle - ConsiderFlush(GpuFlushType::ImplicitWeakHint); + ConsiderFlush(flushType); return csSeqNum; }); - // If any resource tracking took place, flush with a strong hint - if (commandList->HasTrackedResources()) - ConsiderFlush(GpuFlushType::ImplicitStrongHint); - // Restore the immediate context's state if (RestoreContextState) RestoreCommandListState();