From 700cfe7664e645abbb9a4cd3651c5435215fa5d1 Mon Sep 17 00:00:00 2001 From: Brandon Jones Date: Tue, 12 Nov 2019 18:14:21 +0000 Subject: [PATCH] Implement D3D12 Native Render Passes Uses D3D12 native render pass API when possible. On pre-RS5 builds of Windows, Dawn will fall back to a software emulated render pass. A toggle was added to provide test coverage to the emulated render pass implementation and used in tests that test render pass functionality in particular. Bug: dawn:36 Change-Id: I297a3ec7655b68d28204db2d3ab78cb82bb4e7a5 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13082 Reviewed-by: Austin Eng Commit-Queue: Brandon Jones --- BUILD.gn | 2 + src/dawn_native/Toggles.cpp | 8 +- src/dawn_native/Toggles.h | 1 + src/dawn_native/d3d12/CommandBufferD3D12.cpp | 306 +++++++++++------- src/dawn_native/d3d12/CommandBufferD3D12.h | 17 +- .../d3d12/CommandRecordingContext.cpp | 12 + .../d3d12/CommandRecordingContext.h | 2 + src/dawn_native/d3d12/D3D12Info.cpp | 40 ++- src/dawn_native/d3d12/D3D12Info.h | 1 + src/dawn_native/d3d12/DeviceD3D12.cpp | 1 + .../d3d12/RenderPassBuilderD3D12.cpp | 229 +++++++++++++ .../d3d12/RenderPassBuilderD3D12.h | 89 +++++ .../end2end/MultisampledRenderingTests.cpp | 1 + src/tests/end2end/RenderPassTests.cpp | 7 +- src/tests/end2end/TextureZeroInitTests.cpp | 3 + 15 files changed, 581 insertions(+), 138 deletions(-) create mode 100644 src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp create mode 100644 src/dawn_native/d3d12/RenderPassBuilderD3D12.h diff --git a/BUILD.gn b/BUILD.gn index 6f157fd503..99742ed462 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -291,6 +291,8 @@ source_set("libdawn_native_sources") { "src/dawn_native/d3d12/PlatformFunctions.h", "src/dawn_native/d3d12/QueueD3D12.cpp", "src/dawn_native/d3d12/QueueD3D12.h", + "src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp", + "src/dawn_native/d3d12/RenderPassBuilderD3D12.h", "src/dawn_native/d3d12/RenderPipelineD3D12.cpp", "src/dawn_native/d3d12/RenderPipelineD3D12.h", "src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp", diff --git a/src/dawn_native/Toggles.cpp b/src/dawn_native/Toggles.cpp index d3d0e8df2f..f69ccd1a75 100644 --- a/src/dawn_native/Toggles.cpp +++ b/src/dawn_native/Toggles.cpp @@ -75,8 +75,12 @@ namespace dawn_native { {"use_d3d12_resource_heap_tier2", "Enable support for resource heap tier 2. Resource heap tier 2 allows mixing of " "texture and buffers in the same heap. This allows better heap re-use and reduces " - "fragmentation."}}}}; - + "fragmentation."}}, + {Toggle::UseD3D12RenderPass, + {"use_d3d12_render_pass", + "Use the D3D12 render pass API introduced in Windows build 1809 by default. On " + "versions of Windows prior to build 1809, or when this toggle is turned off, Dawn " + "will emulate a render pass."}}}}; } // anonymous namespace void TogglesSet::SetToggle(Toggle toggle, bool enabled) { diff --git a/src/dawn_native/Toggles.h b/src/dawn_native/Toggles.h index 12384504ec..73db7f9d02 100644 --- a/src/dawn_native/Toggles.h +++ b/src/dawn_native/Toggles.h @@ -31,6 +31,7 @@ namespace dawn_native { TurnOffVsync, UseTemporaryBufferInCompressedTextureToTextureCopy, UseD3D12ResourceHeapTier2, + UseD3D12RenderPass, EnumCount, InvalidEnum = EnumCount, diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index 61ff14afbd..ab794f82e2 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -28,6 +28,7 @@ #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/PipelineLayoutD3D12.h" #include "dawn_native/d3d12/PlatformFunctions.h" +#include "dawn_native/d3d12/RenderPassBuilderD3D12.h" #include "dawn_native/d3d12/RenderPipelineD3D12.h" #include "dawn_native/d3d12/SamplerD3D12.h" #include "dawn_native/d3d12/TextureCopySplitter.h" @@ -65,12 +66,6 @@ namespace dawn_native { namespace d3d12 { return false; } - struct OMSetRenderTargetArgs { - unsigned int numRTVs = 0; - std::array RTVs = {}; - D3D12_CPU_DESCRIPTOR_HANDLE dsv = {}; - }; - } // anonymous namespace class BindGroupStateTracker : public BindGroupAndStorageBarrierTrackerBase { @@ -601,11 +596,13 @@ namespace dawn_native { namespace d3d12 { // Records the necessary barriers for the resource usage pre-computed by the frontend auto TransitionForPass = [](CommandRecordingContext* commandContext, - const PassResourceUsage& usages) { + const PassResourceUsage& usages) -> bool { std::vector barriers; ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); + wgpu::BufferUsage bufferUsages = wgpu::BufferUsage::None; + for (size_t i = 0; i < usages.buffers.size(); ++i) { D3D12_RESOURCE_BARRIER barrier; if (ToBackend(usages.buffers[i]) @@ -613,6 +610,7 @@ namespace dawn_native { namespace d3d12 { usages.bufferUsages[i])) { barriers.push_back(barrier); } + bufferUsages |= usages.bufferUsages[i]; } for (size_t i = 0; i < usages.textures.size(); ++i) { @@ -627,6 +625,8 @@ namespace dawn_native { namespace d3d12 { } } + wgpu::TextureUsage textureUsages = wgpu::TextureUsage::None; + for (size_t i = 0; i < usages.textures.size(); ++i) { D3D12_RESOURCE_BARRIER barrier; if (ToBackend(usages.textures[i]) @@ -634,11 +634,15 @@ namespace dawn_native { namespace d3d12 { usages.textureUsages[i])) { barriers.push_back(barrier); } + textureUsages |= usages.textureUsages[i]; } if (barriers.size()) { commandList->ResourceBarrier(barriers.size(), barriers.data()); } + + return (bufferUsages & wgpu::BufferUsage::Storage || + textureUsages & wgpu::TextureUsage::Storage); }; const std::vector& passResourceUsages = GetResourceUsages().perPass; @@ -661,10 +665,11 @@ namespace dawn_native { namespace d3d12 { BeginRenderPassCmd* beginRenderPassCmd = mCommands.NextCommand(); - TransitionForPass(commandContext, passResourceUsages[nextPassNumber]); + const bool passHasUAV = + TransitionForPass(commandContext, passResourceUsages[nextPassNumber]); bindingTracker.SetInComputePass(false); RecordRenderPass(commandContext, &bindingTracker, &renderPassTracker, - beginRenderPassCmd); + beginRenderPassCmd, passHasUAV); nextPassNumber++; } break; @@ -912,126 +917,194 @@ namespace dawn_native { namespace d3d12 { } } - void CommandBuffer::RecordRenderPass(CommandRecordingContext* commandContext, - BindGroupStateTracker* bindingTracker, - RenderPassDescriptorHeapTracker* renderPassTracker, - BeginRenderPassCmd* renderPass) { - OMSetRenderTargetArgs args = renderPassTracker->GetSubpassOMSetRenderTargetArgs(renderPass); + void CommandBuffer::SetupRenderPass(CommandRecordingContext* commandContext, + BeginRenderPassCmd* renderPass, + RenderPassBuilder* renderPassBuilder) { + for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { + RenderPassColorAttachmentInfo& attachmentInfo = renderPass->colorAttachments[i]; + TextureView* view = ToBackend(attachmentInfo.view.Get()); + Texture* texture = ToBackend(view->GetTexture()); + + // Load operation is changed to clear when the texture is uninitialized. + if (!texture->IsSubresourceContentInitialized(view->GetBaseMipLevel(), 1, + view->GetBaseArrayLayer(), 1) && + attachmentInfo.loadOp == wgpu::LoadOp::Load) { + attachmentInfo.loadOp = wgpu::LoadOp::Clear; + attachmentInfo.clearColor = {0.0f, 0.0f, 0.0f, 0.0f}; + } + + // Set color load operation. + renderPassBuilder->SetRenderTargetBeginningAccess( + i, attachmentInfo.loadOp, attachmentInfo.clearColor, view->GetD3D12Format()); + + // Set color store operation. + if (attachmentInfo.resolveTarget.Get() != nullptr) { + TextureView* resolveDestinationView = ToBackend(attachmentInfo.resolveTarget.Get()); + Texture* resolveDestinationTexture = + ToBackend(resolveDestinationView->GetTexture()); + + resolveDestinationTexture->TransitionUsageNow(commandContext, + D3D12_RESOURCE_STATE_RESOLVE_DEST); + + // Mark resolve target as initialized to prevent clearing later. + resolveDestinationTexture->SetIsSubresourceContentInitialized( + true, resolveDestinationView->GetBaseMipLevel(), 1, + resolveDestinationView->GetBaseArrayLayer(), 1); + + renderPassBuilder->SetRenderTargetEndingAccessResolve(i, attachmentInfo.storeOp, + view, resolveDestinationView); + } else { + renderPassBuilder->SetRenderTargetEndingAccess(i, attachmentInfo.storeOp); + } + + // Set whether or not the texture requires initialization after the pass. + bool isInitialized = attachmentInfo.storeOp == wgpu::StoreOp::Store; + texture->SetIsSubresourceContentInitialized(isInitialized, view->GetBaseMipLevel(), 1, + view->GetBaseArrayLayer(), 1); + } + + if (renderPass->attachmentState->HasDepthStencilAttachment()) { + RenderPassDepthStencilAttachmentInfo& attachmentInfo = + renderPass->depthStencilAttachment; + TextureView* view = ToBackend(renderPass->depthStencilAttachment.view.Get()); + Texture* texture = ToBackend(view->GetTexture()); + + const bool hasDepth = view->GetTexture()->GetFormat().HasDepth(); + const bool hasStencil = view->GetTexture()->GetFormat().HasStencil(); + + // Load operations are changed to clear when the texture is uninitialized. + if (!view->GetTexture()->IsSubresourceContentInitialized( + view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(), + view->GetLayerCount())) { + if (hasDepth && attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) { + attachmentInfo.clearDepth = 0.0f; + attachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; + } + if (hasStencil && attachmentInfo.stencilLoadOp == wgpu::LoadOp::Load) { + attachmentInfo.clearStencil = 0u; + attachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; + } + } + + // Set depth/stencil load operations. + if (hasDepth) { + renderPassBuilder->SetDepthAccess( + attachmentInfo.depthLoadOp, attachmentInfo.depthStoreOp, + attachmentInfo.clearDepth, view->GetD3D12Format()); + } else { + renderPassBuilder->SetDepthNoAccess(); + } + + if (hasStencil) { + renderPassBuilder->SetStencilAccess( + attachmentInfo.stencilLoadOp, attachmentInfo.stencilStoreOp, + attachmentInfo.clearStencil, view->GetD3D12Format()); + } else { + renderPassBuilder->SetStencilNoAccess(); + } + + // Set whether or not the texture requires initialization. + ASSERT(!hasDepth || !hasStencil || + attachmentInfo.depthStoreOp == attachmentInfo.stencilStoreOp); + bool isInitialized = attachmentInfo.depthStoreOp == wgpu::StoreOp::Store; + texture->SetIsSubresourceContentInitialized(isInitialized, view->GetBaseMipLevel(), 1, + view->GetBaseArrayLayer(), 1); + } else { + renderPassBuilder->SetDepthStencilNoAccess(); + } + } + + void CommandBuffer::EmulateBeginRenderPass(CommandRecordingContext* commandContext, + const RenderPassBuilder* renderPassBuilder) const { ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); - // Clear framebuffer attachments as needed and transition to render target + // Clear framebuffer attachments as needed. { - for (uint32_t i : - IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { - auto& attachmentInfo = renderPass->colorAttachments[i]; - TextureView* view = ToBackend(attachmentInfo.view.Get()); - + for (uint32_t i = 0; i < renderPassBuilder->GetColorAttachmentCount(); i++) { // Load op - color - ASSERT(view->GetLevelCount() == 1); - ASSERT(view->GetLayerCount() == 1); - if (attachmentInfo.loadOp == wgpu::LoadOp::Clear || - (attachmentInfo.loadOp == wgpu::LoadOp::Load && - !view->GetTexture()->IsSubresourceContentInitialized( - view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1))) { - D3D12_CPU_DESCRIPTOR_HANDLE handle = args.RTVs[i]; - commandList->ClearRenderTargetView(handle, &attachmentInfo.clearColor.r, 0, - nullptr); - } - - TextureView* resolveView = ToBackend(attachmentInfo.resolveTarget.Get()); - if (resolveView != nullptr) { - // We need to set the resolve target to initialized so that it does not get - // cleared later in the pipeline. The texture will be resolved from the source - // color attachment, which will be correctly initialized. - ToBackend(resolveView->GetTexture()) - ->SetIsSubresourceContentInitialized( - true, resolveView->GetBaseMipLevel(), resolveView->GetLevelCount(), - resolveView->GetBaseArrayLayer(), resolveView->GetLayerCount()); - } - - switch (attachmentInfo.storeOp) { - case wgpu::StoreOp::Store: { - view->GetTexture()->SetIsSubresourceContentInitialized( - true, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1); - } break; - - case wgpu::StoreOp::Clear: { - view->GetTexture()->SetIsSubresourceContentInitialized( - false, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1); - } break; - - default: { UNREACHABLE(); } break; + if (renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i] + .BeginningAccess.Type == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) { + commandList->ClearRenderTargetView( + renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i].cpuDescriptor, + renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i] + .BeginningAccess.Clear.ClearValue.Color, + 0, nullptr); } } - if (renderPass->attachmentState->HasDepthStencilAttachment()) { - auto& attachmentInfo = renderPass->depthStencilAttachment; - Texture* texture = ToBackend(renderPass->depthStencilAttachment.view->GetTexture()); - TextureView* view = ToBackend(attachmentInfo.view.Get()); - float clearDepth = attachmentInfo.clearDepth; + if (renderPassBuilder->HasDepth()) { + D3D12_CLEAR_FLAGS clearFlags = {}; + float depthClear = 0.0f; + uint8_t stencilClear = 0u; + + if (renderPassBuilder->GetRenderPassDepthStencilDescriptor() + ->DepthBeginningAccess.Type == + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) { + clearFlags |= D3D12_CLEAR_FLAG_DEPTH; + depthClear = renderPassBuilder->GetRenderPassDepthStencilDescriptor() + ->DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth; + } + if (renderPassBuilder->GetRenderPassDepthStencilDescriptor() + ->StencilBeginningAccess.Type == + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) { + clearFlags |= D3D12_CLEAR_FLAG_STENCIL; + stencilClear = + renderPassBuilder->GetRenderPassDepthStencilDescriptor() + ->StencilBeginningAccess.Clear.ClearValue.DepthStencil.Stencil; + } + // TODO(kainino@chromium.org): investigate: should the Dawn clear // stencil type be uint8_t? - uint8_t clearStencil = static_cast(attachmentInfo.clearStencil); - - // Load op - depth/stencil - bool doDepthClear = texture->GetFormat().HasDepth() && - (attachmentInfo.depthLoadOp == wgpu::LoadOp::Clear); - bool doStencilClear = texture->GetFormat().HasStencil() && - (attachmentInfo.stencilLoadOp == wgpu::LoadOp::Clear); - - D3D12_CLEAR_FLAGS clearFlags = {}; - if (doDepthClear) { - clearFlags |= D3D12_CLEAR_FLAG_DEPTH; - } - if (doStencilClear) { - clearFlags |= D3D12_CLEAR_FLAG_STENCIL; - } - // If the depth stencil texture has not been initialized, we want to use loadop - // clear to init the contents to 0's - if (!texture->IsSubresourceContentInitialized( - view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(), - view->GetLayerCount())) { - if (texture->GetFormat().HasDepth() && - attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) { - clearDepth = 0.0f; - clearFlags |= D3D12_CLEAR_FLAG_DEPTH; - } - if (texture->GetFormat().HasStencil() && - attachmentInfo.stencilLoadOp == wgpu::LoadOp::Load) { - clearStencil = 0u; - clearFlags |= D3D12_CLEAR_FLAG_STENCIL; - } - } - if (clearFlags) { - D3D12_CPU_DESCRIPTOR_HANDLE handle = args.dsv; - commandList->ClearDepthStencilView(handle, clearFlags, clearDepth, clearStencil, - 0, nullptr); - } - - if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Store && - attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store) { - texture->SetIsSubresourceContentInitialized( - true, view->GetBaseMipLevel(), view->GetLevelCount(), - view->GetBaseArrayLayer(), view->GetLayerCount()); - } else if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Clear && - attachmentInfo.stencilStoreOp == wgpu::StoreOp::Clear) { - texture->SetIsSubresourceContentInitialized( - false, view->GetBaseMipLevel(), view->GetLevelCount(), - view->GetBaseArrayLayer(), view->GetLayerCount()); + commandList->ClearDepthStencilView( + renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor, + clearFlags, depthClear, stencilClear, 0, nullptr); } } } - // Set up render targets - { - if (args.dsv.ptr) { - commandList->OMSetRenderTargets(args.numRTVs, args.RTVs.data(), FALSE, &args.dsv); - } else { - commandList->OMSetRenderTargets(args.numRTVs, args.RTVs.data(), FALSE, nullptr); - } + commandList->OMSetRenderTargets( + renderPassBuilder->GetColorAttachmentCount(), renderPassBuilder->GetRenderTargetViews(), + FALSE, + renderPassBuilder->HasDepth() + ? &renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor + : nullptr); + } + + void CommandBuffer::RecordRenderPass( + CommandRecordingContext* commandContext, + BindGroupStateTracker* bindingTracker, + RenderPassDescriptorHeapTracker* renderPassDescriptorHeapTracker, + BeginRenderPassCmd* renderPass, + const bool passHasUAV) { + OMSetRenderTargetArgs args = + renderPassDescriptorHeapTracker->GetSubpassOMSetRenderTargetArgs(renderPass); + + const bool useRenderPass = GetDevice()->IsToggleEnabled(Toggle::UseD3D12RenderPass); + + // renderPassBuilder must be scoped to RecordRenderPass because any underlying + // D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS structs must remain + // valid until after EndRenderPass() has been called. + RenderPassBuilder renderPassBuilder(args, passHasUAV); + + SetupRenderPass(commandContext, renderPass, &renderPassBuilder); + + // Use D3D12's native render pass API if it's available, otherwise emulate the + // beginning and ending access operations. + if (useRenderPass) { + commandContext->GetCommandList4()->BeginRenderPass( + renderPassBuilder.GetColorAttachmentCount(), + renderPassBuilder.GetRenderPassRenderTargetDescriptors(), + renderPassBuilder.HasDepth() + ? renderPassBuilder.GetRenderPassDepthStencilDescriptor() + : nullptr, + renderPassBuilder.GetRenderPassFlags()); + } else { + EmulateBeginRenderPass(commandContext, &renderPassBuilder); } + ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); + // Set up default dynamic state { uint32_t width = renderPass->width; @@ -1189,10 +1262,9 @@ namespace dawn_native { namespace d3d12 { switch (type) { case Command::EndRenderPass: { mCommands.NextCommand(); - - // TODO(brandon1.jones@intel.com): avoid calling this function and enable MSAA - // resolve in D3D12 render pass on the platforms that support this feature. - if (renderPass->attachmentState->GetSampleCount() > 1) { + if (useRenderPass) { + commandContext->GetCommandList4()->EndRenderPass(); + } else if (renderPass->attachmentState->GetSampleCount() > 1) { ResolveMultisampledRenderPass(commandContext, renderPass); } return; diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.h b/src/dawn_native/d3d12/CommandBufferD3D12.h index 3e1a16f243..52f95fd59e 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.h +++ b/src/dawn_native/d3d12/CommandBufferD3D12.h @@ -31,10 +31,17 @@ namespace dawn_native { namespace dawn_native { namespace d3d12 { + struct OMSetRenderTargetArgs { + unsigned int numRTVs = 0; + std::array RTVs = {}; + D3D12_CPU_DESCRIPTOR_HANDLE dsv = {}; + }; + class BindGroupStateTracker; class CommandRecordingContext; class Device; class RenderPassDescriptorHeapTracker; + class RenderPassBuilder; class RenderPipeline; class CommandBuffer : public CommandBufferBase { @@ -49,8 +56,14 @@ namespace dawn_native { namespace d3d12 { BindGroupStateTracker* bindingTracker); void RecordRenderPass(CommandRecordingContext* commandContext, BindGroupStateTracker* bindingTracker, - RenderPassDescriptorHeapTracker* renderPassTracker, - BeginRenderPassCmd* renderPass); + RenderPassDescriptorHeapTracker* renderPassDescriptorHeapTracker, + BeginRenderPassCmd* renderPass, + bool passHasUAV); + void SetupRenderPass(CommandRecordingContext* commandContext, + BeginRenderPassCmd* renderPass, + RenderPassBuilder* renderPassBuilder); + void EmulateBeginRenderPass(CommandRecordingContext* commandContext, + const RenderPassBuilder* renderPassBuilder) const; CommandIterator mCommands; }; diff --git a/src/dawn_native/d3d12/CommandRecordingContext.cpp b/src/dawn_native/d3d12/CommandRecordingContext.cpp index 76ea3b0880..209009c101 100644 --- a/src/dawn_native/d3d12/CommandRecordingContext.cpp +++ b/src/dawn_native/d3d12/CommandRecordingContext.cpp @@ -41,6 +41,9 @@ namespace dawn_native { namespace d3d12 { nullptr, IID_PPV_ARGS(&d3d12GraphicsCommandList)), "D3D12 creating direct command list")); mD3d12CommandList = std::move(d3d12GraphicsCommandList); + // Store a cast to ID3D12GraphicsCommandList4. This is required to use the D3D12 render + // pass APIs introduced in Windows build 1809. + mD3d12CommandList.As(&mD3d12CommandList4); } mIsOpen = true; @@ -80,8 +83,17 @@ namespace dawn_native { namespace d3d12 { return mD3d12CommandList.Get(); } + // This function will fail on Windows versions prior to 1809. Support must be queried through + // the device before calling. + ID3D12GraphicsCommandList4* CommandRecordingContext::GetCommandList4() const { + ASSERT(IsOpen()); + ASSERT(mD3d12CommandList.Get() != nullptr); + return mD3d12CommandList4.Get(); + } + void CommandRecordingContext::Release() { mD3d12CommandList.Reset(); + mD3d12CommandList4.Reset(); mIsOpen = false; mSharedTextures.clear(); } diff --git a/src/dawn_native/d3d12/CommandRecordingContext.h b/src/dawn_native/d3d12/CommandRecordingContext.h index a0b0c83837..d501d59692 100644 --- a/src/dawn_native/d3d12/CommandRecordingContext.h +++ b/src/dawn_native/d3d12/CommandRecordingContext.h @@ -31,6 +31,7 @@ namespace dawn_native { namespace d3d12 { CommandAllocatorManager* commandAllocationManager); ID3D12GraphicsCommandList* GetCommandList() const; + ID3D12GraphicsCommandList4* GetCommandList4() const; void Release(); bool IsOpen() const; @@ -38,6 +39,7 @@ namespace dawn_native { namespace d3d12 { private: ComPtr mD3d12CommandList; + ComPtr mD3d12CommandList4; bool mIsOpen = false; std::set mSharedTextures; }; diff --git a/src/dawn_native/d3d12/D3D12Info.cpp b/src/dawn_native/d3d12/D3D12Info.cpp index edfeb82dae..de9bd0369d 100644 --- a/src/dawn_native/d3d12/D3D12Info.cpp +++ b/src/dawn_native/d3d12/D3D12Info.cpp @@ -24,25 +24,33 @@ namespace dawn_native { namespace d3d12 { ResultOrError GatherDeviceInfo(const Adapter& adapter) { D3D12DeviceInfo info = {}; - // Gather info about device memory - { - // Newer builds replace D3D_FEATURE_DATA_ARCHITECTURE with - // D3D_FEATURE_DATA_ARCHITECTURE1. However, D3D_FEATURE_DATA_ARCHITECTURE can be used - // for backwards compat. - // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ne-d3d12-d3d12_feature - D3D12_FEATURE_DATA_ARCHITECTURE arch = {}; - DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport( - D3D12_FEATURE_ARCHITECTURE, &arch, sizeof(arch)), - "ID3D12Device::CheckFeatureSupport")); + // Newer builds replace D3D_FEATURE_DATA_ARCHITECTURE with + // D3D_FEATURE_DATA_ARCHITECTURE1. However, D3D_FEATURE_DATA_ARCHITECTURE can be used + // for backwards compat. + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ne-d3d12-d3d12_feature + D3D12_FEATURE_DATA_ARCHITECTURE arch = {}; + DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, + &arch, sizeof(arch)), + "ID3D12Device::CheckFeatureSupport")); - info.isUMA = arch.UMA; + info.isUMA = arch.UMA; - D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; - DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport( - D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)), - "ID3D12Device::CheckFeatureSupport")); + D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; + DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, + &options, sizeof(options)), + "ID3D12Device::CheckFeatureSupport")); - info.resourceHeapTier = options.ResourceHeapTier; + info.resourceHeapTier = options.ResourceHeapTier; + + // Windows builds 1809 and above can use the D3D12 render pass API. If we query + // CheckFeatureSupport for D3D12_FEATURE_D3D12_OPTIONS5 successfully, then we can use + // the render pass API. + D3D12_FEATURE_DATA_D3D12_OPTIONS5 featureOptions5 = {}; + if (SUCCEEDED(adapter.GetDevice()->CheckFeatureSupport( + D3D12_FEATURE_D3D12_OPTIONS5, &featureOptions5, sizeof(featureOptions5)))) { + info.supportsRenderPass = true; + } else { + info.supportsRenderPass = false; } return info; diff --git a/src/dawn_native/d3d12/D3D12Info.h b/src/dawn_native/d3d12/D3D12Info.h index 1471be00d9..78d3820816 100644 --- a/src/dawn_native/d3d12/D3D12Info.h +++ b/src/dawn_native/d3d12/D3D12Info.h @@ -25,6 +25,7 @@ namespace dawn_native { namespace d3d12 { struct D3D12DeviceInfo { bool isUMA; uint32_t resourceHeapTier; + bool supportsRenderPass; }; ResultOrError GatherDeviceInfo(const Adapter& adapter); diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp index 4a384a03a6..7ce3a3e7a5 100644 --- a/src/dawn_native/d3d12/DeviceD3D12.cpp +++ b/src/dawn_native/d3d12/DeviceD3D12.cpp @@ -414,6 +414,7 @@ namespace dawn_native { namespace d3d12 { void Device::InitTogglesFromDriver() { const bool useResourceHeapTier2 = (GetDeviceInfo().resourceHeapTier >= 2); SetToggle(Toggle::UseD3D12ResourceHeapTier2, useResourceHeapTier2); + SetToggle(Toggle::UseD3D12RenderPass, GetDeviceInfo().supportsRenderPass); } }} // namespace dawn_native::d3d12 diff --git a/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp b/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp new file mode 100644 index 0000000000..e535a66a16 --- /dev/null +++ b/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp @@ -0,0 +1,229 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/RenderPassBuilderD3D12.h" + +#include "dawn_native/Format.h" +#include "dawn_native/d3d12/CommandBufferD3D12.h" +#include "dawn_native/d3d12/Forward.h" +#include "dawn_native/d3d12/TextureD3D12.h" + +#include "dawn_native/dawn_platform.h" + +namespace dawn_native { namespace d3d12 { + + namespace { + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE D3D12BeginningAccessType(wgpu::LoadOp loadOp) { + switch (loadOp) { + case wgpu::LoadOp::Clear: + return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR; + case wgpu::LoadOp::Load: + return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE; + default: + UNREACHABLE(); + } + } + + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE D3D12EndingAccessType(wgpu::StoreOp storeOp) { + switch (storeOp) { + case wgpu::StoreOp::Clear: + return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD; + case wgpu::StoreOp::Store: + return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE; + default: + UNREACHABLE(); + } + } + + D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS D3D12EndingAccessResolveParameters( + wgpu::StoreOp storeOp, + TextureView* resolveSource, + TextureView* resolveDestination) { + D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS resolveParameters; + + resolveParameters.Format = resolveDestination->GetD3D12Format(); + resolveParameters.pSrcResource = + ToBackend(resolveSource->GetTexture())->GetD3D12Resource(); + resolveParameters.pDstResource = + ToBackend(resolveDestination->GetTexture())->GetD3D12Resource(); + + // Clear or preserve the resolve source. + if (storeOp == wgpu::StoreOp::Clear) { + resolveParameters.PreserveResolveSource = false; + } else if (storeOp == wgpu::StoreOp::Store) { + resolveParameters.PreserveResolveSource = true; + } + + // RESOLVE_MODE_AVERAGE is only valid for non-integer formats. + // TODO: Investigate and determine how integer format resolves should work in WebGPU. + switch (resolveDestination->GetFormat().type) { + case Format::Type::Sint: + case Format::Type::Uint: + resolveParameters.ResolveMode = D3D12_RESOLVE_MODE_MAX; + break; + default: + resolveParameters.ResolveMode = D3D12_RESOLVE_MODE_AVERAGE; + break; + } + + resolveParameters.SubresourceCount = 1; + + return resolveParameters; + } + + D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS + D3D12EndingAccessResolveSubresourceParameters(TextureView* resolveDestination) { + D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS subresourceParameters; + Texture* resolveDestinationTexture = ToBackend(resolveDestination->GetTexture()); + + subresourceParameters.DstX = 0; + subresourceParameters.DstY = 0; + subresourceParameters.SrcSubresource = 0; + subresourceParameters.DstSubresource = resolveDestinationTexture->GetSubresourceIndex( + resolveDestination->GetBaseMipLevel(), resolveDestination->GetBaseArrayLayer()); + subresourceParameters.SrcRect = {0, 0, resolveDestinationTexture->GetSize().width, + resolveDestinationTexture->GetSize().height}; + + return subresourceParameters; + } + } // anonymous namespace + + RenderPassBuilder::RenderPassBuilder(const OMSetRenderTargetArgs& args, bool hasUAV) + : mColorAttachmentCount(args.numRTVs), mRenderTargetViews(args.RTVs.data()) { + for (uint32_t i = 0; i < mColorAttachmentCount; i++) { + mRenderPassRenderTargetDescriptors[i].cpuDescriptor = args.RTVs[i]; + } + + mRenderPassDepthStencilDesc.cpuDescriptor = args.dsv; + + if (hasUAV) { + mRenderPassFlags = D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES; + } + } + + uint32_t RenderPassBuilder::GetColorAttachmentCount() const { + return mColorAttachmentCount; + } + + bool RenderPassBuilder::HasDepth() const { + return mHasDepth; + } + + const D3D12_RENDER_PASS_RENDER_TARGET_DESC* + RenderPassBuilder::GetRenderPassRenderTargetDescriptors() const { + return mRenderPassRenderTargetDescriptors.data(); + } + + const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC* + RenderPassBuilder::GetRenderPassDepthStencilDescriptor() const { + return &mRenderPassDepthStencilDesc; + } + + D3D12_RENDER_PASS_FLAGS RenderPassBuilder::GetRenderPassFlags() const { + return mRenderPassFlags; + } + + const D3D12_CPU_DESCRIPTOR_HANDLE* RenderPassBuilder::GetRenderTargetViews() const { + return mRenderTargetViews; + } + + void RenderPassBuilder::SetRenderTargetBeginningAccess(uint32_t attachment, + wgpu::LoadOp loadOp, + dawn_native::Color clearColor, + DXGI_FORMAT format) { + mRenderPassRenderTargetDescriptors[attachment].BeginningAccess.Type = + D3D12BeginningAccessType(loadOp); + if (loadOp == wgpu::LoadOp::Clear) { + mRenderPassRenderTargetDescriptors[attachment] + .BeginningAccess.Clear.ClearValue.Color[0] = clearColor.r; + mRenderPassRenderTargetDescriptors[attachment] + .BeginningAccess.Clear.ClearValue.Color[1] = clearColor.g; + mRenderPassRenderTargetDescriptors[attachment] + .BeginningAccess.Clear.ClearValue.Color[2] = clearColor.b; + mRenderPassRenderTargetDescriptors[attachment] + .BeginningAccess.Clear.ClearValue.Color[3] = clearColor.a; + mRenderPassRenderTargetDescriptors[attachment].BeginningAccess.Clear.ClearValue.Format = + format; + } + } + + void RenderPassBuilder::SetRenderTargetEndingAccess(uint32_t attachment, + wgpu::StoreOp storeOp) { + mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Type = + D3D12EndingAccessType(storeOp); + } + + void RenderPassBuilder::SetRenderTargetEndingAccessResolve(uint32_t attachment, + wgpu::StoreOp storeOp, + TextureView* resolveSource, + TextureView* resolveDestination) { + mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Type = + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_RESOLVE; + mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Resolve = + D3D12EndingAccessResolveParameters(storeOp, resolveSource, resolveDestination); + + mSubresourceParams[attachment] = + D3D12EndingAccessResolveSubresourceParameters(resolveDestination); + + mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Resolve.pSubresourceParameters = + &mSubresourceParams[attachment]; + } + + void RenderPassBuilder::SetDepthAccess(wgpu::LoadOp loadOp, + wgpu::StoreOp storeOp, + float clearDepth, + DXGI_FORMAT format) { + mHasDepth = true; + mRenderPassDepthStencilDesc.DepthBeginningAccess.Type = D3D12BeginningAccessType(loadOp); + if (loadOp == wgpu::LoadOp::Clear) { + mRenderPassDepthStencilDesc.DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth = + clearDepth; + mRenderPassDepthStencilDesc.DepthBeginningAccess.Clear.ClearValue.Format = format; + } + mRenderPassDepthStencilDesc.DepthEndingAccess.Type = D3D12EndingAccessType(storeOp); + } + + void RenderPassBuilder::SetStencilAccess(wgpu::LoadOp loadOp, + wgpu::StoreOp storeOp, + uint8_t clearStencil, + DXGI_FORMAT format) { + mRenderPassDepthStencilDesc.StencilBeginningAccess.Type = D3D12BeginningAccessType(loadOp); + if (loadOp == wgpu::LoadOp::Clear) { + mRenderPassDepthStencilDesc.StencilBeginningAccess.Clear.ClearValue.DepthStencil + .Stencil = clearStencil; + mRenderPassDepthStencilDesc.StencilBeginningAccess.Clear.ClearValue.Format = format; + } + mRenderPassDepthStencilDesc.StencilEndingAccess.Type = D3D12EndingAccessType(storeOp); + } + + void RenderPassBuilder::SetDepthNoAccess() { + mRenderPassDepthStencilDesc.DepthBeginningAccess.Type = + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS; + mRenderPassDepthStencilDesc.DepthEndingAccess.Type = + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS; + } + + void RenderPassBuilder::SetDepthStencilNoAccess() { + SetDepthNoAccess(); + SetStencilNoAccess(); + } + + void RenderPassBuilder::SetStencilNoAccess() { + mRenderPassDepthStencilDesc.StencilBeginningAccess.Type = + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS; + mRenderPassDepthStencilDesc.StencilEndingAccess.Type = + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS; + } + +}} // namespace dawn_native::d3d12 \ No newline at end of file diff --git a/src/dawn_native/d3d12/RenderPassBuilderD3D12.h b/src/dawn_native/d3d12/RenderPassBuilderD3D12.h new file mode 100644 index 0000000000..1ecd87e3ce --- /dev/null +++ b/src/dawn_native/d3d12/RenderPassBuilderD3D12.h @@ -0,0 +1,89 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_RENDERPASSBUILDERD3D12_H_ +#define DAWNNATIVE_D3D12_RENDERPASSBUILDERD3D12_H_ + +#include "common/Constants.h" +#include "dawn_native/d3d12/d3d12_platform.h" +#include "dawn_native/dawn_platform.h" + +#include + +namespace dawn_native { namespace d3d12 { + + class TextureView; + + struct OMSetRenderTargetArgs; + + // RenderPassBuilder stores parameters related to render pass load and store operations. + // When the D3D12 render pass API is available, the needed descriptors can be fetched + // directly from the RenderPassBuilder. When the D3D12 render pass API is not available, the + // descriptors are still fetched and any information necessary to emulate the load and store + // operations is extracted from the descriptors. + class RenderPassBuilder { + public: + RenderPassBuilder(const OMSetRenderTargetArgs& args, bool hasUAV); + + uint32_t GetColorAttachmentCount() const; + + // Returns descriptors that are fed directly to BeginRenderPass, or are used as parameter + // storage if D3D12 render pass API is unavailable. + const D3D12_RENDER_PASS_RENDER_TARGET_DESC* GetRenderPassRenderTargetDescriptors() const; + const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC* GetRenderPassDepthStencilDescriptor() const; + + D3D12_RENDER_PASS_FLAGS GetRenderPassFlags() const; + + // Returns attachment RTVs to use with OMSetRenderTargets. + const D3D12_CPU_DESCRIPTOR_HANDLE* GetRenderTargetViews() const; + + bool HasDepth() const; + + // Functions that set the appropriate values in the render pass descriptors. + void SetDepthAccess(wgpu::LoadOp loadOp, + wgpu::StoreOp storeOp, + float clearDepth, + DXGI_FORMAT format); + void SetDepthNoAccess(); + void SetDepthStencilNoAccess(); + void SetRenderTargetBeginningAccess(uint32_t attachment, + wgpu::LoadOp loadOp, + dawn_native::Color clearColor, + DXGI_FORMAT format); + void SetRenderTargetEndingAccess(uint32_t attachment, wgpu::StoreOp storeOp); + void SetRenderTargetEndingAccessResolve(uint32_t attachment, + wgpu::StoreOp storeOp, + TextureView* resolveSource, + TextureView* resolveDestination); + void SetStencilAccess(wgpu::LoadOp loadOp, + wgpu::StoreOp storeOp, + uint8_t clearStencil, + DXGI_FORMAT format); + void SetStencilNoAccess(); + + private: + uint32_t mColorAttachmentCount = 0; + bool mHasDepth = false; + D3D12_RENDER_PASS_FLAGS mRenderPassFlags = D3D12_RENDER_PASS_FLAG_NONE; + D3D12_RENDER_PASS_DEPTH_STENCIL_DESC mRenderPassDepthStencilDesc; + std::array + mRenderPassRenderTargetDescriptors; + const D3D12_CPU_DESCRIPTOR_HANDLE* mRenderTargetViews; + std::array + mSubresourceParams; + }; +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_RENDERPASSBUILDERD3D12_H_ \ No newline at end of file diff --git a/src/tests/end2end/MultisampledRenderingTests.cpp b/src/tests/end2end/MultisampledRenderingTests.cpp index cd1b0eebf3..1097391059 100644 --- a/src/tests/end2end/MultisampledRenderingTests.cpp +++ b/src/tests/end2end/MultisampledRenderingTests.cpp @@ -506,6 +506,7 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) { DAWN_INSTANTIATE_TEST(MultisampledRenderingTest, D3D12Backend, ForceWorkarounds(D3D12Backend, {}, {"use_d3d12_resource_heap_tier2"}), + ForceWorkarounds(D3D12Backend, {}, {"use_d3d12_render_pass"}), MetalBackend, OpenGLBackend, VulkanBackend, diff --git a/src/tests/end2end/RenderPassTests.cpp b/src/tests/end2end/RenderPassTests.cpp index c17af18dc4..6d7660fbe0 100644 --- a/src/tests/end2end/RenderPassTests.cpp +++ b/src/tests/end2end/RenderPassTests.cpp @@ -171,4 +171,9 @@ TEST_P(RenderPassTest, NoCorrespondingFragmentShaderOutputs) { EXPECT_PIXEL_RGBA8_EQ(kRed, renderTarget, kRTSize - 1, 1); } -DAWN_INSTANTIATE_TEST(RenderPassTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(RenderPassTest, + D3D12Backend, + ForceWorkarounds(D3D12Backend, {}, {"use_d3d12_render_pass"}), + MetalBackend, + OpenGLBackend, + VulkanBackend); diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp index f19e316a00..2f68329f48 100644 --- a/src/tests/end2end/TextureZeroInitTests.cpp +++ b/src/tests/end2end/TextureZeroInitTests.cpp @@ -779,5 +779,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencilStoreOpClear) { DAWN_INSTANTIATE_TEST( TextureZeroInitTest, ForceWorkarounds(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}), + ForceWorkarounds(D3D12Backend, + {"nonzero_clear_resources_on_creation_for_testing"}, + {"use_d3d12_render_pass"}), ForceWorkarounds(OpenGLBackend, {"nonzero_clear_resources_on_creation_for_testing"}), ForceWorkarounds(VulkanBackend, {"nonzero_clear_resources_on_creation_for_testing"}));