From cb9b2f9fdd8960d38086a4ccdee29f97eadb2a3f Mon Sep 17 00:00:00 2001 From: Yunchao He Date: Tue, 2 Nov 2021 19:53:09 +0000 Subject: [PATCH] Implement readonly depth/stencil attachment on D3D12 This change adds a end2end test for readonly depth/stencil attachment and implements it on D3D12. This change focuses on depth. If it is OK, I will add test(s) for stencil. The key points are: - Set DEPTH_READ transition barrier to replace DEPTH_WRITE barrier if it is readonly depth/stencil attachment. - Set appropriate D3D12_DEPTH_STENCIL_DESC (which is already correct) and D3D12_DEPTH_STENCIL_VIEW_DESC (which needs some particular flags). Otherwise, the underlying driver (validation layer) may think we still need DEPTH_WRITE transition barrier. Bug: dawn:485 Change-Id: I64d30426ed8042a98b3fef084bb90b125320a6f8 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/67742 Commit-Queue: Yunchao He Reviewed-by: Corentin Wallez --- src/dawn_native/CommandEncoder.cpp | 4 + src/dawn_native/Commands.h | 2 + src/dawn_native/d3d12/CommandBufferD3D12.cpp | 3 +- src/dawn_native/d3d12/TextureD3D12.cpp | 25 ++- src/dawn_native/d3d12/TextureD3D12.h | 8 +- src/tests/BUILD.gn | 1 + .../ReadOnlyDepthStencilAttachmentTests.cpp | 150 ++++++++++++++++++ src/utils/TextureUtils.h | 8 + 8 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 src/tests/end2end/ReadOnlyDepthStencilAttachmentTests.cpp diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp index 153ef7b721..2137828d2a 100644 --- a/src/dawn_native/CommandEncoder.cpp +++ b/src/dawn_native/CommandEncoder.cpp @@ -589,6 +589,10 @@ namespace dawn_native { descriptor->depthStencilAttachment->stencilLoadOp; cmd->depthStencilAttachment.stencilStoreOp = descriptor->depthStencilAttachment->stencilStoreOp; + cmd->depthStencilAttachment.depthReadOnly = + descriptor->depthStencilAttachment->depthReadOnly; + cmd->depthStencilAttachment.stencilReadOnly = + descriptor->depthStencilAttachment->stencilReadOnly; if (IsReadOnlyDepthStencilAttachment(descriptor->depthStencilAttachment)) { // TODO(dawn:485): Readonly depth/stencil attachment is not fully diff --git a/src/dawn_native/Commands.h b/src/dawn_native/Commands.h index cee9bbd52e..1e71828ea0 100644 --- a/src/dawn_native/Commands.h +++ b/src/dawn_native/Commands.h @@ -90,6 +90,8 @@ namespace dawn_native { wgpu::StoreOp stencilStoreOp; float clearDepth; uint32_t clearStencil; + bool depthReadOnly; + bool stencilReadOnly; }; struct BeginRenderPassCmd { diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index 445c7810b1..f50c8a49d7 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -1223,7 +1223,8 @@ namespace dawn_native { namespace d3d12 { dsvAllocation, device->GetDepthStencilViewAllocator()->AllocateTransientCPUDescriptors()); - const D3D12_DEPTH_STENCIL_VIEW_DESC viewDesc = view->GetDSVDescriptor(); + const D3D12_DEPTH_STENCIL_VIEW_DESC viewDesc = view->GetDSVDescriptor( + attachmentInfo.depthReadOnly, attachmentInfo.stencilReadOnly); const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = dsvAllocation.GetBaseDescriptor(); device->GetD3D12Device()->CreateDepthStencilView( diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index 59f9424f03..362a5dcca7 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -65,6 +65,11 @@ namespace dawn_native { namespace d3d12 { } } + if (usage & kReadOnlyRenderAttachment) { + // There is no STENCIL_READ state. Readonly for stencil is bundled with DEPTH_READ. + resourceState |= D3D12_RESOURCE_STATE_DEPTH_READ; + } + return resourceState; } @@ -944,10 +949,19 @@ namespace dawn_native { namespace d3d12 { D3D12_DEPTH_STENCIL_VIEW_DESC Texture::GetDSVDescriptor(uint32_t mipLevel, uint32_t baseArrayLayer, - uint32_t layerCount) const { + uint32_t layerCount, + Aspect aspects, + bool depthReadOnly, + bool stencilReadOnly) const { D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; dsvDesc.Format = GetD3D12Format(); dsvDesc.Flags = D3D12_DSV_FLAG_NONE; + if (depthReadOnly && aspects & Aspect::Depth) { + dsvDesc.Flags |= D3D12_DSV_FLAG_READ_ONLY_DEPTH; + } + if (stencilReadOnly && aspects & Aspect::Stencil) { + dsvDesc.Flags |= D3D12_DSV_FLAG_READ_ONLY_STENCIL; + } if (IsMultisampledTexture()) { ASSERT(GetNumMipLevels() == 1); @@ -1014,7 +1028,8 @@ namespace dawn_native { namespace d3d12 { device->GetDepthStencilViewAllocator()->AllocateTransientCPUDescriptors()); const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = dsvHandle.GetBaseDescriptor(); - D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = GetDSVDescriptor(level, layer, 1); + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = + GetDSVDescriptor(level, layer, 1, range.aspects, false, false); device->GetD3D12Device()->CreateDepthStencilView(GetD3D12Resource(), &dsvDesc, baseDescriptor); @@ -1279,10 +1294,12 @@ namespace dawn_native { namespace d3d12 { ->GetRTVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount()); } - D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() const { + D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor(bool depthReadOnly, + bool stencilReadOnly) const { ASSERT(GetLevelCount() == 1); return ToBackend(GetTexture()) - ->GetDSVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount()); + ->GetDSVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount(), + GetAspects(), depthReadOnly, stencilReadOnly); } D3D12_UNORDERED_ACCESS_VIEW_DESC TextureView::GetUAVDescriptor() const { diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h index c414a8ae61..4d49d6fa9f 100644 --- a/src/dawn_native/d3d12/TextureD3D12.h +++ b/src/dawn_native/d3d12/TextureD3D12.h @@ -62,7 +62,10 @@ namespace dawn_native { namespace d3d12 { uint32_t sliceCount) const; D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(uint32_t mipLevel, uint32_t baseArrayLayer, - uint32_t layerCount) const; + uint32_t layerCount, + Aspect aspects, + bool depthReadOnly, + bool stencilReadOnly) const; void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext, const SubresourceRange& range); @@ -147,7 +150,8 @@ namespace dawn_native { namespace d3d12 { const D3D12_SHADER_RESOURCE_VIEW_DESC& GetSRVDescriptor() const; D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor() const; - D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor() const; + D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(bool depthReadOnly, + bool stencilReadOnly) const; D3D12_UNORDERED_ACCESS_VIEW_DESC GetUAVDescriptor() const; private: diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn index 1fb6de0d5d..8ac2c9f542 100644 --- a/src/tests/BUILD.gn +++ b/src/tests/BUILD.gn @@ -383,6 +383,7 @@ source_set("dawn_end2end_tests_sources") { "end2end/QueryTests.cpp", "end2end/QueueTests.cpp", "end2end/QueueTimelineTests.cpp", + "end2end/ReadOnlyDepthStencilAttachmentTests.cpp", "end2end/RenderAttachmentTests.cpp", "end2end/RenderBundleTests.cpp", "end2end/RenderPassLoadOpTests.cpp", diff --git a/src/tests/end2end/ReadOnlyDepthStencilAttachmentTests.cpp b/src/tests/end2end/ReadOnlyDepthStencilAttachmentTests.cpp new file mode 100644 index 0000000000..d76f15b754 --- /dev/null +++ b/src/tests/end2end/ReadOnlyDepthStencilAttachmentTests.cpp @@ -0,0 +1,150 @@ +// Copyright 2021 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 "tests/DawnTest.h" + +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/TextureUtils.h" +#include "utils/WGPUHelpers.h" + +constexpr static uint32_t kSize = 4; + +namespace { + using TextureFormat = wgpu::TextureFormat; + DAWN_TEST_PARAM_STRUCT(ReadOnlyDepthStencilAttachmentTestsParams, TextureFormat) +} // namespace + +class ReadOnlyDepthStencilAttachmentTests + : public DawnTestWithParams { + protected: + wgpu::RenderPipeline CreateRenderPipeline(wgpu::TextureFormat format) { + utils::ComboRenderPipelineDescriptor pipelineDescriptor; + + // Draw a rectangle via two triangles. The depth value of the top of the rectangle is 0.4. + // The depth value of the bottom is 0.0. The depth value gradually change from 0.4 to 0.0 + // from the top to the bottom. The top part will compare with the depth values and fail to + // pass the depth test. The bottom part will compare with the depth values in depth buffer + // and pass the depth test, and sample from the depth buffer in fragment shader in the same + // pipeline. + pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"( + [[stage(vertex)]] + fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4 { + var pos = array, 6>( + vec3(-1.0, 1.0, 0.4), + vec3(-1.0, -1.0, 0.0), + vec3( 1.0, 1.0, 0.4), + vec3( 1.0, 1.0, 0.4), + vec3(-1.0, -1.0, 0.0), + vec3( 1.0, -1.0, 0.0)); + return vec4(pos[VertexIndex], 1.0); + })"); + + pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"( + [[group(0), binding(0)]] var samp : sampler; + [[group(0), binding(1)]] var tex : texture_depth_2d; + + [[stage(fragment)]] + fn main([[builtin(position)]] FragCoord : vec4) -> [[location(0)]] vec4 { + return vec4(textureSample(tex, samp, FragCoord.xy), 0.0, 0.0, 0.0); + })"); + + // Enable depth test. But depth write is not enabled. + wgpu::DepthStencilState* depthStencil = pipelineDescriptor.EnableDepthStencil(format); + depthStencil->depthCompare = wgpu::CompareFunction::LessEqual; + + return device.CreateRenderPipeline(&pipelineDescriptor); + } + + wgpu::Texture CreateTexture(wgpu::TextureFormat format, wgpu::TextureUsage usage) { + wgpu::TextureDescriptor descriptor = {}; + descriptor.size = {kSize, kSize, 1}; + descriptor.format = format; + descriptor.usage = usage; + return device.CreateTexture(&descriptor); + } + + void TestDepth(wgpu::TextureFormat format, wgpu::Texture colorTexture) { + wgpu::Texture depthStencilTexture = CreateTexture( + format, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding); + + // Note that we can only select one single aspect for texture view used in pipeline. + wgpu::TextureViewDescriptor viewDesc = {}; + viewDesc.aspect = wgpu::TextureAspect::DepthOnly; + wgpu::TextureView depthStencilViewInPipeline = depthStencilTexture.CreateView(&viewDesc); + + wgpu::Sampler sampler = device.CreateSampler(); + + wgpu::RenderPipeline pipeline = CreateRenderPipeline(format); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, depthStencilViewInPipeline}}); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + + // Create a render pass to initialize the depth attachment. + // Note that we must encompass all aspects for texture view used in attachment. + wgpu::TextureView depthStencilViewInAttachment = depthStencilTexture.CreateView(); + utils::ComboRenderPassDescriptor passDescriptorInit({}, depthStencilViewInAttachment); + passDescriptorInit.cDepthStencilAttachmentInfo.clearDepth = 0.2; + wgpu::RenderPassEncoder passInit = commandEncoder.BeginRenderPass(&passDescriptorInit); + passInit.EndPass(); + + // Create a render pass with readonly depth attachment. The readonly depth attachment + // has already been initialized. The pipeline in this render pass will sample from the + // depth attachment. The pipeline will read from the depth attachment to do depth test too. + utils::ComboRenderPassDescriptor passDescriptor({colorTexture.CreateView()}, + depthStencilViewInAttachment); + passDescriptor.cDepthStencilAttachmentInfo.depthReadOnly = true; + passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + passDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + // Set stencilReadOnly if the format has both depth and stencil aspects. + if (format == wgpu::TextureFormat::Depth24PlusStencil8) { + passDescriptor.cDepthStencilAttachmentInfo.stencilReadOnly = true; + passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + passDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + } + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.EndPass(); + + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + } +}; + +TEST_P(ReadOnlyDepthStencilAttachmentTests, Depth) { + wgpu::Texture colorTexture = + CreateTexture(wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc); + + wgpu::TextureFormat depthStencilFormat = GetParam().mTextureFormat; + TestDepth(depthStencilFormat, colorTexture); + + // The top part is not rendered by the pipeline. Its color is the default clear color for + // color attachment. + const std::vector kExpectedTopColors(kSize * kSize / 2, {0, 0, 0, 0}); + // The bottom part is rendered, whose red channel is sampled from depth attachment, which + // is initialized into 0.2. + const std::vector kExpectedBottomColors(kSize * kSize / 2, + {static_cast(0.2 * 255), 0, 0, 0}); + EXPECT_TEXTURE_EQ(kExpectedTopColors.data(), colorTexture, {0, 0}, {kSize, kSize / 2}); + EXPECT_TEXTURE_EQ(kExpectedBottomColors.data(), colorTexture, {0, kSize / 2}, + {kSize, kSize / 2}); +} + +DAWN_INSTANTIATE_TEST_P(ReadOnlyDepthStencilAttachmentTests, + {D3D12Backend()}, + std::vector(utils::kDepthStencilFormats.begin(), + utils::kDepthStencilFormats.end())); diff --git a/src/utils/TextureUtils.h b/src/utils/TextureUtils.h index 9c570c203d..0ea797f0a6 100644 --- a/src/utils/TextureUtils.h +++ b/src/utils/TextureUtils.h @@ -179,6 +179,14 @@ namespace utils { kBCFormats.size() + kETC2Formats.size() + kASTCFormats.size(), "Number of compressed format must equal number of BC, ETC2, and ASTC formats."); + // TODO(dawn:666, 570, 690): Add more depth/stencil formats if Stencil8, Depth16Unorm, + // Depth24UnormStencil8, Depth32FloatStencil8 are implemented. + static constexpr std::array kDepthStencilFormats = { + wgpu::TextureFormat::Depth32Float, + wgpu::TextureFormat::Depth24Plus, + wgpu::TextureFormat::Depth24PlusStencil8, + }; + bool TextureFormatSupportsStorageTexture(wgpu::TextureFormat format); bool IsBCTextureFormat(wgpu::TextureFormat textureFormat);