// Copyright 2020 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 #include "common/Constants.h" #include "common/Math.h" #include "utils/ComboRenderPipelineDescriptor.h" #include "utils/TestUtils.h" #include "utils/TextureFormatUtils.h" #include "utils/WGPUHelpers.h" class DepthStencilCopyTests : public DawnTest { protected: void SetUp() override { DawnTest::SetUp(); // Draw a square in the bottom left quarter of the screen. mVertexModule = utils::CreateShaderModuleFromWGSL(device, R"( [[builtin(vertex_idx)]] var VertexIndex : u32; [[builtin(position)]] var Position : vec4; [[stage(vertex)]] fn main() -> void { const pos : array, 6> = array, 6>( vec2(-1.0, -1.0), vec2( 0.0, -1.0), vec2(-1.0, 0.0), vec2(-1.0, 0.0), vec2( 0.0, -1.0), vec2( 0.0, 0.0)); Position = vec4(pos[VertexIndex], 0.0, 1.0); })"); } wgpu::Texture CreateDepthStencilTexture(uint32_t width, uint32_t height, wgpu::TextureUsage usage, uint32_t mipLevelCount = 1) { wgpu::TextureDescriptor texDescriptor = {}; texDescriptor.size = {width, height, 1}; texDescriptor.format = wgpu::TextureFormat::Depth24PlusStencil8; texDescriptor.usage = usage; texDescriptor.mipLevelCount = mipLevelCount; return device.CreateTexture(&texDescriptor); } wgpu::Texture CreateDepthTexture(uint32_t width, uint32_t height, wgpu::TextureUsage usage, uint32_t mipLevelCount = 1) { wgpu::TextureDescriptor texDescriptor = {}; texDescriptor.size = {width, height, 1}; texDescriptor.format = wgpu::TextureFormat::Depth32Float; texDescriptor.usage = usage; texDescriptor.mipLevelCount = mipLevelCount; return device.CreateTexture(&texDescriptor); } void PopulatePipelineDescriptorWriteDepth(utils::ComboRenderPipelineDescriptor* desc, wgpu::TextureFormat format, float regionDepth) { desc->vertexStage.module = mVertexModule; std::string fsSource = R"( #version 450 void main() { gl_FragDepth = )" + std::to_string(regionDepth) + ";\n}"; desc->cFragmentStage.module = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fsSource.c_str()); desc->cDepthStencilState.format = format; desc->cDepthStencilState.depthWriteEnabled = true; desc->depthStencilState = &desc->cDepthStencilState; desc->colorStateCount = 0; } // Initialize the depth/stencil values for the texture using a render pass. // The texture will be cleared to the "clear" value, and then bottom left corner will // be written with the "region" value. void InitializeDepthTextureRegion(wgpu::Texture texture, float clearDepth, float regionDepth, uint32_t mipLevel = 0) { wgpu::TextureViewDescriptor viewDesc = {}; viewDesc.baseMipLevel = mipLevel; viewDesc.mipLevelCount = 1; utils::ComboRenderPassDescriptor renderPassDesc({}, texture.CreateView(&viewDesc)); renderPassDesc.cDepthStencilAttachmentInfo.clearDepth = clearDepth; utils::ComboRenderPipelineDescriptor renderPipelineDesc(device); PopulatePipelineDescriptorWriteDepth(&renderPipelineDesc, wgpu::TextureFormat::Depth32Float, regionDepth); wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPassDesc); pass.SetPipeline(pipeline); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); } // Initialize the depth/stencil values for the texture using a render pass. // The texture will be cleared to the "clear" values, and then bottom left corner will // be written with the "region" values. void InitializeDepthStencilTextureRegion(wgpu::Texture texture, float clearDepth, float regionDepth, uint8_t clearStencil, uint8_t regionStencil, uint32_t mipLevel = 0) { wgpu::TextureViewDescriptor viewDesc = {}; viewDesc.baseMipLevel = mipLevel; viewDesc.mipLevelCount = 1; utils::ComboRenderPassDescriptor renderPassDesc({}, texture.CreateView(&viewDesc)); renderPassDesc.cDepthStencilAttachmentInfo.clearDepth = clearDepth; renderPassDesc.cDepthStencilAttachmentInfo.clearStencil = clearStencil; utils::ComboRenderPipelineDescriptor renderPipelineDesc(device); PopulatePipelineDescriptorWriteDepth(&renderPipelineDesc, wgpu::TextureFormat::Depth24PlusStencil8, regionDepth); renderPipelineDesc.cDepthStencilState.stencilFront.passOp = wgpu::StencilOperation::Replace; wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc); // Draw the quad (two triangles) wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPassDesc); pass.SetPipeline(pipeline); pass.SetStencilReference(regionStencil); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); } wgpu::Texture CreateInitializeDepthStencilTextureAndCopyT2T(float clearDepth, float regionDepth, uint8_t clearStencil, uint8_t regionStencil, uint32_t width, uint32_t height, wgpu::TextureUsage usage, uint32_t mipLevel = 0) { wgpu::Texture src = CreateDepthStencilTexture( width, height, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, mipLevel + 1); wgpu::Texture dst = CreateDepthStencilTexture( width, height, usage | wgpu::TextureUsage::CopyDst, mipLevel + 1); InitializeDepthStencilTextureRegion(src, clearDepth, regionDepth, clearStencil, regionStencil, mipLevel); // Perform a T2T copy of all aspects { wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::TextureCopyView srcView = utils::CreateTextureCopyView(src, mipLevel, {0, 0, 0}); wgpu::TextureCopyView dstView = utils::CreateTextureCopyView(dst, mipLevel, {0, 0, 0}); wgpu::Extent3D copySize = {width >> mipLevel, height >> mipLevel, 1}; commandEncoder.CopyTextureToTexture(&srcView, &dstView, ©Size); wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); } return dst; } // Check depth by uploading expected data to a sampled texture, writing it out as a depth // attachment, and then using the "equals" depth test to check the contents are the same. void ExpectDepthData(wgpu::Texture depthTexture, wgpu::TextureFormat depthFormat, uint32_t width, uint32_t height, uint32_t mipLevel, std::vector expected) { wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); // Make the color attachment that we'll use to read back. wgpu::TextureDescriptor colorTexDesc = {}; colorTexDesc.size = {width, height, 1}; colorTexDesc.format = wgpu::TextureFormat::R32Uint; colorTexDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc; wgpu::Texture colorTexture = device.CreateTexture(&colorTexDesc); // Make a sampleable texture to store the depth data. We'll sample this in the // shader to output depth. wgpu::TextureDescriptor depthDataDesc = {}; depthDataDesc.size = {width, height, 1}; depthDataDesc.format = wgpu::TextureFormat::R32Float; depthDataDesc.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopyDst; wgpu::Texture depthDataTexture = device.CreateTexture(&depthDataDesc); // Upload the depth data. uint32_t bytesPerRow = utils::GetMinimumBytesPerRow(wgpu::TextureFormat::R32Float, width); wgpu::BufferDescriptor uploadBufferDesc = {}; uploadBufferDesc.size = utils::RequiredBytesInCopy(bytesPerRow, height, depthDataDesc.size, wgpu::TextureFormat::R32Float); uploadBufferDesc.usage = wgpu::BufferUsage::CopySrc; uploadBufferDesc.mappedAtCreation = true; // TODO(enga): Use WriteTexture when implemented on OpenGL. wgpu::Buffer uploadBuffer = device.CreateBuffer(&uploadBufferDesc); uint8_t* dst = static_cast(uploadBuffer.GetMappedRange()); float* src = expected.data(); for (uint32_t y = 0; y < height; ++y) { memcpy(dst, src, width * sizeof(float)); dst += bytesPerRow; src += width; } uploadBuffer.Unmap(); wgpu::BufferCopyView bufferCopy = utils::CreateBufferCopyView(uploadBuffer, 0, bytesPerRow, height); wgpu::TextureCopyView textureCopy = utils::CreateTextureCopyView(depthDataTexture, 0, {0, 0, 0}, wgpu::TextureAspect::All); commandEncoder.CopyBufferToTexture(&bufferCopy, &textureCopy, &depthDataDesc.size); // Pipeline for a full screen quad. utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); pipelineDescriptor.vertexStage.module = utils::CreateShaderModuleFromWGSL(device, R"( [[builtin(vertex_idx)]] var VertexIndex : u32; [[builtin(position)]] var Position : vec4; [[stage(vertex)]] fn main() -> void { const pos : array, 3> = array, 3>( vec2(-1.0, -1.0), vec2( 3.0, -1.0), vec2(-1.0, 3.0)); Position = vec4(pos[VertexIndex], 0.0, 1.0); })"); // Sample the input texture and write out depth. |result| will only be set to 1 if we // pass the depth test. pipelineDescriptor.cFragmentStage.module = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(set = 0, binding = 0) uniform sampler sampler0; layout(set = 0, binding = 1) uniform texture2D texture0; layout(location = 0) out uint result; void main() { result = 1u; gl_FragDepth = texelFetch(sampler2D(texture0, sampler0), ivec2(gl_FragCoord), 0)[0]; })"); // Pass the depth test only if the depth is equal. pipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleList; pipelineDescriptor.depthStencilState = &pipelineDescriptor.cDepthStencilState; pipelineDescriptor.cDepthStencilState.format = depthFormat; pipelineDescriptor.cDepthStencilState.depthCompare = wgpu::CompareFunction::Equal; pipelineDescriptor.cColorStates[0].format = colorTexDesc.format; // TODO(jiawei.shao@intel.com): The Intel Mesa Vulkan driver can't set gl_FragDepth unless // depthWriteEnabled == true. This either needs to be fixed in the driver or restricted by // the WebGPU API. pipelineDescriptor.cDepthStencilState.depthWriteEnabled = true; wgpu::TextureViewDescriptor viewDesc = {}; viewDesc.baseMipLevel = mipLevel; viewDesc.mipLevelCount = 1; utils::ComboRenderPassDescriptor passDescriptor({colorTexture.CreateView()}, depthTexture.CreateView(&viewDesc)); passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); // Bind a sampler and the depth data texture. wgpu::SamplerDescriptor samplerDesc = {}; wgpu::BindGroup bindGroup = utils::MakeBindGroup( device, pipeline.GetBindGroupLayout(0), {{0, device.CreateSampler(&samplerDesc)}, {1, depthDataTexture.CreateView()}}); wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); pass.SetPipeline(pipeline); pass.SetBindGroup(0, bindGroup); pass.Draw(3); pass.EndPass(); wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); std::vector colorData(width * height, 1u); EXPECT_TEXTURE_EQ(colorData.data(), colorTexture, 0, 0, width, height, 0, 0); } wgpu::ShaderModule mVertexModule; }; // Test copying the depth-only aspect into a buffer. TEST_P(DepthStencilCopyTests, FromDepthAspect) { constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; wgpu::Texture depthTexture = CreateDepthTexture( kWidth, kHeight, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc); InitializeDepthTextureRegion(depthTexture, 0.f, 0.3f); // This expectation is the test as it performs the CopyTextureToBuffer. std::vector expectedData = { 0.0, 0.0, 0.0, 0.0, // 0.0, 0.0, 0.0, 0.0, // 0.3, 0.3, 0.0, 0.0, // 0.3, 0.3, 0.0, 0.0, // }; EXPECT_TEXTURE_EQ(expectedData.data(), depthTexture, 0, 0, kWidth, kHeight, 0, 0, wgpu::TextureAspect::DepthOnly); } // Test copying the stencil-only aspect into a buffer. TEST_P(DepthStencilCopyTests, FromStencilAspect) { constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; wgpu::Texture depthStencilTexture = CreateDepthStencilTexture( kWidth, kHeight, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc); InitializeDepthStencilTextureRegion(depthStencilTexture, 0.f, 0.3f, 0u, 1u); // This expectation is the test as it performs the CopyTextureToBuffer. std::vector expectedData = { 0u, 0u, 0u, 0u, // 0u, 0u, 0u, 0u, // 1u, 1u, 0u, 0u, // 1u, 1u, 0u, 0u, // }; EXPECT_TEXTURE_EQ(expectedData.data(), depthStencilTexture, 0, 0, kWidth, kHeight, 0, 0, wgpu::TextureAspect::StencilOnly); } // Test copying the non-zero mip, stencil-only aspect into a buffer. TEST_P(DepthStencilCopyTests, FromNonZeroMipStencilAspect) { // TODO(enga): Figure out why this fails on MacOS Intel Iris. // It passes on AMD Radeon Pro and Intel HD Graphics 630. DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); wgpu::Texture depthStencilTexture = CreateDepthStencilTexture( 9, 9, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 2); InitializeDepthStencilTextureRegion(depthStencilTexture, 0.f, 0.3f, 0u, 1u, 1u); // This expectation is the test as it performs the CopyTextureToBuffer. std::vector expectedData = { 0u, 0u, 0u, 0u, // 0u, 0u, 0u, 0u, // 1u, 1u, 0u, 0u, // 1u, 1u, 0u, 0u, // }; EXPECT_TEXTURE_EQ(expectedData.data(), depthStencilTexture, 0, 0, 4, 4, 1, 0, wgpu::TextureAspect::StencilOnly); } // Test copying the non-zero mip, depth-only aspect into a buffer. TEST_P(DepthStencilCopyTests, FromNonZeroMipDepthAspect) { wgpu::Texture depthTexture = CreateDepthTexture( 9, 9, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 2); InitializeDepthTextureRegion(depthTexture, 0.f, 0.4f, 1); // This expectation is the test as it performs the CopyTextureToBuffer. std::vector expectedData = { 0.0, 0.0, 0.0, 0.0, // 0.0, 0.0, 0.0, 0.0, // 0.4, 0.4, 0.0, 0.0, // 0.4, 0.4, 0.0, 0.0, // }; EXPECT_TEXTURE_EQ(expectedData.data(), depthTexture, 0, 0, 4, 4, 1, 0, wgpu::TextureAspect::DepthOnly); } // Test copying both aspects in a T2T copy, then copying only stencil. TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyStencil) { // TODO(enga): Figure out why this fails on MacOS Intel Iris. // It passes on AMD Radeon Pro and Intel HD Graphics 630. // Maybe has to do with the RenderAttachment usage. Notably, a later test // T2TBothAspectsThenCopyNonRenderableStencil does not use RenderAttachment and works correctly. DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; wgpu::Texture texture = CreateInitializeDepthStencilTextureAndCopyT2T( 0.1f, 0.3f, 1u, 3u, kWidth, kHeight, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); // Check the stencil std::vector expectedData = { 1u, 1u, 1u, 1u, // 1u, 1u, 1u, 1u, // 3u, 3u, 1u, 1u, // 3u, 3u, 1u, 1u, // }; EXPECT_TEXTURE_EQ(expectedData.data(), texture, 0, 0, kWidth, kHeight, 0, 0, wgpu::TextureAspect::StencilOnly); } // Test that part of a non-renderable stencil aspect can be copied. Notably, // this test has different behavior on some platforms than T2TBothAspectsThenCopyStencil. TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyNonRenderableStencil) { constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; wgpu::Texture texture = CreateInitializeDepthStencilTextureAndCopyT2T( 0.1f, 0.3f, 1u, 3u, kWidth, kHeight, wgpu::TextureUsage::CopySrc); // Check the stencil std::vector expectedData = { 1u, 1u, 1u, 1u, // 1u, 1u, 1u, 1u, // 3u, 3u, 1u, 1u, // 3u, 3u, 1u, 1u, // }; EXPECT_TEXTURE_EQ(expectedData.data(), texture, 0, 0, kWidth, kHeight, 0, 0, wgpu::TextureAspect::StencilOnly); } // Test that part of a non-renderable, non-zero mip stencil aspect can be copied. Notably, // this test has different behavior on some platforms than T2TBothAspectsThenCopyStencil. TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyNonRenderableNonZeroMipStencil) { // TODO(enga): Figure out why this fails on MacOS Intel Iris. // It passes on AMD Radeon Pro and Intel HD Graphics 630. // Maybe has to do with the non-zero mip. Notably, a previous test // T2TBothAspectsThenCopyNonRenderableStencil works correctly. DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); wgpu::Texture texture = CreateInitializeDepthStencilTextureAndCopyT2T( 0.1f, 0.3f, 1u, 3u, 9, 9, wgpu::TextureUsage::CopySrc, 1); // Check the stencil std::vector expectedData = { 1u, 1u, 1u, 1u, // 1u, 1u, 1u, 1u, // 3u, 3u, 1u, 1u, // 3u, 3u, 1u, 1u, // }; EXPECT_TEXTURE_EQ(expectedData.data(), texture, 0, 0, 4, 4, 1, 0, wgpu::TextureAspect::StencilOnly); } // Test copying both aspects in a T2T copy, then copying only depth. TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyDepth) { constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; wgpu::Texture texture = CreateInitializeDepthStencilTextureAndCopyT2T( 0.1f, 0.3f, 1u, 3u, kWidth, kHeight, wgpu::TextureUsage::RenderAttachment); // Check the depth ExpectDepthData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth, kHeight, 0, { 0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, // 0.3, 0.3, 0.1, 0.1, // 0.3, 0.3, 0.1, 0.1, // }); } // Test copying both aspects in a T2T copy, then copying only depth at a nonzero mip. TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyNonZeroMipDepth) { wgpu::Texture texture = CreateInitializeDepthStencilTextureAndCopyT2T( 0.1f, 0.3f, 1u, 3u, 8, 8, wgpu::TextureUsage::RenderAttachment, 1); // Check the depth ExpectDepthData(texture, wgpu::TextureFormat::Depth24PlusStencil8, 4, 4, 1, { 0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, // 0.3, 0.3, 0.1, 0.1, // 0.3, 0.3, 0.1, 0.1, // }); } // Test copying both aspects in a T2T copy, then copying stencil, then copying depth TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyStencilThenDepth) { constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; wgpu::Texture texture = CreateInitializeDepthStencilTextureAndCopyT2T( 0.1f, 0.3f, 1u, 3u, kWidth, kHeight, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); // Check the stencil std::vector expectedData = { 1u, 1u, 1u, 1u, // 1u, 1u, 1u, 1u, // 3u, 3u, 1u, 1u, // 3u, 3u, 1u, 1u, // }; EXPECT_TEXTURE_EQ(expectedData.data(), texture, 0, 0, kWidth, kHeight, 0, 0, wgpu::TextureAspect::StencilOnly); // Check the depth ExpectDepthData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth, kHeight, 0, { 0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, // 0.3, 0.3, 0.1, 0.1, // 0.3, 0.3, 0.1, 0.1, // }); } // Test copying both aspects in a T2T copy, then copying depth, then copying stencil TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyDepthThenStencil) { // TODO(enga): Figure out why this fails on MacOS Intel Iris. // It passes on AMD Radeon Pro and Intel HD Graphics 630. // It seems like the depth readback copy mutates the stencil because the previous // test T2TBothAspectsThenCopyStencil passes. // T2TBothAspectsThenCopyStencilThenDepth which checks stencil first also passes. DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; wgpu::Texture texture = CreateInitializeDepthStencilTextureAndCopyT2T( 0.1f, 0.3f, 1u, 3u, kWidth, kHeight, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment); // Check the depth ExpectDepthData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth, kHeight, 0, { 0.1, 0.1, 0.1, 0.1, // 0.1, 0.1, 0.1, 0.1, // 0.3, 0.3, 0.1, 0.1, // 0.3, 0.3, 0.1, 0.1, // }); // Check the stencil std::vector expectedData = { 1u, 1u, 1u, 1u, // 1u, 1u, 1u, 1u, // 3u, 3u, 1u, 1u, // 3u, 3u, 1u, 1u, // }; EXPECT_TEXTURE_EQ(expectedData.data(), texture, 0, 0, kWidth, kHeight, 0, 0, wgpu::TextureAspect::StencilOnly); } // Test copying to the stencil-aspect of a buffer TEST_P(DepthStencilCopyTests, ToStencilAspect) { // Copies to a single aspect are unsupported on OpenGL. DAWN_SKIP_TEST_IF(IsOpenGL()); DAWN_SKIP_TEST_IF(IsOpenGLES()); // TODO(enga): Figure out why this fails on MacOS Intel Iris. // It passes on AMD Radeon Pro and Intel HD Graphics 630. DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); // TODO(enga): Figure out why this fails on Windows Intel Vulkan. // Reading back the depth does not work. DAWN_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan()); // Create a stencil texture constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; wgpu::Texture depthStencilTexture = CreateDepthStencilTexture(kWidth, kHeight, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst); { wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); // Clear depth to 0.7, so we can check that the stencil copy doesn't mutate the depth. utils::ComboRenderPassDescriptor passDescriptor({}, depthStencilTexture.CreateView()); passDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.7; wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); pass.EndPass(); wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); } std::vector stencilData = { 1u, 2u, 3u, 4u, // 5u, 6u, 7u, 8u, // 9u, 10u, 11u, 12u, // 13u, 14u, 15u, 16u, // }; // After copying stencil data in, we will decrement stencil values in the bottom left // of the screen. This is the expected result. std::vector expectedStencilData = { 1u, 2u, 3u, 4u, // 5u, 6u, 7u, 8u, // 8u, 9u, 11u, 12u, // 12u, 13u, 15u, 16u, // }; // Upload the stencil data. wgpu::TextureDataLayout stencilDataLayout = {}; stencilDataLayout.bytesPerRow = kWidth * sizeof(uint8_t); wgpu::TextureCopyView stencilDataCopyView = utils::CreateTextureCopyView( depthStencilTexture, 0, {0, 0, 0}, wgpu::TextureAspect::StencilOnly); wgpu::Extent3D writeSize = {kWidth, kHeight, 1}; queue.WriteTexture(&stencilDataCopyView, stencilData.data(), stencilData.size() * sizeof(uint8_t), &stencilDataLayout, &writeSize); // Decrement the stencil value in a render pass to ensure the data is visible to the pipeline. { wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); // Create a render pipline which decrements the stencil value for passing fragments. // A quad is drawn in the bottom left. utils::ComboRenderPipelineDescriptor renderPipelineDesc(device); renderPipelineDesc.vertexStage.module = mVertexModule; renderPipelineDesc.cFragmentStage.module = utils::CreateShaderModuleFromWGSL(device, R"( [[stage(fragment)]] fn main() -> void { })"); renderPipelineDesc.cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; renderPipelineDesc.cDepthStencilState.stencilFront.passOp = wgpu::StencilOperation::DecrementClamp; renderPipelineDesc.depthStencilState = &renderPipelineDesc.cDepthStencilState; renderPipelineDesc.colorStateCount = 0; wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc); // Create a render pass which loads the stencil. We want to load the values we // copied in. Also load the canary depth values so they're not lost. utils::ComboRenderPassDescriptor passDescriptor({}, depthStencilTexture.CreateView()); passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; // Draw the quad in the bottom left (two triangles). wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); pass.SetPipeline(pipeline); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); } // Copy back the stencil data and check it is correct. EXPECT_TEXTURE_EQ(expectedStencilData.data(), depthStencilTexture, 0, 0, kWidth, kHeight, 0, 0, wgpu::TextureAspect::StencilOnly); ExpectDepthData(depthStencilTexture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth, kHeight, 0, { 0.7, 0.7, 0.7, 0.7, // 0.7, 0.7, 0.7, 0.7, // 0.7, 0.7, 0.7, 0.7, // 0.7, 0.7, 0.7, 0.7, // }); } DAWN_INSTANTIATE_TEST(DepthStencilCopyTests, D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend());