diff --git a/sources/Renderer/Direct3D11/Command/D3D11CommandContext.h b/sources/Renderer/Direct3D11/Command/D3D11CommandContext.h index fcf06b4623..2ebf33f952 100644 --- a/sources/Renderer/Direct3D11/Command/D3D11CommandContext.h +++ b/sources/Renderer/Direct3D11/Command/D3D11CommandContext.h @@ -91,6 +91,12 @@ class D3D11CommandContext return context_.Get(); } + // Returns a pointer to the state manager for this command context. + inline D3D11StateManager* GetStateManagerPtr() const + { + return stateMngr_.get(); + } + // Returns the state manager for this command context. inline D3D11StateManager& GetStateManager() const { diff --git a/sources/Renderer/Direct3D11/Command/D3D11PrimaryCommandBuffer.h b/sources/Renderer/Direct3D11/Command/D3D11PrimaryCommandBuffer.h index aa15cc8f49..ee060569bf 100644 --- a/sources/Renderer/Direct3D11/Command/D3D11PrimaryCommandBuffer.h +++ b/sources/Renderer/Direct3D11/Command/D3D11PrimaryCommandBuffer.h @@ -44,28 +44,19 @@ class D3D11PrimaryCommandBuffer final : public D3D11CommandBuffer return commandList_.Get(); } - private: - - void ClearWithIntermediateUAV(ID3D11Buffer* buffer, UINT offset, UINT size, const UINT (&valuesVec4)[4]); - - // Creates a copy of this buffer as ByteAddressBuffer; 'size' must be a multiple of 4. - void CreateByteAddressBufferR32Typeless( - ID3D11Device* device, - ID3D11DeviceContext* context, - ID3D11Buffer** bufferOutput, - ID3D11ShaderResourceView** srvOutput, - ID3D11UnorderedAccessView** uavOutput, - UINT size, - D3D11_USAGE usage = D3D11_USAGE_DEFAULT - ); - // Returns the native D3D11 device context. inline ID3D11DeviceContext* GetNative() const { return context_.GetNative(); } - // Returns the state manager for this command context. + // Returns a pointer to the state manager for this command buffer. + inline D3D11StateManager* GetStateManagerPtr() const + { + return context_.GetStateManagerPtr(); + } + + // Returns the state manager for this command buffer. inline D3D11StateManager& GetStateManager() const { return context_.GetStateManager(); @@ -77,6 +68,21 @@ class D3D11PrimaryCommandBuffer final : public D3D11CommandBuffer return context_.GetBindingTable(); } + private: + + void ClearWithIntermediateUAV(ID3D11Buffer* buffer, UINT offset, UINT size, const UINT (&valuesVec4)[4]); + + // Creates a copy of this buffer as ByteAddressBuffer; 'size' must be a multiple of 4. + void CreateByteAddressBufferR32Typeless( + ID3D11Device* device, + ID3D11DeviceContext* context, + ID3D11Buffer** bufferOutput, + ID3D11ShaderResourceView** srvOutput, + ID3D11UnorderedAccessView** uavOutput, + UINT size, + D3D11_USAGE usage = D3D11_USAGE_DEFAULT + ); + private: // Device object to create on-demand objects like temporary SRVs and UAVs diff --git a/sources/Renderer/Direct3D11/D3D11RenderSystem.cpp b/sources/Renderer/Direct3D11/D3D11RenderSystem.cpp index 631cc9a180..220a10af99 100644 --- a/sources/Renderer/Direct3D11/D3D11RenderSystem.cpp +++ b/sources/Renderer/Direct3D11/D3D11RenderSystem.cpp @@ -126,6 +126,9 @@ CommandBuffer* D3D11RenderSystem::CreateCommandBuffer(const CommandBufferDescrip /* Create state manager dedicated to deferred context */ std::shared_ptr deferredStateMngr = std::make_shared(device_.Get(), deferredContext); + /* Store references to unique state manager - we need to notify all binding tables on resource release */ + deferredStateMngrRefs_.push_back(deferredStateMngr.get()); + /* Create command buffer with deferred context and dedicated state manager */ return commandBuffers_.emplace(device_.Get(), deferredContext, std::move(deferredStateMngr), commandBufferDesc); } @@ -133,6 +136,22 @@ CommandBuffer* D3D11RenderSystem::CreateCommandBuffer(const CommandBufferDescrip void D3D11RenderSystem::Release(CommandBuffer& commandBuffer) { + auto& commandBufferD3D = LLGL_CAST(D3D11CommandBuffer&, commandBuffer); + if (!commandBufferD3D.IsSecondaryCmdBuffer()) + { + /* If this is command buffer has a unique state manager, remove it from the list of deferred state managers */ + auto& primaryCmdBufferD3D = LLGL_CAST(D3D11PrimaryCommandBuffer&, commandBufferD3D); + if (primaryCmdBufferD3D.GetStateManagerPtr() != stateMngr_.get()) + { + RemoveFromListIf( + deferredStateMngrRefs_, + [stateMngr = primaryCmdBufferD3D.GetStateManagerPtr()](D3D11StateManager* entry) -> bool + { + return (entry == stateMngr); + } + ); + } + } commandBuffers_.erase(&commandBuffer); } @@ -155,6 +174,8 @@ BufferArray* D3D11RenderSystem::CreateBufferArray(std::uint32_t numBuffers, Buff void D3D11RenderSystem::Release(Buffer& buffer) { + auto& bufferD3D = LLGL_CAST(D3D11Buffer&, buffer); + NotifyBindingTablesOnRelease(bufferD3D.GetBindingLocator()); buffers_.erase(&buffer); } @@ -212,6 +233,8 @@ Texture* D3D11RenderSystem::CreateTexture(const TextureDescriptor& textureDesc, void D3D11RenderSystem::Release(Texture& texture) { + auto& textureD3D = LLGL_CAST(D3D11Texture&, texture); + NotifyBindingTablesOnRelease(textureD3D.GetBindingLocator()); textures_.erase(&texture); } @@ -377,6 +400,11 @@ RenderTarget* D3D11RenderSystem::CreateRenderTarget(const RenderTargetDescriptor void D3D11RenderSystem::Release(RenderTarget& renderTarget) { + auto& renderTargetD3D = LLGL_CAST(D3D11RenderTarget&, renderTarget); + const D3D11RenderTargetHandles& rtHandles = renderTargetD3D.GetRenderTargetHandles(); + NotifyBindingTablesOnRelease(rtHandles.GetDepthStencilLocator()); + for_range(i, rtHandles.GetNumRenderTargetViews()) + NotifyBindingTablesOnRelease(rtHandles.GetRenderTargetLocators()[i]); renderTargets_.erase(&renderTarget); } @@ -1097,6 +1125,19 @@ bool D3D11RenderSystem::CheckFactoryFeatureSupport(DXGI_FEATURE feature) const #endif +void D3D11RenderSystem::NotifyBindingTablesOnRelease(D3D11BindingLocator* locator) +{ + if (locator != nullptr) + { + /* Notify state manager that is shared across the primary D3D device context */ + stateMngr_->GetBindingTable().NotifyResourceRelease(locator); + + /* Notify state managers for all deferred device contexts */ + for (D3D11StateManager* deferredStateMngr : deferredStateMngrRefs_) + deferredStateMngr->GetBindingTable().NotifyResourceRelease(locator); + } +} + } // /namespace LLGL diff --git a/sources/Renderer/Direct3D11/D3D11RenderSystem.h b/sources/Renderer/Direct3D11/D3D11RenderSystem.h index 2e4d308664..0a4b5f9c90 100644 --- a/sources/Renderer/Direct3D11/D3D11RenderSystem.h +++ b/sources/Renderer/Direct3D11/D3D11RenderSystem.h @@ -130,6 +130,8 @@ class D3D11RenderSystem final : public RenderSystem bool CheckFactoryFeatureSupport(DXGI_FEATURE feature) const; #endif + void NotifyBindingTablesOnRelease(D3D11BindingLocator* locator); + private: /* ----- Common objects ----- */ @@ -164,6 +166,7 @@ class D3D11RenderSystem final : public RenderSystem bool tearingSupported_ = false; std::shared_ptr stateMngr_; + std::vector deferredStateMngrRefs_; /* ----- Hardware object containers ----- */ diff --git a/sources/Renderer/Direct3D11/RenderState/D3D11BindingTable.cpp b/sources/Renderer/Direct3D11/RenderState/D3D11BindingTable.cpp index 10eb3654dd..de903a355f 100644 --- a/sources/Renderer/Direct3D11/RenderState/D3D11BindingTable.cpp +++ b/sources/Renderer/Direct3D11/RenderState/D3D11BindingTable.cpp @@ -249,6 +249,7 @@ void D3D11BindingTable::SetRenderTargets( void D3D11BindingTable::ClearState() { + /* Clear binding locators from all tables */ ClearBindingLocators(vb_); ClearBindingLocators(ib_); ClearBindingLocators(srvVS_); @@ -279,16 +280,23 @@ void D3D11BindingTable::FlushOutputMergerUAVs() } } +void D3D11BindingTable::NotifyResourceRelease(D3D11BindingLocator* locator) +{ + /* When a resource is about to be released, evict all of its bindings from this binding table */ + EvictAllBindings(locator); +} + /* * ======= Private: ======= */ +static void* const g_nullArray[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] = {}; + template static T* const* GetNullPointerArray() { - static void* const nullArray[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] = {}; - return reinterpret_cast(nullArray); + return reinterpret_cast(g_nullArray); } void D3D11BindingTable::InsertInput(D3D11BindingLocator** container, D3D11BindingLocator::D3DInputs input, UINT slot, D3D11BindingLocator* locator) diff --git a/sources/Renderer/Direct3D11/RenderState/D3D11BindingTable.h b/sources/Renderer/Direct3D11/RenderState/D3D11BindingTable.h index edc98d2db6..906a14de72 100644 --- a/sources/Renderer/Direct3D11/RenderState/D3D11BindingTable.h +++ b/sources/Renderer/Direct3D11/RenderState/D3D11BindingTable.h @@ -99,11 +99,15 @@ class D3D11BindingTable D3D11BindingLocator* depthStencilLocators ); + // Clears the binding table state but doesn't perform any operations on the D3D device context. void ClearState(); // Binds all pending output merger UAVs to the device context if they have previosuly changed. void FlushOutputMergerUAVs(); + // Notifies the binding table that a resource, represented by the specified binding locator, is about to be released. + void NotifyResourceRelease(D3D11BindingLocator* locator); + private: using D3D11BindingLocatorIterator = SparseForwardIterator; diff --git a/tests/Testbed/UnitTests/TestResourceBinding.cpp b/tests/Testbed/UnitTests/TestResourceBinding.cpp index 61a861b326..9e97f892a3 100644 --- a/tests/Testbed/UnitTests/TestResourceBinding.cpp +++ b/tests/Testbed/UnitTests/TestResourceBinding.cpp @@ -21,6 +21,13 @@ static bool VectorsEqual(const Gs::Vector4i& lhs, const Gs::Vector4i& rhs) ); } +#define SAFE_RELEASE(OBJ) \ + if (OBJ != nullptr) \ + { \ + renderer->Release(*OBJ); \ + OBJ = nullptr; \ + } + /* This test is primarily aiming at the D3D11 backend to ensure the automatic unbinding of R/W resources is working correctly (see D3D11BindingTable, D3DBindingLocator). Bind buffer and texture resources as SRV and UAV in an alternating fashion and across both graphics and compute stages. @@ -52,15 +59,15 @@ DEF_TEST( ResourceBinding ) }; static TestResult result = TestResult::Passed; - static RenderPass* renderPass; - static PipelineLayout* psoLayout[NumPSOs]; - static PipelineState* pso[NumPSOs]; - static Buffer* buffers[4]; - static Buffer* intermediateBuffer; - static Texture* textures[4]; - static RenderTarget* renderTargets[2]; - static ResourceHeap* graphicsResourceHeaps[2]; - static ResourceHeap* computeResourceHeaps[2]; + static RenderPass* renderPass = nullptr; + static PipelineLayout* psoLayout[NumPSOs] = {}; + static PipelineState* pso[NumPSOs] = {}; + static Buffer* buffers[4] = {}; + static Buffer* intermediateBuffer = nullptr; + static Texture* textures[4] = {}; + static RenderTarget* renderTargets[2] = {}; + static ResourceHeap* graphicsResourceHeaps[2] = {}; + static ResourceHeap* computeResourceHeaps[2] = {}; if (shaders[VSResourceBinding] == nullptr || shaders[PSResourceBinding] == nullptr || shaders[CSResourceBinding] == nullptr) { @@ -68,6 +75,104 @@ DEF_TEST( ResourceBinding ) return TestResult::FailedErrors; } + auto CreateBuffersAndTextures = [this]() -> void + { + SAFE_RELEASE(intermediateBuffer); + + // Create in/out resources + BufferDescriptor bufDesc; + { + bufDesc.size = sizeof(std::int32_t)*4; + bufDesc.format = Format::RGBA32SInt; + bufDesc.bindFlags = BindFlags::Sampled | BindFlags::Storage | BindFlags::CopySrc | BindFlags::CopyDst; + } + + TextureDescriptor texDesc; + { + texDesc.type = TextureType::Texture1D; + texDesc.format = Format::RGBA32SInt; + texDesc.extent = { 1, 1, 1 }; + texDesc.bindFlags = BindFlags::ColorAttachment | BindFlags::Storage | BindFlags::Sampled | BindFlags::CopySrc | BindFlags::CopyDst; + texDesc.miscFlags = MiscFlags::NoInitialData; + } + + for_range(i, 4) + { + SAFE_RELEASE(buffers[i]); + const std::string bufName = "RWBuffer[" + std::to_string(i) + "]"; + bufDesc.debugName = bufName.c_str(); + buffers[i] = renderer->CreateBuffer(bufDesc); + + SAFE_RELEASE(textures[i]); + const std::string texName = "RWTexture1D[" + std::to_string(i) + "]"; + texDesc.debugName = texName.c_str(); + textures[i] = renderer->CreateTexture(texDesc); + } + + bufDesc.debugName = "RWBuffer.intermediate"; + intermediateBuffer = renderer->CreateBuffer(bufDesc); + + #if 0 //TODO + // Create extra texture with multiple MIP maps + TextureDescriptor texDescMips; + { + texDescMips.debugName = "RWTexture1D.mips[3]"; + texDescMips.type = TextureType::Texture1D; + texDescMips.format = Format::RGBA32SInt; + texDescMips.extent = { 4, 1, 1 }; + texDescMips.bindFlags = BindFlags::ColorAttachment | BindFlags::Storage | BindFlags::Sampled | BindFlags::CopySrc | BindFlags::CopyDst; + texDescMips.miscFlags = MiscFlags::NoInitialData; + } + textures[4] = renderer->CreateTexture(texDescMips); + #endif + + #if 0 //TODO + // Define subresource views to read and write to texture resource simultaneously, but at different MIP levels + ResourceViewDescriptor texture4ResView0; + texture4ResView0.resource = textures[4]; + texture4ResView0.textureView.type = TextureType::Texture1D; + texture4ResView0.textureView.format = textures[4]->GetFormat(); + texture4ResView0.textureView.subresource.baseMipLevel = 0; + + ResourceViewDescriptor texture4ResView2; + texture4ResView2.resource = textures[4]; + texture4ResView2.textureView.type = TextureType::Texture1D; + texture4ResView2.textureView.format = textures[4]->GetFormat(); + texture4ResView2.textureView.subresource.baseMipLevel = 2; + #endif + + // Create resource heaps + SAFE_RELEASE(graphicsResourceHeaps[0]); + SAFE_RELEASE(graphicsResourceHeaps[1]); + + graphicsResourceHeaps[0] = renderer->CreateResourceHeap(psoLayout[GraphicsPSOResourceHeap], { buffers[0], buffers[1], buffers[2], buffers[3], textures[0] }); + graphicsResourceHeaps[1] = renderer->CreateResourceHeap(psoLayout[GraphicsPSOResourceHeap], { buffers[0], buffers[3], buffers[1], buffers[2], textures[2] }); + + SAFE_RELEASE(computeResourceHeaps[0]); + SAFE_RELEASE(computeResourceHeaps[1]); + + computeResourceHeaps[0] = renderer->CreateResourceHeap(psoLayout[ComputePSOResourceHeap], { buffers[0], buffers[1], buffers[2], buffers[3], textures[0] }); + computeResourceHeaps[1] = renderer->CreateResourceHeap(psoLayout[ComputePSOResourceHeap], { buffers[0], buffers[3], buffers[1], buffers[2], textures[2] }); + }; + + auto CreateRenderTargets = [this]() -> void + { + // Create render targets for graphics PSO output + for_range(i, 2) + { + const Extent3D texExtent = textures[i*2+0]->GetMipExtent(0); + RenderTargetDescriptor renderTargetDesc; + { + renderTargetDesc.renderPass = renderPass; + renderTargetDesc.resolution.width = texExtent.width; + renderTargetDesc.resolution.height = texExtent.height; + renderTargetDesc.colorAttachments[0] = textures[i*2+0]; + renderTargetDesc.colorAttachments[1] = textures[i*2+1]; + } + renderTargets[i] = renderer->CreateRenderTarget(renderTargetDesc); + } + }; + if (frame == 0) { result = TestResult::Passed; @@ -198,86 +303,8 @@ DEF_TEST( ResourceBinding ) } } - // Create in/out resources - BufferDescriptor bufDesc; - { - bufDesc.size = sizeof(std::int32_t)*4; - bufDesc.format = Format::RGBA32SInt; - bufDesc.bindFlags = BindFlags::Sampled | BindFlags::Storage | BindFlags::CopySrc | BindFlags::CopyDst; - } - - TextureDescriptor texDesc; - { - texDesc.type = TextureType::Texture1D; - texDesc.format = Format::RGBA32SInt; - texDesc.extent = { 1, 1, 1 }; - texDesc.bindFlags = BindFlags::ColorAttachment | BindFlags::Storage | BindFlags::Sampled | BindFlags::CopySrc | BindFlags::CopyDst; - texDesc.miscFlags = MiscFlags::NoInitialData; - } - - for_range(i, 4) - { - const std::string bufName = "RWBuffer[" + std::to_string(i) + "]"; - bufDesc.debugName = bufName.c_str(); - buffers[i] = renderer->CreateBuffer(bufDesc); - - const std::string texName = "RWTexture1D[" + std::to_string(i) + "]"; - texDesc.debugName = texName.c_str(); - textures[i] = renderer->CreateTexture(texDesc); - } - - bufDesc.debugName = "RWBuffer.intermediate"; - intermediateBuffer = renderer->CreateBuffer(bufDesc); - - #if 0 //TODO - // Create extra texture with multiple MIP maps - TextureDescriptor texDescMips; - { - texDescMips.debugName = "RWTexture1D.mips[3]"; - texDescMips.type = TextureType::Texture1D; - texDescMips.format = Format::RGBA32SInt; - texDescMips.extent = { 4, 1, 1 }; - texDescMips.bindFlags = BindFlags::ColorAttachment | BindFlags::Storage | BindFlags::Sampled | BindFlags::CopySrc | BindFlags::CopyDst; - texDescMips.miscFlags = MiscFlags::NoInitialData; - } - textures[4] = renderer->CreateTexture(texDescMips); - #endif - - // Create render targets for graphics PSO output - for_range(i, 2) - { - RenderTargetDescriptor renderTargetDesc; - { - renderTargetDesc.renderPass = renderPass; - renderTargetDesc.resolution.width = texDesc.extent.width; - renderTargetDesc.resolution.height = texDesc.extent.height; - renderTargetDesc.colorAttachments[0] = textures[i*2+0]; - renderTargetDesc.colorAttachments[1] = textures[i*2+1]; - } - renderTargets[i] = renderer->CreateRenderTarget(renderTargetDesc); - } - - #if 0 //TODO - // Define subresource views to read and write to texture resource simultaneously, but at different MIP levels - ResourceViewDescriptor texture4ResView0; - texture4ResView0.resource = textures[4]; - texture4ResView0.textureView.type = TextureType::Texture1D; - texture4ResView0.textureView.format = textures[4]->GetFormat(); - texture4ResView0.textureView.subresource.baseMipLevel = 0; - - ResourceViewDescriptor texture4ResView2; - texture4ResView2.resource = textures[4]; - texture4ResView2.textureView.type = TextureType::Texture1D; - texture4ResView2.textureView.format = textures[4]->GetFormat(); - texture4ResView2.textureView.subresource.baseMipLevel = 2; - #endif - - // Create resource heaps - graphicsResourceHeaps[0] = renderer->CreateResourceHeap(psoLayout[GraphicsPSOResourceHeap], { buffers[0], buffers[1], buffers[2], buffers[3], textures[0] }); - graphicsResourceHeaps[1] = renderer->CreateResourceHeap(psoLayout[GraphicsPSOResourceHeap], { buffers[0], buffers[3], buffers[1], buffers[2], textures[2] }); - - computeResourceHeaps[0] = renderer->CreateResourceHeap(psoLayout[ComputePSOResourceHeap], { buffers[0], buffers[1], buffers[2], buffers[3], textures[0] }); - computeResourceHeaps[1] = renderer->CreateResourceHeap(psoLayout[ComputePSOResourceHeap], { buffers[0], buffers[3], buffers[1], buffers[2], textures[2] }); + CreateBuffersAndTextures(); + CreateRenderTargets(); } auto PrintIntermediateResultsVerbose = [this, frame](const char* dispatchName, const ExpectedResults& expectedResults) -> void @@ -536,6 +563,19 @@ DEF_TEST( ResourceBinding ) PrintIntermediateResultsVerbose("RenderOrder3", expectedResults); }; + auto RecreateResources = [&]() -> void + { + if (this->opt.verbose) + Log::Printf("Recreate resources\n"); + + CreateBuffersAndTextures(); + CreateRenderTargets(); + }; + + // Re-create resources intermittently + if (frame % 10 == 10 - 1) + RecreateResources(); + cmdBuf.Begin(); { // Initialize expected results and its resources