From 99b76d1b8fddadd1696dd39fdeee5b074ae45101 Mon Sep 17 00:00:00 2001 From: Natasha Lee Date: Mon, 24 Jun 2019 17:49:46 +0000 Subject: [PATCH] Clear D3D12 texture on first usage if not yet initialized This prevents dirty textures to be used when memory is recycled while destroying/creating new textures. If a texture is being used for the first time and has not yet been initialized, it will be cleared to zeros. Bug: dawn:145 Change-Id: I8b9571c5a8fdd366717ffbd0fafca89b86653cea Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8062 Commit-Queue: Natasha Lee Reviewed-by: Austin Eng --- src/dawn_native/d3d12/CommandBufferD3D12.cpp | 61 ++++++- src/dawn_native/d3d12/TextureD3D12.cpp | 165 +++++++++++++++---- src/dawn_native/d3d12/TextureD3D12.h | 17 +- src/tests/end2end/TextureZeroInitTests.cpp | 2 + 4 files changed, 205 insertions(+), 40 deletions(-) diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index 8f404b07ce..862aa46728 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -440,6 +440,14 @@ namespace dawn_native { namespace d3d12 { ToBackend(usages.buffers[i])->SetUsage(usages.bufferUsages[i]); } + for (size_t i = 0; i < usages.textures.size(); ++i) { + Texture* texture = ToBackend(usages.textures[i]); + // TODO(natlee@microsoft.com): Update clearing here when subresource tracking is + // implemented + texture->EnsureSubresourceContentInitialized( + commandList, 0, texture->GetNumMipLevels(), 0, texture->GetArrayLayers()); + } + for (size_t i = 0; i < usages.textures.size(); ++i) { D3D12_RESOURCE_BARRIER barrier; if (ToBackend(usages.textures[i]) @@ -500,6 +508,15 @@ namespace dawn_native { namespace d3d12 { Buffer* buffer = ToBackend(copy->source.buffer.Get()); Texture* texture = ToBackend(copy->destination.texture.Get()); + if (IsCompleteSubresourceCopiedTo(texture, copy->copySize, + copy->destination.level)) { + texture->SetIsSubresourceContentInitialized(copy->destination.level, 1, + copy->destination.slice, 1); + } else { + texture->EnsureSubresourceContentInitialized( + commandList, copy->destination.level, 1, copy->destination.slice, 1); + } + buffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferSrc); texture->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferDst); @@ -544,6 +561,9 @@ namespace dawn_native { namespace d3d12 { Texture* texture = ToBackend(copy->source.texture.Get()); Buffer* buffer = ToBackend(copy->destination.buffer.Get()); + texture->EnsureSubresourceContentInitialized(commandList, copy->source.level, 1, + copy->source.slice, 1); + texture->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferSrc); buffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferDst); @@ -591,6 +611,16 @@ namespace dawn_native { namespace d3d12 { Texture* source = ToBackend(copy->source.texture.Get()); Texture* destination = ToBackend(copy->destination.texture.Get()); + source->EnsureSubresourceContentInitialized(commandList, copy->source.level, 1, + copy->source.slice, 1); + if (IsCompleteSubresourceCopiedTo(destination, copy->copySize, + copy->destination.level)) { + destination->SetIsSubresourceContentInitialized(copy->destination.level, 1, + copy->destination.slice, 1); + } else { + destination->EnsureSubresourceContentInitialized( + commandList, copy->destination.level, 1, copy->destination.slice, 1); + } source->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferSrc); destination->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferDst); @@ -599,7 +629,6 @@ namespace dawn_native { namespace d3d12 { destination->GetSize(), copy->copySize)) { commandList->CopyResource(destination->GetD3D12Resource(), source->GetD3D12Resource()); - } else { D3D12_TEXTURE_COPY_LOCATION srcLocation = CreateTextureCopyLocationForTexture(*source, copy->source.level, @@ -732,18 +761,43 @@ namespace dawn_native { namespace d3d12 { { for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { auto& attachmentInfo = renderPass->colorAttachments[i]; + TextureView* view = ToBackend(attachmentInfo.view.Get()); // Load op - color + ASSERT(view->GetLevelCount() == 1); + ASSERT(view->GetLayerCount() == 1); if (attachmentInfo.loadOp == dawn::LoadOp::Clear) { D3D12_CPU_DESCRIPTOR_HANDLE handle = args.RTVs[i]; commandList->ClearRenderTargetView(handle, &attachmentInfo.clearColor.r, 0, nullptr); + } else if (attachmentInfo.loadOp == dawn::LoadOp::Load && view->GetTexture()) { + ToBackend(view->GetTexture()) + ->EnsureSubresourceContentInitialized(commandList, view->GetBaseMipLevel(), + 1, view->GetBaseArrayLayer(), 1); + } + switch (attachmentInfo.storeOp) { + case dawn::StoreOp::Store: { + view->GetTexture()->SetIsSubresourceContentInitialized( + view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1); + } break; + + default: { UNREACHABLE(); } break; } } if (renderPass->hasDepthStencilAttachment) { auto& attachmentInfo = renderPass->depthStencilAttachment; Texture* texture = ToBackend(renderPass->depthStencilAttachment.view->GetTexture()); + if ((texture->GetFormat().HasDepth() && + attachmentInfo.depthLoadOp == dawn::LoadOp::Load) || + (texture->GetFormat().HasStencil() && + attachmentInfo.stencilLoadOp == dawn::LoadOp::Load)) { + texture->EnsureSubresourceContentInitialized( + commandList, attachmentInfo.view->GetBaseMipLevel(), + attachmentInfo.view->GetLevelCount(), + attachmentInfo.view->GetBaseArrayLayer(), + attachmentInfo.view->GetLayerCount()); + } // Load op - depth/stencil bool doDepthClear = texture->GetFormat().HasDepth() && @@ -766,6 +820,11 @@ namespace dawn_native { namespace d3d12 { uint8_t clearStencil = static_cast(attachmentInfo.clearStencil); commandList->ClearDepthStencilView( handle, clearFlags, attachmentInfo.clearDepth, clearStencil, 0, nullptr); + texture->SetIsSubresourceContentInitialized( + attachmentInfo.view->GetBaseMipLevel(), + attachmentInfo.view->GetLevelCount(), + attachmentInfo.view->GetBaseArrayLayer(), + attachmentInfo.view->GetLayerCount()); } } } diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index d698c89bd4..215f26fa1b 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -72,6 +72,12 @@ namespace dawn_native { namespace d3d12 { } else { flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; } + } else if ((usage & dawn::TextureUsageBit::TransferDst) || + (usage & dawn::TextureUsageBit::TransferSrc)) { + // if texture is used as copy source or destination, it may need to be + // cleared/initialized, which requires it to be a render target + // TODO(natlee@microsoft.com): optimize texture clearing without render target + flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; } ASSERT(!(flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) || @@ -140,21 +146,43 @@ namespace dawn_native { namespace d3d12 { mResourcePtr = mResource.Get(); if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { - TransitionUsageNow(device->GetPendingCommandList(), D3D12_RESOURCE_STATE_RENDER_TARGET); - uint32_t arrayLayerCount = GetArrayLayers(); - DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator(); - DescriptorHeapHandle rtvHeap = - descriptorHeapAllocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1); - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetCPUHandle(0); + if (GetFormat().HasDepthOrStencil()) { + TransitionUsageNow(device->GetPendingCommandList(), + D3D12_RESOURCE_STATE_DEPTH_WRITE); + DescriptorHeapHandle dsvHeap = + descriptorHeapAllocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1); + D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = dsvHeap.GetCPUHandle(0); + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = GetDSVDescriptor(0); + device->GetD3D12Device()->CreateDepthStencilView(mResourcePtr, &dsvDesc, dsvHandle); - const float clearColor[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - // TODO(natlee@microsoft.com): clear all array layers for 2D array textures - for (int i = 0; i < resourceDescriptor.MipLevels; i++) { - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = GetRTVDescriptor(i, arrayLayerCount, 0); - device->GetD3D12Device()->CreateRenderTargetView(mResourcePtr, &rtvDesc, rtvHandle); - device->GetPendingCommandList()->ClearRenderTargetView(rtvHandle, clearColor, 0, - nullptr); + D3D12_CLEAR_FLAGS clearFlags = {}; + if (GetFormat().HasDepth()) { + clearFlags |= D3D12_CLEAR_FLAG_DEPTH; + } + if (GetFormat().HasStencil()) { + clearFlags |= D3D12_CLEAR_FLAG_STENCIL; + } + + device->GetPendingCommandList()->ClearDepthStencilView(dsvHandle, clearFlags, 1.0f, + 1u, 0, nullptr); + } else { + TransitionUsageNow(device->GetPendingCommandList(), + D3D12_RESOURCE_STATE_RENDER_TARGET); + DescriptorHeapHandle rtvHeap = + descriptorHeapAllocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1); + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetCPUHandle(0); + + const float clearColor[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + // TODO(natlee@microsoft.com): clear all array layers for 2D array textures + for (int i = 0; i < resourceDescriptor.MipLevels; i++) { + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = + GetRTVDescriptor(i, 0, GetArrayLayers()); + device->GetD3D12Device()->CreateRenderTargetView(mResourcePtr, &rtvDesc, + rtvHandle); + device->GetPendingCommandList()->ClearRenderTargetView(rtvHandle, clearColor, 0, + nullptr); + } } } } @@ -259,17 +287,17 @@ namespace dawn_native { namespace d3d12 { mLastState = newState; } - D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipSlice, - uint32_t arrayLayers, - uint32_t baseArrayLayer) const { + D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t baseMipLevel, + uint32_t baseArrayLayer, + uint32_t layerCount) const { ASSERT(GetDimension() == dawn::TextureDimension::e2D); D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; rtvDesc.Format = GetD3D12Format(); if (IsMultisampledTexture()) { ASSERT(GetNumMipLevels() == 1); - ASSERT(arrayLayers == 1); + ASSERT(layerCount == 1); ASSERT(baseArrayLayer == 0); - ASSERT(mipSlice == 0); + ASSERT(baseMipLevel == 0); rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; } else { // Currently we always use D3D12_TEX2D_ARRAY_RTV because we cannot specify base array @@ -280,13 +308,88 @@ namespace dawn_native { namespace d3d12 { // _rtv rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; rtvDesc.Texture2DArray.FirstArraySlice = baseArrayLayer; - rtvDesc.Texture2DArray.ArraySize = arrayLayers; - rtvDesc.Texture2DArray.MipSlice = mipSlice; + rtvDesc.Texture2DArray.ArraySize = layerCount; + rtvDesc.Texture2DArray.MipSlice = baseMipLevel; rtvDesc.Texture2DArray.PlaneSlice = 0; } return rtvDesc; } + D3D12_DEPTH_STENCIL_VIEW_DESC Texture::GetDSVDescriptor(uint32_t baseMipLevel) const { + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; + dsvDesc.Format = GetD3D12Format(); + dsvDesc.Flags = D3D12_DSV_FLAG_NONE; + ASSERT(baseMipLevel == 0); + + if (IsMultisampledTexture()) { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; + } else { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + dsvDesc.Texture2D.MipSlice = baseMipLevel; + } + + return dsvDesc; + } + + void Texture::ClearTexture(ComPtr commandList, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount) { + Device* device = ToBackend(GetDevice()); + DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator(); + + if (GetFormat().HasDepthOrStencil()) { + TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_DEPTH_WRITE); + DescriptorHeapHandle dsvHeap = + descriptorHeapAllocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1); + D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = dsvHeap.GetCPUHandle(0); + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = GetDSVDescriptor(baseMipLevel); + device->GetD3D12Device()->CreateDepthStencilView(mResourcePtr, &dsvDesc, dsvHandle); + + D3D12_CLEAR_FLAGS clearFlags = {}; + if (GetFormat().HasDepth()) { + clearFlags |= D3D12_CLEAR_FLAG_DEPTH; + } + if (GetFormat().HasStencil()) { + clearFlags |= D3D12_CLEAR_FLAG_STENCIL; + } + + commandList->ClearDepthStencilView(dsvHandle, clearFlags, 0.0f, 0u, 0, nullptr); + } else { + TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RENDER_TARGET); + DescriptorHeapHandle rtvHeap = + descriptorHeapAllocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1); + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetCPUHandle(0); + const float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + + // TODO(natlee@microsoft.com): clear all array layers for 2D array textures + for (uint32_t i = baseMipLevel; i < baseMipLevel + levelCount; i++) { + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = + GetRTVDescriptor(i, baseArrayLayer, layerCount); + device->GetD3D12Device()->CreateRenderTargetView(mResourcePtr, &rtvDesc, rtvHandle); + commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); + } + } + SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer, layerCount); + } + + void Texture::EnsureSubresourceContentInitialized(ComPtr commandList, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount) { + if (!ToBackend(GetDevice())->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { + return; + } + if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer, + layerCount)) { + // If subresource has not been initialized, clear it to black as it could contain + // dirty bits from recycled memory + ClearTexture(commandList, baseMipLevel, levelCount, baseArrayLayer, layerCount); + } + } + TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) : TextureViewBase(texture, descriptor) { mSrvDesc.Format = D3D12TextureFormat(descriptor->format); @@ -337,26 +440,16 @@ namespace dawn_native { namespace d3d12 { D3D12_RENDER_TARGET_VIEW_DESC TextureView::GetRTVDescriptor() const { return ToBackend(GetTexture()) - ->GetRTVDescriptor(GetBaseMipLevel(), GetLayerCount(), GetBaseArrayLayer()); + ->GetRTVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount()); } D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() const { - D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; - dsvDesc.Format = ToBackend(GetTexture())->GetD3D12Format(); - dsvDesc.Flags = D3D12_DSV_FLAG_NONE; - // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture. - ASSERT(GetTexture()->GetArrayLayers() == 1 && GetTexture()->GetNumMipLevels() == 1 && - GetBaseArrayLayer() == 0 && GetBaseMipLevel() == 0); - - if (GetTexture()->IsMultisampledTexture()) { - dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; - } else { - dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; - dsvDesc.Texture2D.MipSlice = 0; - } - - return dsvDesc; + ASSERT(GetLayerCount() == 1); + ASSERT(GetLevelCount() == 1); + ASSERT(GetBaseMipLevel() == 0); + ASSERT(GetBaseArrayLayer() == 0); + return ToBackend(GetTexture())->GetDSVDescriptor(GetBaseMipLevel()); } }} // namespace dawn_native::d3d12 diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h index 1c76f0dfd4..7b5eb53e2d 100644 --- a/src/dawn_native/d3d12/TextureD3D12.h +++ b/src/dawn_native/d3d12/TextureD3D12.h @@ -43,13 +43,24 @@ namespace dawn_native { namespace d3d12 { void TransitionUsageNow(ComPtr commandList, D3D12_RESOURCE_STATES newState); - D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(uint32_t mipSlice, - uint32_t arrayLayers, - uint32_t baseArrayLayer) const; + D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(uint32_t baseMipLevel, + uint32_t baseArrayLayer, + uint32_t layerCount) const; + D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(uint32_t baseMipLevel) const; + void EnsureSubresourceContentInitialized(ComPtr commandList, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount); private: // Dawn API void DestroyImpl() override; + void ClearTexture(ComPtr commandList, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount); UINT16 GetDepthOrArraySize(); diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp index 7b530f4339..316d473c05 100644 --- a/src/tests/end2end/TextureZeroInitTests.cpp +++ b/src/tests/end2end/TextureZeroInitTests.cpp @@ -400,5 +400,7 @@ TEST_P(TextureZeroInitTest, DepthStencilClear) { } DAWN_INSTANTIATE_TEST(TextureZeroInitTest, + ForceWorkarounds(D3D12Backend, + {"nonzero_clear_resources_on_creation_for_testing"}), ForceWorkarounds(VulkanBackend, {"nonzero_clear_resources_on_creation_for_testing"}));