diff --git a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp index 4f921d19ec..d6431c8a6d 100644 --- a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp +++ b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp @@ -215,12 +215,37 @@ class DepthStencilCopyTests : public DawnTestWithParams> testLevel; + uint32_t copyHeight = textureHeight >> testLevel; + wgpu::Extent3D copySize = {copyWidth, copyHeight, 1}; + + constexpr uint32_t kBytesPerRow = kTextureBytesPerRowAlignment; + wgpu::BufferDescriptor bufferDescriptor = {}; + bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + bufferDescriptor.size = + bufferCopyOffset + BufferSizeForTextureCopy(copyWidth, copyHeight, 1, + GetParam().mTextureFormat, + wgpu::TextureAspect::DepthOnly); + wgpu::Buffer destinationBuffer = device.CreateBuffer(&bufferDescriptor); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture( + texture, testLevel, {0, 0, 0}, wgpu::TextureAspect::DepthOnly); + wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer( + destinationBuffer, bufferCopyOffset, kBytesPerRow, copyHeight); + encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + if (GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm) { + uint16_t expected = FloatToUnorm(initDepth); + std::vector expectedData = { + 0, 0, 0, 0, // + 0, 0, 0, 0, // + expected, expected, 0, 0, // + expected, expected, 0, 0, // + }; + + for (uint32_t y = 0; y < copyHeight; ++y) { + EXPECT_BUFFER_U16_RANGE_EQ(expectedData.data() + copyWidth * y, destinationBuffer, + bufferCopyOffset + y * kBytesPerRow, copyWidth); + } + + } else { + std::vector expectedData = { + 0.0, 0.0, 0.0, 0.0, // + 0.0, 0.0, 0.0, 0.0, // + initDepth, initDepth, 0.0, 0.0, // + initDepth, initDepth, 0.0, 0.0, // + }; + + for (uint32_t y = 0; y < copyHeight; ++y) { + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data() + copyWidth * y, destinationBuffer, + bufferCopyOffset + y * kBytesPerRow, copyWidth); + } + } + } +}; // Test copying the depth-only aspect into a buffer. TEST_P(DepthCopyTests, FromDepthAspect) { - // TODO(crbug.com/dawn/1237): Depth16Unorm test failed on OpenGL and OpenGLES which says - // Invalid format and type combination in glReadPixels - DAWN_TEST_UNSUPPORTED_IF(GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm && - (IsOpenGL() || IsOpenGLES())); - - // TODO(crbug.com/dawn/1291): These tests are failing on GLES (both native and ANGLE) - // when using Tint/GLSL. - DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES()); - + constexpr float kInitDepth = 0.2f; + constexpr uint32_t kBufferCopyOffset = 0; constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; + constexpr uint32_t kTestLevel = 0; + DoCopyFromDepthTest(kBufferCopyOffset, kInitDepth, kWidth, kHeight, kTestLevel); +} - wgpu::Texture texture = CreateTexture( - kWidth, kHeight, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc); +// Test copying the depth-only aspect into a buffer at a non-zero offset. +TEST_P(DepthCopyTests, FromDepthAspectToBufferAtNonZeroOffset) { + // TODO(crbug.com/dawn/727): currently this test fails on many D3D12 drivers as there is a bug + // in the implementation of texture-to-buffer copies with depth stencil textures on D3D12. + DAWN_SUPPRESS_TEST_IF(IsD3D12()); constexpr float kInitDepth = 0.2f; - InitializeDepthStencilTextureRegion(texture, 0.f, kInitDepth, 0, 0); - - // This expectation is the test as it performs the CopyTextureToBuffer. - if (GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm) { - uint16_t expected = FloatToUnorm(kInitDepth); - std::vector expectedData = { - 0, 0, 0, 0, // - 0, 0, 0, 0, // - expected, expected, 0, 0, // - expected, expected, 0, 0, // - }; - EXPECT_TEXTURE_EQ(expectedData.data(), texture, {0, 0}, {kWidth, kHeight}, 0, - wgpu::TextureAspect::DepthOnly); - } else { - std::vector expectedData = { - 0.0, 0.0, 0.0, 0.0, // - 0.0, 0.0, 0.0, 0.0, // - kInitDepth, kInitDepth, 0.0, 0.0, // - kInitDepth, kInitDepth, 0.0, 0.0, // - }; - EXPECT_TEXTURE_EQ(expectedData.data(), texture, {0, 0}, {kWidth, kHeight}, 0, - wgpu::TextureAspect::DepthOnly); + constexpr uint32_t kWidth = 4; + constexpr uint32_t kHeight = 4; + constexpr uint32_t kTestLevel = 0; + constexpr std::array kBufferCopyOffsets = {4u, 512u}; + for (uint32_t offset : kBufferCopyOffsets) { + DoCopyFromDepthTest(offset, kInitDepth, kWidth, kHeight, kTestLevel); } } // Test copying the non-zero mip, depth-only aspect into a buffer. TEST_P(DepthCopyTests, FromNonZeroMipDepthAspect) { - // TODO(crbug.com/dawn/1237): Depth16Unorm test failed on OpenGL and OpenGLES which says - // Invalid format and type combination in glReadPixels - DAWN_TEST_UNSUPPORTED_IF(GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm && - (IsOpenGL() || IsOpenGLES())); - - // TODO(crbug.com/dawn/1291): These tests are failing on GLES (both native and ANGLE) - // when using Tint/GLSL. - DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES()); - - wgpu::Texture depthTexture = CreateDepthTexture( - 9, 9, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 2); - - constexpr float kInitDepth = 0.4f; - InitializeDepthStencilTextureRegion(depthTexture, 0.f, kInitDepth, 0, 0, /*mipLevel*/ 1); - - // This expectation is the test as it performs the CopyTextureToBuffer. - if (GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm) { - uint16_t expected = FloatToUnorm(kInitDepth); - std::vector expectedData = { - 0, 0, 0, 0, // - 0, 0, 0, 0, // - expected, expected, 0, 0, // - expected, expected, 0, 0, // - }; - EXPECT_TEXTURE_EQ(expectedData.data(), depthTexture, {0, 0}, {4, 4}, 1, - wgpu::TextureAspect::DepthOnly); - } else { - std::vector expectedData = { - 0.0, 0.0, 0.0, 0.0, // - 0.0, 0.0, 0.0, 0.0, // - kInitDepth, kInitDepth, 0.0, 0.0, // - kInitDepth, kInitDepth, 0.0, 0.0, // - }; - EXPECT_TEXTURE_EQ(expectedData.data(), depthTexture, {0, 0}, {4, 4}, 1, - wgpu::TextureAspect::DepthOnly); - } + constexpr float kInitDepth = 0.2f; + constexpr uint32_t kBufferCopyOffset = 0; + constexpr uint32_t kWidth = 9; + constexpr uint32_t kHeight = 9; + constexpr uint32_t kTestLevel = 1; + DoCopyFromDepthTest(kBufferCopyOffset, kInitDepth, kWidth, kHeight, kTestLevel); } -class DepthCopyFromBufferTests : public DepthStencilCopyTests {}; +class DepthCopyFromBufferTests : public DepthStencilCopyTests { + public: + void DoTest(uint32_t bufferCopyOffset, bool hasRenderAttachmentUsage) { + // TODO(crbug.com/dawn/1237): Depth16Unorm test failed on OpenGL and OpenGLES which says + // Invalid format and type combination in glReadPixels + DAWN_TEST_UNSUPPORTED_IF(GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm && + (IsOpenGL() || IsOpenGLES())); + + constexpr uint32_t kWidth = 8; + constexpr uint32_t kHeight = 1; + + wgpu::TextureUsage textureUsage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + // On D3D12 backend the resource flag D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL is set only + // when wgpu::TextureUsage::RenderAttachment is set on the creation of a depth stencil + // texture. + if (hasRenderAttachmentUsage) { + textureUsage |= wgpu::TextureUsage::RenderAttachment; + } + wgpu::Texture destTexture = CreateTexture(kWidth, kHeight, textureUsage); + + wgpu::BufferDescriptor descriptor; + descriptor.size = bufferCopyOffset + + BufferSizeForTextureCopy(kWidth, kHeight, 1, GetParam().mTextureFormat); + descriptor.usage = wgpu::BufferUsage::CopySrc; + descriptor.mappedAtCreation = true; + wgpu::Buffer srcBuffer = device.CreateBuffer(&descriptor); + + constexpr uint32_t kBytesPerRow = kTextureBytesPerRowAlignment; + wgpu::ImageCopyBuffer imageCopyBuffer = + utils::CreateImageCopyBuffer(srcBuffer, bufferCopyOffset, kBytesPerRow, kHeight); + wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture( + destTexture, 0, {0, 0, 0}, wgpu::TextureAspect::DepthOnly); + wgpu::Extent3D extent = {kWidth, kHeight, 1}; + + constexpr float kInitDepth = 0.2f; + + // This expectation is the test as it performs the CopyTextureToBuffer. + if (GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm) { + uint16_t expected = FloatToUnorm(kInitDepth); + std::vector expectedData = { + 0, 0, expected, expected, 0, 0, expected, expected, + }; + size_t expectedSize = expectedData.size() * sizeof(uint16_t); + memcpy(srcBuffer.GetMappedRange(bufferCopyOffset, expectedSize), expectedData.data(), + expectedSize); + srcBuffer.Unmap(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, &extent); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_TEXTURE_EQ(expectedData.data(), destTexture, {0, 0}, {kWidth, kHeight}, 0, + wgpu::TextureAspect::DepthOnly); + } else { + std::vector expectedData = { + 0.0, 0.0, kInitDepth, kInitDepth, 0.0, 0.0, kInitDepth, kInitDepth, + }; + size_t expectedSize = expectedData.size() * sizeof(float); + + memcpy(srcBuffer.GetMappedRange(bufferCopyOffset, expectedSize), expectedData.data(), + expectedSize); + srcBuffer.Unmap(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, &extent); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_TEXTURE_EQ(expectedData.data(), destTexture, {0, 0}, {kWidth, kHeight}, 0, + wgpu::TextureAspect::DepthOnly); + } + } +}; // Test copying the depth-only aspect from a buffer. TEST_P(DepthCopyFromBufferTests, BufferToDepthAspect) { - // TODO(crbug.com/dawn/1237): Depth16Unorm test failed on OpenGL and OpenGLES which says - // Invalid format and type combination in glReadPixels - DAWN_TEST_UNSUPPORTED_IF(GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm && - (IsOpenGL() || IsOpenGLES())); + constexpr uint32_t kBufferCopyOffset = 0; + constexpr bool kIsRenderable = false; + DoTest(kBufferCopyOffset, kIsRenderable); +} - constexpr uint32_t kWidth = 8; - constexpr uint32_t kHeight = 1; - - wgpu::Texture destTexture = - CreateTexture(kWidth, kHeight, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); - - wgpu::BufferDescriptor descriptor; - descriptor.size = BufferSizeForTextureCopy(kWidth, kHeight, 1, GetParam().mTextureFormat); - descriptor.usage = wgpu::BufferUsage::CopySrc; - descriptor.mappedAtCreation = true; - wgpu::Buffer srcBuffer = device.CreateBuffer(&descriptor); - - wgpu::ImageCopyBuffer imageCopyBuffer = - utils::CreateImageCopyBuffer(srcBuffer, 0, 256, kHeight); - wgpu::ImageCopyTexture imageCopyTexture = - utils::CreateImageCopyTexture(destTexture, 0, {0, 0, 0}, wgpu::TextureAspect::DepthOnly); - wgpu::Extent3D extent = {kWidth, kHeight, 1}; - - constexpr float kInitDepth = 0.2f; - - // This expectation is the test as it performs the CopyTextureToBuffer. - if (GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm) { - uint16_t expected = FloatToUnorm(kInitDepth); - std::vector expectedData = { - 0, 0, expected, expected, 0, 0, expected, expected, - }; - size_t expectedSize = expectedData.size() * sizeof(uint16_t); - - memcpy(srcBuffer.GetMappedRange(0, expectedSize), expectedData.data(), expectedSize); - srcBuffer.Unmap(); - - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, &extent); - wgpu::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - EXPECT_TEXTURE_EQ(expectedData.data(), destTexture, {0, 0}, {kWidth, kHeight}, 0, - wgpu::TextureAspect::DepthOnly); - } else { - std::vector expectedData = { - 0.0, 0.0, kInitDepth, kInitDepth, 0.0, 0.0, kInitDepth, kInitDepth, - }; - size_t expectedSize = expectedData.size() * sizeof(float); - - memcpy(srcBuffer.GetMappedRange(0, expectedSize), expectedData.data(), expectedSize); - srcBuffer.Unmap(); - - wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, &extent); - wgpu::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - EXPECT_TEXTURE_EQ(expectedData.data(), destTexture, {0, 0}, {kWidth, kHeight}, 0, - wgpu::TextureAspect::DepthOnly); +// Test copying the depth-only aspect from a buffer at a non-zero offset. +TEST_P(DepthCopyFromBufferTests, BufferToNonRenderableDepthAspectAtNonZeroOffset) { + constexpr std::array kBufferCopyOffsets = {8, 512}; + constexpr bool kIsRenderable = false; + for (uint32_t offset : kBufferCopyOffsets) { + DoTest(offset, kIsRenderable); } } -class StencilCopyTests : public DepthStencilCopyTests {}; +// Test copying the depth-only aspect from a buffer at a non-zero offset. +TEST_P(DepthCopyFromBufferTests, BufferToRenderableDepthAspectAtNonZeroOffset) { + constexpr std::array kBufferCopyOffsets = {8, 512}; + + // TODO(crbug.com/dawn/727): currently this test fails on many D3D12 drivers as there is a bug + // in the implementation of texture-to-buffer copies with depth stencil textures on D3D12. + DAWN_SUPPRESS_TEST_IF(IsD3D12()); + constexpr bool kIsRenderable = true; + for (uint32_t offset : kBufferCopyOffsets) { + DoTest(offset, kIsRenderable); + } +} + +class StencilCopyTests : public DepthStencilCopyTests { + public: + void DoCopyFromStencilTest(uint32_t bufferCopyOffset, + uint32_t textureWidth, + uint32_t textureHeight, + uint32_t testLevel) { + // TODO(crbug.com/dawn/1497): glReadPixels: GL error: HIGH: Invalid format and type + // combination. + DAWN_SUPPRESS_TEST_IF(IsANGLE()); + + // TODO(crbug.com/dawn/667): Work around the fact that some platforms are unable to read + // stencil. + DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_depth_stencil_read")); + + uint32_t mipLevelCount = testLevel + 1; + wgpu::Texture depthStencilTexture = CreateDepthStencilTexture( + textureWidth, textureHeight, + wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, mipLevelCount); + + InitializeDepthStencilTextureRegion(depthStencilTexture, 0.f, 0.3f, 0u, 1u, testLevel); + + std::vector expectedData = { + 0u, 0u, 0u, 0u, // + 0u, 0u, 0u, 0u, // + 1u, 1u, 0u, 0u, // + 1u, 1u, 0u, 0u, // + }; + + uint32_t copyWidth = textureWidth >> testLevel; + uint32_t copyHeight = textureHeight >> testLevel; + ASSERT_EQ(expectedData.size(), copyWidth * copyHeight); + wgpu::Extent3D copySize = {copyWidth, copyHeight, 1}; + + constexpr uint32_t kBytesPerRow = kTextureBytesPerRowAlignment; + wgpu::BufferDescriptor bufferDescriptor = {}; + bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + bufferDescriptor.size = + bufferCopyOffset + BufferSizeForTextureCopy(copyWidth, copyHeight, 1, + GetParam().mTextureFormat, + wgpu::TextureAspect::StencilOnly); + wgpu::Buffer destinationBuffer = device.CreateBuffer(&bufferDescriptor); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture( + depthStencilTexture, testLevel, {0, 0, 0}, wgpu::TextureAspect::StencilOnly); + wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer( + destinationBuffer, bufferCopyOffset, kBytesPerRow, copyHeight); + encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + for (uint32_t y = 0; y < copyHeight; ++y) { + EXPECT_BUFFER_U8_RANGE_EQ(expectedData.data() + copyWidth * y, destinationBuffer, + bufferCopyOffset + y * kBytesPerRow, copyWidth); + } + } + + void DoCopyToStencilTest(uint32_t bufferCopyOffset) { + // Copies to a single aspect are unsupported on OpenGL. + DAWN_TEST_UNSUPPORTED_IF(IsOpenGL()); + DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES()); + + // TODO(crbug.com/dawn/704): Readback after clear via stencil copy does not work + // on some Intel drivers. + DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel()); + + // TODO(crbug.com/dawn/1273): Fails on Win11 with D3D12 debug layer and full validation + DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsBackendValidationEnabled()); + + // Create a stencil texture + constexpr uint32_t kWidth = 4; + constexpr uint32_t kHeight = 4; + const bool hasDepth = !utils::IsStencilOnlyFormat(GetParam().mTextureFormat); + + wgpu::Texture depthStencilTexture = CreateDepthStencilTexture( + kWidth, kHeight, + wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc | + wgpu::TextureUsage::CopyDst); + + if (hasDepth) { + 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.UnsetDepthStencilLoadStoreOpsForFormat(GetParam().mTextureFormat); + passDescriptor.cDepthStencilAttachmentInfo.depthClearValue = 0.7; + + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); + pass.End(); + + 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::BufferDescriptor descriptor; + descriptor.size = bufferCopyOffset + BufferSizeForTextureCopy( + kWidth, kHeight, 1, GetParam().mTextureFormat, + wgpu::TextureAspect::StencilOnly); + descriptor.usage = wgpu::BufferUsage::CopySrc; + descriptor.mappedAtCreation = true; + wgpu::Buffer srcBuffer = device.CreateBuffer(&descriptor); + uint8_t* mappedPtr = static_cast(srcBuffer.GetMappedRange(bufferCopyOffset)); + constexpr uint32_t kBytesPerRow = kTextureBytesPerRowAlignment; + for (uint32_t y = 0; y < kHeight; ++y) { + memcpy(mappedPtr + y * kBytesPerRow, stencilData.data() + y * kWidth, kWidth); + } + srcBuffer.Unmap(); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ImageCopyBuffer imageCopyBuffer = + utils::CreateImageCopyBuffer(srcBuffer, bufferCopyOffset, kBytesPerRow, kHeight); + wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture( + depthStencilTexture, 0, {0, 0, 0}, wgpu::TextureAspect::StencilOnly); + wgpu::Extent3D copySize = {kWidth, kHeight, 1}; + commandEncoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + } + + // 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; + renderPipelineDesc.vertex.module = mVertexModule; + renderPipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"( + @fragment fn main() { + })"); + renderPipelineDesc.cFragment.targetCount = 0; + wgpu::DepthStencilState* depthStencil = + renderPipelineDesc.EnableDepthStencil(GetParam().mTextureFormat); + depthStencil->stencilFront.passOp = wgpu::StencilOperation::DecrementClamp; + if (!hasDepth) { + depthStencil->depthWriteEnabled = false; + depthStencil->depthCompare = wgpu::CompareFunction::Always; + } + + 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; + passDescriptor.UnsetDepthStencilLoadStoreOpsForFormat(GetParam().mTextureFormat); + + // Draw the quad in the bottom left (two triangles). + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); + pass.SetPipeline(pipeline); + pass.Draw(6); + pass.End(); + + 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, wgpu::TextureAspect::StencilOnly); + + if (hasDepth) { + ExpectAttachmentDepthTestData(depthStencilTexture, GetParam().mTextureFormat, kWidth, + kHeight, 0, 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, // + }); + } + } +}; // Test copying the stencil-only aspect into a buffer. TEST_P(StencilCopyTests, FromStencilAspect) { - // TODO(crbug.com/dawn/1497): glReadPixels: GL error: HIGH: Invalid format and type combination. - DAWN_SUPPRESS_TEST_IF(IsANGLE()); + constexpr uint32_t kWidth = 4; + constexpr uint32_t kHeight = 4; + constexpr uint32_t kTestLevel = 0; + constexpr uint32_t kBufferCopyOffset = 0; + DoCopyFromStencilTest(kBufferCopyOffset, kWidth, kHeight, kTestLevel); +} - // TODO(crbug.com/dawn/667): Work around the fact that some platforms are unable to read - // stencil. - DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_depth_stencil_read")); +// Test copying the stencil-only aspect into a buffer at a non-zero offset +TEST_P(StencilCopyTests, FromStencilAspectAtNonZeroOffset) { + // TODO(crbug.com/dawn/727): currently this test fails on many D3D12 drivers as there is a bug + // in the implementation of texture-to-buffer copies with depth stencil textures on D3D12. + DAWN_SUPPRESS_TEST_IF(IsD3D12()); 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, - wgpu::TextureAspect::StencilOnly); + constexpr uint32_t kTestLevel = 0; + constexpr std::array kBufferCopyOffsets = {4u, 512u}; + for (uint32_t offset : kBufferCopyOffsets) { + DoCopyFromStencilTest(offset, kWidth, kHeight, kTestLevel); + } } // Test copying the non-zero mip, stencil-only aspect into a buffer. TEST_P(StencilCopyTests, FromNonZeroMipStencilAspect) { - // TODO(crbug.com/dawn/704): Readback after clear via stencil copy does not work - // on some Intel drivers. - DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel()); - - // TODO(crbug.com/dawn/1497): glReadPixels: GL error: HIGH: Invalid format and type combination. - DAWN_SUPPRESS_TEST_IF(IsANGLE()); - - // TODO(crbug.com/dawn/667): Work around some platforms' inability to read back stencil. - DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_depth_stencil_read")); - - 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, - wgpu::TextureAspect::StencilOnly); + constexpr uint32_t kWidth = 9; + constexpr uint32_t kHeight = 9; + constexpr uint32_t kTestLevel = 1; + constexpr uint32_t kBufferCopyOffset = 0; + DoCopyFromStencilTest(kBufferCopyOffset, kWidth, kHeight, kTestLevel); } -// Test copying to the stencil-aspect of a buffer +// Test copying to the stencil-aspect of a texture TEST_P(StencilCopyTests, ToStencilAspect) { - // Copies to a single aspect are unsupported on OpenGL. - DAWN_TEST_UNSUPPORTED_IF(IsOpenGL()); - DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES()); + constexpr uint32_t kBufferCopyOffset = 0; + DoCopyToStencilTest(kBufferCopyOffset); +} - // TODO(crbug.com/dawn/704): Readback after clear via stencil copy does not work - // on some Intel drivers. - DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel()); +// Test copying to the stencil-aspect of a texture at non-zero offset +TEST_P(StencilCopyTests, ToStencilAspectAtNonZeroOffset) { + // TODO(crbug.com/dawn/727): currently this test fails on many D3D12 drivers as there is a bug + // in the implementation of texture-to-buffer copies with depth stencil textures on D3D12. + DAWN_SUPPRESS_TEST_IF(IsD3D12()); - // TODO(crbug.com/dawn/1273): Fails on Win11 with D3D12 debug layer and full validation - DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsBackendValidationEnabled()); - - // Create a stencil texture - constexpr uint32_t kWidth = 4; - constexpr uint32_t kHeight = 4; - const bool hasDepth = !utils::IsStencilOnlyFormat(GetParam().mTextureFormat); - - wgpu::Texture depthStencilTexture = - CreateDepthStencilTexture(kWidth, kHeight, - wgpu::TextureUsage::RenderAttachment | - wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst); - - if (hasDepth) { - 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.UnsetDepthStencilLoadStoreOpsForFormat(GetParam().mTextureFormat); - passDescriptor.cDepthStencilAttachmentInfo.depthClearValue = 0.7; - - wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); - pass.End(); - - 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::ImageCopyTexture stencilDataCopyTexture = utils::CreateImageCopyTexture( - depthStencilTexture, 0, {0, 0, 0}, wgpu::TextureAspect::StencilOnly); - - wgpu::Extent3D writeSize = {kWidth, kHeight, 1}; - queue.WriteTexture(&stencilDataCopyTexture, 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; - renderPipelineDesc.vertex.module = mVertexModule; - renderPipelineDesc.cFragment.module = utils::CreateShaderModule(device, R"( - @fragment fn main() { - })"); - renderPipelineDesc.cFragment.targetCount = 0; - wgpu::DepthStencilState* depthStencil = - renderPipelineDesc.EnableDepthStencil(GetParam().mTextureFormat); - depthStencil->stencilFront.passOp = wgpu::StencilOperation::DecrementClamp; - if (!hasDepth) { - depthStencil->depthWriteEnabled = false; - depthStencil->depthCompare = wgpu::CompareFunction::Always; - } - - 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; - passDescriptor.UnsetDepthStencilLoadStoreOpsForFormat(GetParam().mTextureFormat); - - // Draw the quad in the bottom left (two triangles). - wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); - pass.SetPipeline(pipeline); - pass.Draw(6); - pass.End(); - - 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, - wgpu::TextureAspect::StencilOnly); - - if (hasDepth) { - ExpectAttachmentDepthTestData(depthStencilTexture, GetParam().mTextureFormat, kWidth, - kHeight, 0, 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, // - }); + constexpr std::array kBufferCopyOffsets = {8, 512}; + for (uint32_t offset : kBufferCopyOffsets) { + DoCopyToStencilTest(offset); } }