// 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 "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" #include "utils/DawnHelpers.h" class TextureZeroInitTest : public DawnTest { protected: void SetUp() override { DawnTest::SetUp(); } dawn::TextureDescriptor CreateTextureDescriptor(uint32_t mipLevelCount, uint32_t arrayLayerCount, dawn::TextureUsage usage, dawn::TextureFormat format) { dawn::TextureDescriptor descriptor; descriptor.dimension = dawn::TextureDimension::e2D; descriptor.size.width = kSize; descriptor.size.height = kSize; descriptor.size.depth = 1; descriptor.arrayLayerCount = arrayLayerCount; descriptor.sampleCount = 1; descriptor.format = format; descriptor.mipLevelCount = mipLevelCount; descriptor.usage = usage; return descriptor; } dawn::TextureViewDescriptor CreateTextureViewDescriptor(uint32_t baseMipLevel, uint32_t baseArrayLayer) { dawn::TextureViewDescriptor descriptor; descriptor.format = kColorFormat; descriptor.baseArrayLayer = baseArrayLayer; descriptor.arrayLayerCount = 1; descriptor.baseMipLevel = baseMipLevel; descriptor.mipLevelCount = 1; descriptor.dimension = dawn::TextureViewDimension::e2D; return descriptor; } dawn::RenderPipeline CreatePipelineForTest() { utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); const char* vs = R"(#version 450 const vec2 pos[6] = vec2[6](vec2(-1.0f, -1.0f), vec2(-1.0f, 1.0f), vec2( 1.0f, -1.0f), vec2( 1.0f, 1.0f), vec2(-1.0f, 1.0f), vec2( 1.0f, -1.0f) ); void main() { gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); })"; pipelineDescriptor.cVertexStage.module = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vs); const char* fs = R"(#version 450 layout(location = 0) out vec4 fragColor; void main() { fragColor = vec4(1.0, 0.0, 0.0, 1.0); })"; pipelineDescriptor.cFragmentStage.module = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fs); pipelineDescriptor.cDepthStencilState.depthCompare = dawn::CompareFunction::Equal; pipelineDescriptor.cDepthStencilState.stencilFront.compare = dawn::CompareFunction::Equal; pipelineDescriptor.depthStencilState = &pipelineDescriptor.cDepthStencilState; return device.CreateRenderPipeline(&pipelineDescriptor); } constexpr static uint32_t kSize = 128; constexpr static dawn::TextureFormat kColorFormat = dawn::TextureFormat::RGBA8Unorm; constexpr static dawn::TextureFormat kDepthStencilFormat = dawn::TextureFormat::Depth24PlusStencil8; constexpr static dawn::TextureFormat kNonrenderableColorFormat = dawn::TextureFormat::RGBA8Snorm; }; // This tests that the code path of CopyTextureToBuffer clears correctly to Zero after first usage TEST_P(TextureZeroInitTest, CopyTextureToBufferSource) { dawn::TextureDescriptor descriptor = CreateTextureDescriptor( 1, 1, dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopySrc, kColorFormat); dawn::Texture texture = device.CreateTexture(&descriptor); // Texture's first usage is in EXPECT_PIXEL_RGBA8_EQ's call to CopyTextureToBuffer RGBA8 filledWithZeros(0, 0, 0, 0); EXPECT_LAZY_CLEAR(1u, EXPECT_PIXEL_RGBA8_EQ(filledWithZeros, texture, 0, 0)); } // Test that non-zero mip level clears subresource to Zero after first use // This goes through the BeginRenderPass's code path TEST_P(TextureZeroInitTest, RenderingMipMapClearsToZero) { dawn::TextureDescriptor descriptor = CreateTextureDescriptor( 4, 1, dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopySrc, kColorFormat); dawn::Texture texture = device.CreateTexture(&descriptor); dawn::TextureViewDescriptor viewDescriptor = CreateTextureViewDescriptor(2, 0); dawn::TextureView view = texture.CreateView(&viewDescriptor); utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat); renderPass.renderPassInfo.cColorAttachmentsInfoPtr[0]->attachment = view; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); { // Texture's first usage is in BeginRenderPass's call to RecordRenderPass dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.EndPass(); } dawn::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); uint32_t mipSize = kSize >> 2; std::vector expected(mipSize * mipSize, {0, 0, 0, 0}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, mipSize, mipSize, 2, 0); } // Test that non-zero array layers clears subresource to Zero after first use. // This goes through the BeginRenderPass's code path TEST_P(TextureZeroInitTest, RenderingArrayLayerClearsToZero) { dawn::TextureDescriptor descriptor = CreateTextureDescriptor( 1, 4, dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopySrc, kColorFormat); dawn::Texture texture = device.CreateTexture(&descriptor); dawn::TextureViewDescriptor viewDescriptor = CreateTextureViewDescriptor(0, 2); dawn::TextureView view = texture.CreateView(&viewDescriptor); utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat); renderPass.renderPassInfo.cColorAttachmentsInfoPtr[0]->attachment = view; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); { dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.EndPass(); } dawn::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); std::vector expected(kSize * kSize, {0, 0, 0, 0}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, kSize, kSize, 0, 2); } // This tests CopyBufferToTexture fully overwrites copy so lazy init is not needed. // TODO(natlee@microsoft.com): Add backdoor to dawn native to query the number of zero-inited // subresources TEST_P(TextureZeroInitTest, CopyBufferToTexture) { dawn::TextureDescriptor descriptor = CreateTextureDescriptor( 4, 1, dawn::TextureUsage::CopyDst | dawn::TextureUsage::Sampled | dawn::TextureUsage::CopySrc, kColorFormat); dawn::Texture texture = device.CreateTexture(&descriptor); std::vector data(4 * kSize * kSize, 100); dawn::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), dawn::BufferUsage::CopySrc); dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); dawn::Extent3D copySize = {kSize, kSize, 1}; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); dawn::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); std::vector expected(kSize * kSize, {100, 100, 100, 100}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 0); } // Test for a copy only to a subset of the subresource, lazy init is necessary to clear the other // half. TEST_P(TextureZeroInitTest, CopyBufferToTextureHalf) { dawn::TextureDescriptor descriptor = CreateTextureDescriptor( 4, 1, dawn::TextureUsage::CopyDst | dawn::TextureUsage::Sampled | dawn::TextureUsage::CopySrc, kColorFormat); dawn::Texture texture = device.CreateTexture(&descriptor); std::vector data(4 * kSize * kSize, 100); dawn::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), dawn::BufferUsage::CopySrc); dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); dawn::Extent3D copySize = {kSize / 2, kSize, 1}; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); dawn::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); std::vector expected100((kSize / 2) * kSize, {100, 100, 100, 100}); std::vector expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0}); // first half filled with 100, by the buffer data EXPECT_TEXTURE_RGBA8_EQ(expected100.data(), texture, 0, 0, kSize / 2, kSize, 0, 0); // second half should be cleared EXPECT_TEXTURE_RGBA8_EQ(expectedZeros.data(), texture, kSize / 2, 0, kSize / 2, kSize, 0, 0); } // This tests CopyTextureToTexture fully overwrites copy so lazy init is not needed. TEST_P(TextureZeroInitTest, CopyTextureToTexture) { dawn::TextureDescriptor srcDescriptor = CreateTextureDescriptor( 1, 1, dawn::TextureUsage::Sampled | dawn::TextureUsage::CopySrc, kColorFormat); dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); dawn::TextureCopyView srcTextureCopyView = utils::CreateTextureCopyView(srcTexture, 0, 0, {0, 0, 0}); dawn::TextureDescriptor dstDescriptor = CreateTextureDescriptor(1, 1, dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopyDst | dawn::TextureUsage::CopySrc, kColorFormat); dawn::Texture dstTexture = device.CreateTexture(&dstDescriptor); dawn::TextureCopyView dstTextureCopyView = utils::CreateTextureCopyView(dstTexture, 0, 0, {0, 0, 0}); dawn::Extent3D copySize = {kSize, kSize, 1}; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); dawn::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); std::vector expected(kSize * kSize, {0, 0, 0, 0}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, 0, 0, kSize, kSize, 0, 0); } // This Tests the CopyTextureToTexture's copy only to a subset of the subresource, lazy init is // necessary to clear the other half. TEST_P(TextureZeroInitTest, CopyTextureToTextureHalf) { dawn::TextureDescriptor srcDescriptor = CreateTextureDescriptor( 1, 1, dawn::TextureUsage::Sampled | dawn::TextureUsage::CopySrc | dawn::TextureUsage::CopyDst, kColorFormat); dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); // fill srcTexture with 100 { std::vector data(4 * kSize * kSize, 100); dawn::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), dawn::BufferUsage::CopySrc); dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(srcTexture, 0, 0, {0, 0, 0}); dawn::Extent3D copySize = {kSize, kSize, 1}; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); dawn::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); } dawn::TextureCopyView srcTextureCopyView = utils::CreateTextureCopyView(srcTexture, 0, 0, {0, 0, 0}); dawn::TextureDescriptor dstDescriptor = CreateTextureDescriptor(1, 1, dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopyDst | dawn::TextureUsage::CopySrc, kColorFormat); dawn::Texture dstTexture = device.CreateTexture(&dstDescriptor); dawn::TextureCopyView dstTextureCopyView = utils::CreateTextureCopyView(dstTexture, 0, 0, {0, 0, 0}); dawn::Extent3D copySize = {kSize / 2, kSize, 1}; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); dawn::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); std::vector expectedWithZeros((kSize / 2) * kSize, {0, 0, 0, 0}); std::vector expectedWith100(kSize * kSize, {100, 100, 100, 100}); EXPECT_TEXTURE_RGBA8_EQ(expectedWith100.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); EXPECT_TEXTURE_RGBA8_EQ(expectedWith100.data(), dstTexture, 0, 0, kSize / 2, kSize, 0, 0); EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), dstTexture, kSize / 2, 0, kSize / 2, kSize, 0, 0); } // This tests the texture with depth attachment and load op load will init depth stencil texture to // 0s. TEST_P(TextureZeroInitTest, RenderingLoadingDepth) { dawn::TextureDescriptor srcDescriptor = CreateTextureDescriptor(1, 1, dawn::TextureUsage::CopySrc | dawn::TextureUsage::CopyDst | dawn::TextureUsage::OutputAttachment, kColorFormat); dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); dawn::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( 1, 1, dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopySrc, kDepthStencilFormat); dawn::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = dawn::LoadOp::Load; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = dawn::LoadOp::Clear; renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 0; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest()); pass.Draw(6, 1, 0, 0); pass.EndPass(); dawn::CommandBuffer commandBuffer = encoder.Finish(); // Expect 1 lazy clear for the depthStencilTexture EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); // Expect the texture to be red because depth test passed. std::vector expected(kSize * kSize, {255, 0, 0, 255}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); } // This tests the texture with stencil attachment and load op load will init depth stencil texture // to 0s. TEST_P(TextureZeroInitTest, RenderingLoadingStencil) { dawn::TextureDescriptor srcDescriptor = CreateTextureDescriptor(1, 1, dawn::TextureUsage::CopySrc | dawn::TextureUsage::CopyDst | dawn::TextureUsage::OutputAttachment, kColorFormat); dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); dawn::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( 1, 1, dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopySrc, kDepthStencilFormat); dawn::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = dawn::LoadOp::Clear; renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.0f; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = dawn::LoadOp::Load; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest()); pass.Draw(6, 1, 0, 0); pass.EndPass(); dawn::CommandBuffer commandBuffer = encoder.Finish(); // Expect 1 lazy clear for depthStencilTexture. EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); // Expect the texture to be red because stencil test passed. std::vector expected(kSize * kSize, {255, 0, 0, 255}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); } // This tests the texture with depth stencil attachment and load op load will init depth stencil // texture to 0s. TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencil) { dawn::TextureDescriptor srcDescriptor = CreateTextureDescriptor(1, 1, dawn::TextureUsage::CopySrc | dawn::TextureUsage::CopyDst | dawn::TextureUsage::OutputAttachment, kColorFormat); dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); dawn::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( 1, 1, dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopySrc, kDepthStencilFormat); dawn::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = dawn::LoadOp::Load; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = dawn::LoadOp::Load; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest()); pass.Draw(6, 1, 0, 0); pass.EndPass(); dawn::CommandBuffer commandBuffer = encoder.Finish(); // Expect 1 lazy clear for depthStencilTexture. EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); // Expect the texture to be red because both depth and stencil tests passed. std::vector expected(kSize * kSize, {255, 0, 0, 255}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); } // This tests the color attachments clear to 0s TEST_P(TextureZeroInitTest, ColorAttachmentsClear) { dawn::TextureDescriptor descriptor = CreateTextureDescriptor( 1, 1, dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopySrc, kColorFormat); dawn::Texture texture = device.CreateTexture(&descriptor); utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat); renderPass.renderPassInfo.cColorAttachmentsInfoPtr[0]->loadOp = dawn::LoadOp::Load; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.EndPass(); dawn::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); std::vector expected(kSize * kSize, {0, 0, 0, 0}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, kSize, kSize, 0, 0); } // This tests the clearing of sampled textures in render pass TEST_P(TextureZeroInitTest, RenderPassSampledTextureClear) { // Create needed resources dawn::TextureDescriptor descriptor = CreateTextureDescriptor(1, 1, dawn::TextureUsage::Sampled, kColorFormat); dawn::Texture texture = device.CreateTexture(&descriptor); dawn::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor( 1, 1, dawn::TextureUsage::CopySrc | dawn::TextureUsage::OutputAttachment, kColorFormat); dawn::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); dawn::Sampler sampler = device.CreateSampler(&samplerDesc); dawn::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( device, {{0, dawn::ShaderStage::Fragment, dawn::BindingType::Sampler}, {1, dawn::ShaderStage::Fragment, dawn::BindingType::SampledTexture}}); // Create render pipeline utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); renderPipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bindGroupLayout); renderPipelineDescriptor.cVertexStage.module = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(#version 450 const vec2 pos[6] = vec2[6](vec2(-1.0f, -1.0f), vec2(-1.0f, 1.0f), vec2( 1.0f, -1.0f), vec2( 1.0f, 1.0f), vec2(-1.0f, 1.0f), vec2( 1.0f, -1.0f) ); void main() { gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); })"); renderPipelineDescriptor.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 vec4 fragColor; void main() { fragColor = texelFetch(sampler2D(texture0, sampler0), ivec2(gl_FragCoord), 0); })"); renderPipelineDescriptor.cColorStates[0]->format = kColorFormat; dawn::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor); // Create bindgroup dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bindGroupLayout, {{0, sampler}, {1, texture.CreateView()}}); // Encode pass and submit dawn::CommandEncoder encoder = device.CreateCommandEncoder(); utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()}); renderPassDesc.cColorAttachmentsInfoPtr[0]->clearColor = {1.0, 1.0, 1.0, 1.0}; renderPassDesc.cColorAttachmentsInfoPtr[0]->loadOp = dawn::LoadOp::Clear; dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); pass.SetPipeline(renderPipeline); pass.SetBindGroup(0, bindGroup, 0, nullptr); pass.Draw(6, 1, 0, 0); pass.EndPass(); dawn::CommandBuffer commands = encoder.Finish(); // Expect 1 lazy clear for sampled texture EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); // Expect the rendered texture to be cleared std::vector expectedWithZeros(kSize * kSize, {0, 0, 0, 0}); EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0, kSize, kSize, 0, 0); } // This tests the clearing of sampled textures during compute pass TEST_P(TextureZeroInitTest, ComputePassSampledTextureClear) { // Create needed resources dawn::TextureDescriptor descriptor = CreateTextureDescriptor(1, 1, dawn::TextureUsage::Sampled, kColorFormat); descriptor.size.width = 1; descriptor.size.height = 1; dawn::Texture texture = device.CreateTexture(&descriptor); uint32_t bufferSize = 4 * sizeof(uint32_t); dawn::BufferDescriptor bufferDescriptor; bufferDescriptor.size = bufferSize; bufferDescriptor.usage = dawn::BufferUsage::CopySrc | dawn::BufferUsage::Storage | dawn::BufferUsage::CopyDst; dawn::Buffer bufferTex = device.CreateBuffer(&bufferDescriptor); // Add data to buffer to ensure it is initialized uint32_t data = 100; bufferTex.SetSubData(0, sizeof(data), &data); dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); dawn::Sampler sampler = device.CreateSampler(&samplerDesc); dawn::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( device, {{0, dawn::ShaderStage::Compute, dawn::BindingType::SampledTexture}, {1, dawn::ShaderStage::Compute, dawn::BindingType::StorageBuffer}, {2, dawn::ShaderStage::Compute, dawn::BindingType::Sampler}}); // Create compute pipeline dawn::ComputePipelineDescriptor computePipelineDescriptor; computePipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bindGroupLayout); dawn::PipelineStageDescriptor computeStage; const char* cs = R"(#version 450 layout(binding = 0) uniform texture2D sampleTex; layout(std430, binding = 1) buffer BufferTex { vec4 result; } bufferTex; layout(binding = 2) uniform sampler sampler0; void main() { bufferTex.result = texelFetch(sampler2D(sampleTex, sampler0), ivec2(0,0), 0); })"; computeStage.module = utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, cs); computeStage.entryPoint = "main"; computePipelineDescriptor.computeStage = &computeStage; dawn::ComputePipeline computePipeline = device.CreateComputePipeline(&computePipelineDescriptor); // Create bindgroup dawn::BindGroup bindGroup = utils::MakeBindGroup( device, bindGroupLayout, {{0, texture.CreateView()}, {1, bufferTex, 0, bufferSize}, {2, sampler}}); // Encode the pass and submit dawn::CommandEncoder encoder = device.CreateCommandEncoder(); dawn::ComputePassEncoder pass = encoder.BeginComputePass(); pass.SetPipeline(computePipeline); pass.SetBindGroup(0, bindGroup, 0, nullptr); pass.Dispatch(1, 1, 1); pass.EndPass(); dawn::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); // Expect the buffer to be zeroed out by the compute pass std::vector expectedWithZeros(bufferSize, 0); EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferTex, 0, 4); } // This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures TEST_P(TextureZeroInitTest, NonRenderableTextureClear) { // skip test for other backends since they are not implemented yet DAWN_SKIP_TEST_IF(IsOpenGL()); dawn::TextureDescriptor descriptor = CreateTextureDescriptor(1, 1, dawn::TextureUsage::CopySrc, kNonrenderableColorFormat); dawn::Texture texture = device.CreateTexture(&descriptor); // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy uint32_t bufferSize = 4 * kSize * kSize; std::vector data(bufferSize, 100); dawn::Buffer bufferDst = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), dawn::BufferUsage::CopySrc); dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(bufferDst, 0, 0, 0); dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); dawn::Extent3D copySize = {kSize, kSize, 1}; dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); dawn::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); std::vector expectedWithZeros(bufferSize, 0); EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, 8); } DAWN_INSTANTIATE_TEST( TextureZeroInitTest, ForceWorkarounds(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}), ForceWorkarounds(OpenGLBackend, {"nonzero_clear_resources_on_creation_for_testing"}), ForceWorkarounds(VulkanBackend, {"nonzero_clear_resources_on_creation_for_testing"}));