// 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 "common/Math.h" #include "utils/ComboRenderPipelineDescriptor.h" #include "utils/TestUtils.h" #include "utils/WGPUHelpers.h" #define EXPECT_LAZY_CLEAR(N, statement) \ do { \ if (UsesWire()) { \ statement; \ } else { \ size_t lazyClearsBefore = dawn_native::GetLazyClearCountForTesting(device.Get()); \ statement; \ size_t lazyClearsAfter = dawn_native::GetLazyClearCountForTesting(device.Get()); \ EXPECT_EQ(N, lazyClearsAfter - lazyClearsBefore); \ } \ } while (0) class TextureZeroInitTest : public DawnTest { protected: void SetUp() override { DawnTest::SetUp(); DAWN_SKIP_TEST_IF(UsesWire()); } wgpu::TextureDescriptor CreateTextureDescriptor(uint32_t mipLevelCount, uint32_t arrayLayerCount, wgpu::TextureUsage usage, wgpu::TextureFormat format) { wgpu::TextureDescriptor descriptor; descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = kSize; descriptor.size.height = kSize; descriptor.size.depthOrArrayLayers = arrayLayerCount; descriptor.sampleCount = 1; descriptor.format = format; descriptor.mipLevelCount = mipLevelCount; descriptor.usage = usage; return descriptor; } wgpu::TextureViewDescriptor CreateTextureViewDescriptor( uint32_t baseMipLevel, uint32_t baseArrayLayer, wgpu::TextureFormat format = kColorFormat) { wgpu::TextureViewDescriptor descriptor; descriptor.format = format; descriptor.baseArrayLayer = baseArrayLayer; descriptor.arrayLayerCount = 1; descriptor.baseMipLevel = baseMipLevel; descriptor.mipLevelCount = 1; descriptor.dimension = wgpu::TextureViewDimension::e2D; return descriptor; } wgpu::RenderPipeline CreatePipelineForTest(float depth = 0.f) { utils::ComboRenderPipelineDescriptor2 pipelineDescriptor; pipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest(depth); const char* fs = R"( [[location(0)]] var fragColor : vec4; [[stage(fragment)]] fn main() -> void { fragColor = vec4(1.0, 0.0, 0.0, 1.0); } )"; pipelineDescriptor.cFragment.module = utils::CreateShaderModuleFromWGSL(device, fs); wgpu::DepthStencilState* depthStencil = pipelineDescriptor.EnableDepthStencil(); depthStencil->depthCompare = wgpu::CompareFunction::Equal; depthStencil->stencilFront.compare = wgpu::CompareFunction::Equal; return device.CreateRenderPipeline2(&pipelineDescriptor); } wgpu::ShaderModule CreateBasicVertexShaderForTest(float depth = 0.f) { std::string source = R"( const pos : array, 6> = array, 6>( vec2(-1.0, -1.0), vec2(-1.0, 1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0), vec2(-1.0, 1.0), vec2( 1.0, -1.0) ); [[builtin(vertex_index)]] var VertexIndex : u32; [[builtin(position)]] var Position : vec4; [[stage(vertex)]] fn main() -> void { Position = vec4(pos[VertexIndex], )" + std::to_string(depth) + R"(, 1.0); })"; return utils::CreateShaderModuleFromWGSL(device, source.c_str()); } wgpu::ShaderModule CreateSampledTextureFragmentShaderForTest() { return utils::CreateShaderModuleFromWGSL(device, R"( [[group(0), binding(0)]] var texture0 : texture_2d; [[builtin(frag_coord)]] var FragCoord : vec4; [[location(0)]] var fragColor : vec4; [[stage(fragment)]] fn main() -> void { fragColor = textureLoad(texture0, vec2(FragCoord.xy), 0); } )"); } constexpr static uint32_t kSize = 128; constexpr static uint32_t kUnalignedSize = 127; // All texture formats used (RGBA8Unorm, Depth24PlusStencil8, and RGBA8Snorm, BC formats) // have the same block byte size of 4. constexpr static uint32_t kFormatBlockByteSize = 4; constexpr static wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; constexpr static wgpu::TextureFormat kDepthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; constexpr static wgpu::TextureFormat kNonrenderableColorFormat = wgpu::TextureFormat::RGBA8Snorm; }; // This tests that the code path of CopyTextureToBuffer clears correctly to Zero after first usage TEST_P(TextureZeroInitTest, CopyTextureToBufferSource) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::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)); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); } // This tests that the code path of CopyTextureToBuffer with multiple texture array layers clears // correctly to Zero after first usage TEST_P(TextureZeroInitTest, CopyMultipleTextureArrayLayersToBufferSource) { constexpr uint32_t kArrayLayers = 6u; const wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 1, kArrayLayers, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); const uint32_t bytesPerRow = utils::GetMinimumBytesPerRow(kColorFormat, kSize); const uint32_t rowsPerImage = kSize; wgpu::BufferDescriptor bufferDescriptor; bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; bufferDescriptor.size = utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, {kSize, kSize, kArrayLayers}, kColorFormat); wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); const wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(buffer, 0, bytesPerRow, kSize); const wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); const wgpu::Extent3D copySize = {kSize, kSize, kArrayLayers}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size); wgpu::CommandBuffer commandBuffer = encoder.Finish(); // Expect texture to be lazy initialized. EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); // Expect texture subresource initialized to be true EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, kArrayLayers)); const std::vector kExpectedAllZero(kSize * kSize, {0, 0, 0, 0}); for (uint32_t layer = 0; layer < kArrayLayers; ++layer) { EXPECT_TEXTURE_RGBA8_EQ(kExpectedAllZero.data(), texture, 0, 0, kSize, kSize, 0, layer); } } // 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) { uint32_t baseMipLevel = 2; uint32_t levelCount = 4; uint32_t baseArrayLayer = 0; uint32_t layerCount = 1; wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( levelCount, layerCount, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); wgpu::TextureViewDescriptor viewDescriptor = CreateTextureViewDescriptor(baseMipLevel, baseArrayLayer); wgpu::TextureView view = texture.CreateView(&viewDescriptor); utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat); // Specify loadOp Load. Clear should be used to zero-initialize. renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load; // Specify non-zero clear color. It should still be cleared to zero. renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f}; renderPass.renderPassInfo.cColorAttachments[0].attachment = view; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { // Texture's first usage is in BeginRenderPass's call to RecordRenderPass wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.EndPass(); } wgpu::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, baseMipLevel, baseArrayLayer); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( renderPass.color.Get(), baseMipLevel, 1, baseArrayLayer, 1)); } // 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) { uint32_t baseMipLevel = 0; uint32_t levelCount = 1; uint32_t baseArrayLayer = 2; uint32_t layerCount = 4; wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( levelCount, layerCount, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); wgpu::TextureViewDescriptor viewDescriptor = CreateTextureViewDescriptor(baseMipLevel, baseArrayLayer); wgpu::TextureView view = texture.CreateView(&viewDescriptor); utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat); // Specify loadOp Load. Clear should be used to zero-initialize. renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load; // Specify non-zero clear color. It should still be cleared to zero. renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f}; renderPass.renderPassInfo.cColorAttachments[0].attachment = view; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.EndPass(); } wgpu::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, baseMipLevel, baseArrayLayer); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( renderPass.color.Get(), baseMipLevel, 1, baseArrayLayer, 1)); } // This tests CopyBufferToTexture fully overwrites copy so lazy init is not needed. TEST_P(TextureZeroInitTest, CopyBufferToTexture) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 4, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); std::vector data(kFormatBlockByteSize * kSize * kSize, 100); wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * sizeof(uint32_t)); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kSize, kSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size); wgpu::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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); } // Test for a copy only to a subset of the subresource, lazy init is necessary to clear the other // half. TEST_P(TextureZeroInitTest, CopyBufferToTextureHalf) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 4, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); std::vector data(kFormatBlockByteSize * kSize * kSize, 100); wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * sizeof(uint16_t)); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kSize / 2, kSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size); wgpu::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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); } // This tests CopyBufferToTexture fully overwrites a range of subresources, so lazy initialization // is needed for neither the subresources involved in the copy nor the other subresources. TEST_P(TextureZeroInitTest, CopyBufferToTextureMultipleArrayLayers) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 1, 6, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); constexpr uint32_t kBaseArrayLayer = 2u; constexpr uint32_t kCopyLayerCount = 3u; std::vector data(kFormatBlockByteSize * kSize * kSize * kCopyLayerCount, 100); wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); const wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * kFormatBlockByteSize, kSize); const wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, kBaseArrayLayer}); const wgpu::Extent3D copySize = {kSize, kSize, kCopyLayerCount}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size); wgpu::CommandBuffer commands = encoder.Finish(); // The copy overwrites the whole subresources so we don't need to do lazy initialization on // them. EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); // Expect texture subresource initialized to be true EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, kBaseArrayLayer, kCopyLayerCount)); const std::vector expected100(kSize * kSize, {100, 100, 100, 100}); for (uint32_t layer = kBaseArrayLayer; layer < kBaseArrayLayer + kCopyLayerCount; ++layer) { EXPECT_TEXTURE_RGBA8_EQ(expected100.data(), texture, 0, 0, kSize, kSize, 0, layer); } } // This tests CopyTextureToTexture fully overwrites copy so lazy init is not needed. TEST_P(TextureZeroInitTest, CopyTextureToTexture) { wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); wgpu::ImageCopyTexture srcImageCopyTexture = utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0}); wgpu::TextureDescriptor dstDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor); wgpu::ImageCopyTexture dstImageCopyTexture = utils::CreateImageCopyTexture(dstTexture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kSize, kSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, ©Size); wgpu::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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(dstTexture.Get(), 0, 1, 0, 1)); } // 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) { wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst, kColorFormat); wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); // fill srcTexture with 100 { std::vector data(kFormatBlockByteSize * kSize * kSize, 100); wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * kFormatBlockByteSize); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kSize, kSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size); wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); } wgpu::ImageCopyTexture srcImageCopyTexture = utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0}); wgpu::TextureDescriptor dstDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor); wgpu::ImageCopyTexture dstImageCopyTexture = utils::CreateImageCopyTexture(dstTexture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kSize / 2, kSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, ©Size); wgpu::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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(dstTexture.Get(), 0, 1, 0, 1)); } // This tests the texture with depth attachment and load op load will init depth stencil texture to // 0s. TEST_P(TextureZeroInitTest, RenderingLoadingDepth) { wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kDepthStencilFormat); wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; // Set clearDepth to non-zero. It should still be cleared to 0 by the loadOp. renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.5f; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 0; renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest()); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); // Expect 0 lazy clears, depth stencil texture will clear using loadop EXPECT_LAZY_CLEAR(0u, 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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); } // This tests the texture with stencil attachment and load op load will init depth stencil texture // to 0s. TEST_P(TextureZeroInitTest, RenderingLoadingStencil) { wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kDepthStencilFormat); wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.0f; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; // Set clearStencil to non-zero. It should still be cleared to 0 by the loadOp. renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 2; renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest()); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); // Expect 0 lazy clears, depth stencil texture will clear using loadop EXPECT_LAZY_CLEAR(0u, 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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); } // This tests the texture with depth stencil attachment and load op load will init depth stencil // texture to 0s. TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencil) { wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kDepthStencilFormat); wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest()); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); // Expect 0 lazy clears, depth stencil texture will clear using loadop EXPECT_LAZY_CLEAR(0u, 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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); } // Test that clear state is tracked independently for depth/stencil textures. TEST_P(TextureZeroInitTest, IndependentDepthStencilLoadAfterDiscard) { // TODO(enga): Figure out why this fails on Metal Intel. DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kDepthStencilFormat); wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); // Uninitialize only depth { // Clear the stencil to 2 and discard the depth { utils::ComboRenderPassDescriptor renderPassDescriptor({}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 2; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); } // "all" subresources are not initialized; Depth is not initialized EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); // Now load both depth and stencil. Depth should be cleared and stencil should stay the same // at 2. { wgpu::TextureDescriptor colorDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture colorTexture = device.CreateTexture(&colorDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest()); pass.SetStencilReference(2); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); // No lazy clear because depth will be cleared with a loadOp EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); // Expect the texture to be red because the depth and stencil tests passed. Depth was 0 // and stencil was 2. std::vector expected(kSize * kSize, {255, 0, 0, 255}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), colorTexture, 0, 0, kSize, kSize, 0, 0); } // Everything is initialized now EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms if (IsMetal() || IsVulkan() || IsD3D12()) { // Check by copy that the stencil data is 2. std::vector expected(kSize * kSize, 2); EXPECT_LAZY_CLEAR( 0u, EXPECT_TEXTURE_EQ(expected.data(), depthStencilTexture, 0, 0, kSize, kSize, 0, 0, wgpu::TextureAspect::StencilOnly)); } } // Uninitialize only stencil { // Clear the depth to 0.7 and discard the stencil. { utils::ComboRenderPassDescriptor renderPassDescriptor({}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.7; renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Clear; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); } // "all" subresources are not initialized; Stencil is not initialized EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); // Now load both depth and stencil. Stencil should be cleared and depth should stay the same // at 0.7. { wgpu::TextureDescriptor colorDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture colorTexture = device.CreateTexture(&colorDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest(0.7)); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); // No lazy clear because stencil will clear using a loadOp. EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); // Expect the texture to be red because both the depth a stencil tests passed. // Depth was 0.7 and stencil was 0 std::vector expected(kSize * kSize, {255, 0, 0, 255}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), colorTexture, 0, 0, kSize, kSize, 0, 0); } // Everything is initialized now EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms if (IsMetal() || IsVulkan() || IsD3D12()) { // Check by copy that the stencil data is 0. std::vector expected(kSize * kSize, 0); EXPECT_LAZY_CLEAR( 0u, EXPECT_TEXTURE_EQ(expected.data(), depthStencilTexture, 0, 0, kSize, kSize, 0, 0, wgpu::TextureAspect::StencilOnly)); } } } // Test that clear state is tracked independently for depth/stencil textures. // Lazy clear of the stencil aspect via copy should not touch depth. TEST_P(TextureZeroInitTest, IndependentDepthStencilCopyAfterDiscard) { // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms DAWN_SKIP_TEST_IF(!(IsMetal() || IsVulkan() || IsD3D12())); // TODO(enga): Figure out why this fails on Metal Intel. DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kDepthStencilFormat); wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); // Clear the depth to 0.3 and discard the stencil. { utils::ComboRenderPassDescriptor renderPassDescriptor({}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.3; renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Clear; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); } // "all" subresources are not initialized; Stencil is not initialized EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized( depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); // Check by copy that the stencil data is lazily cleared to 0. std::vector expected(kSize * kSize, 0); EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_EQ(expected.data(), depthStencilTexture, 0, 0, kSize, kSize, 0, 0, wgpu::TextureAspect::StencilOnly)); // Everything is initialized now EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly)); // Now load both depth and stencil. Stencil should be cleared and depth should stay the same // at 0.3. { wgpu::TextureDescriptor colorDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture colorTexture = device.CreateTexture(&colorDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest(0.3)); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); // No lazy clear because stencil will clear using a loadOp. EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); // Expect the texture to be red because both the depth a stencil tests passed. // Depth was 0.3 and stencil was 0 std::vector expected(kSize * kSize, {255, 0, 0, 255}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), colorTexture, 0, 0, kSize, kSize, 0, 0); } } // This tests the color attachments clear to 0s TEST_P(TextureZeroInitTest, ColorAttachmentsClear) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat); renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.EndPass(); wgpu::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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(renderPass.color.Get(), 0, 1, 0, 1)); } // This tests the clearing of sampled textures in render pass TEST_P(TextureZeroInitTest, RenderPassSampledTextureClear) { // Create needed resources wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::Sampled, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); // Create render pipeline utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor; renderPipelineDescriptor.cTargets[0].format = kColorFormat; renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest(); renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest(); wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline2(&renderPipelineDescriptor); // Create bindgroup wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), {{0, texture.CreateView()}}); // Encode pass and submit wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()}); renderPassDesc.cColorAttachments[0].clearColor = {1.0, 1.0, 1.0, 1.0}; renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); pass.SetPipeline(renderPipeline); pass.SetBindGroup(0, bindGroup); pass.Draw(6); pass.EndPass(); wgpu::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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(renderTexture.Get(), 0, 1, 0, 1)); } // This is a regression test for a bug where a texture wouldn't get clear for a pass if at least // one of its subresources was used as an attachment. It tests that if a texture is used as both // sampled and attachment (with LoadOp::Clear so the lazy clear can be skipped) then the sampled // subresource is correctly cleared. TEST_P(TextureZeroInitTest, TextureBothSampledAndAttachmentClear) { // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES. DAWN_SKIP_TEST_IF(IsOpenGLES()); // Create a 2D array texture, layer 0 will be used as attachment, layer 1 as sampled. wgpu::TextureDescriptor texDesc; texDesc.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc; texDesc.size = {1, 1, 2}; texDesc.format = wgpu::TextureFormat::RGBA8Unorm; wgpu::Texture texture = device.CreateTexture(&texDesc); wgpu::TextureViewDescriptor viewDesc; viewDesc.dimension = wgpu::TextureViewDimension::e2D; viewDesc.arrayLayerCount = 1; viewDesc.baseArrayLayer = 0; wgpu::TextureView attachmentView = texture.CreateView(&viewDesc); viewDesc.baseArrayLayer = 1; wgpu::TextureView sampleView = texture.CreateView(&viewDesc); // Create render pipeline utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor; renderPipelineDescriptor.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm; renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest(); renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest(); wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline2(&renderPipelineDescriptor); wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), {{0, sampleView}}); // Encode pass and submit wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); utils::ComboRenderPassDescriptor renderPassDesc({attachmentView}); renderPassDesc.cColorAttachments[0].clearColor = {1.0, 1.0, 1.0, 1.0}; renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); pass.SetPipeline(renderPipeline); pass.SetBindGroup(0, bindGroup); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commands = encoder.Finish(); // Expect the lazy clear for the sampled subresource. EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); // Expect both subresources to be zero: the sampled one with lazy-clearing and the attachment // because it sampled the lazy-cleared sampled subresource. EXPECT_TEXTURE_RGBA8_EQ(&RGBA8::kZero, texture, 0, 0, 1, 1, 0, 0); EXPECT_TEXTURE_RGBA8_EQ(&RGBA8::kZero, texture, 0, 0, 1, 1, 0, 1); // The whole texture is now initialized. EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 2)); } // This tests the clearing of sampled textures during compute pass TEST_P(TextureZeroInitTest, ComputePassSampledTextureClear) { // Create needed resources wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::Sampled, kColorFormat); descriptor.size.width = 1; descriptor.size.height = 1; wgpu::Texture texture = device.CreateTexture(&descriptor); uint32_t bufferSize = kFormatBlockByteSize * sizeof(uint32_t); wgpu::BufferDescriptor bufferDescriptor; bufferDescriptor.size = bufferSize; bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst; wgpu::Buffer bufferTex = device.CreateBuffer(&bufferDescriptor); // Add data to buffer to ensure it is initialized uint32_t data = 100; queue.WriteBuffer(bufferTex, 0, &data, sizeof(data)); wgpu::Sampler sampler = device.CreateSampler(); // Create compute pipeline wgpu::ComputePipelineDescriptor computePipelineDescriptor; wgpu::ProgrammableStageDescriptor computeStage; const char* cs = R"( [[group(0), binding(0)]] var tex : texture_2d; [[block]] struct Result { value : vec4; }; [[group(0), binding(1)]] var result : [[access(read_write)]] Result; [[stage(compute)]] fn main() -> void { result.value = textureLoad(tex, vec2(0,0), 0); } )"; computePipelineDescriptor.computeStage.module = utils::CreateShaderModuleFromWGSL(device, cs); computePipelineDescriptor.computeStage.entryPoint = "main"; wgpu::ComputePipeline computePipeline = device.CreateComputePipeline(&computePipelineDescriptor); // Create bindgroup wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, computePipeline.GetBindGroupLayout(0), {{0, texture.CreateView()}, {1, bufferTex, 0, bufferSize}}); // Encode the pass and submit wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.SetPipeline(computePipeline); pass.SetBindGroup(0, bindGroup); pass.Dispatch(1); pass.EndPass(); wgpu::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, kFormatBlockByteSize); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); } // This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures TEST_P(TextureZeroInitTest, NonRenderableTextureClear) { // TODO(crbug.com/dawn/667): Work around the fact that some platforms do not support reading // from Snorm textures. DAWN_SKIP_TEST_IF(HasToggleEnabled("disable_snorm_read")); wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc, kNonrenderableColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy uint32_t bytesPerRow = Align(kSize * kFormatBlockByteSize, kTextureBytesPerRowAlignment); uint32_t bufferSize = bytesPerRow * kSize; std::vector data(bufferSize, 100); wgpu::Buffer bufferDst = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(bufferDst, 0, bytesPerRow); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kSize, kSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size); wgpu::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, kSize); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); } // This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures TEST_P(TextureZeroInitTest, NonRenderableTextureClearUnalignedSize) { // TODO(crbug.com/dawn/667): Work around the fact that some platforms do not support reading // from Snorm textures. DAWN_SKIP_TEST_IF(HasToggleEnabled("disable_snorm_read")); wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc, kNonrenderableColorFormat); descriptor.size.width = kUnalignedSize; descriptor.size.height = kUnalignedSize; wgpu::Texture texture = device.CreateTexture(&descriptor); // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy uint32_t bytesPerRow = Align(kUnalignedSize * kFormatBlockByteSize, kTextureBytesPerRowAlignment); uint32_t bufferSize = bytesPerRow * kUnalignedSize; std::vector data(bufferSize, 100); wgpu::Buffer bufferDst = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(bufferDst, 0, bytesPerRow); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kUnalignedSize, kUnalignedSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size); wgpu::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, kUnalignedSize); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); } // This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures // with more than 1 array layers TEST_P(TextureZeroInitTest, NonRenderableTextureClearWithMultiArrayLayers) { // TODO(crbug.com/dawn/667): Work around the fact that some platforms do not support reading // from Snorm textures. DAWN_SKIP_TEST_IF(HasToggleEnabled("disable_snorm_read")); wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(1, 2, wgpu::TextureUsage::CopySrc, kNonrenderableColorFormat); wgpu::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 = kFormatBlockByteSize * kSize * kSize; std::vector data(bufferSize, 100); wgpu::Buffer bufferDst = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(bufferDst, 0, kSize * kFormatBlockByteSize); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 1}); wgpu::Extent3D copySize = {kSize, kSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size); wgpu::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); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 1, 1)); } // This tests that storeOp clear resets resource state to uninitialized. // Start with a sample texture that is initialized with data. // Then expect the render texture to not store the data from sample texture // because it will be lazy cleared by the EXPECT_TEXTURE_RGBA8_EQ call. TEST_P(TextureZeroInitTest, RenderPassStoreOpClear) { // Create needed resources wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopyDst, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); // Fill the sample texture with data std::vector data(kFormatBlockByteSize * kSize * kSize, 1); wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * kFormatBlockByteSize); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kSize, kSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size); wgpu::CommandBuffer commands = encoder.Finish(); // Expect 0 lazy clears because the texture will be completely copied to EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); // Create render pipeline utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor; renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest(); renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest(); renderPipelineDescriptor.cTargets[0].format = kColorFormat; wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline2(&renderPipelineDescriptor); // Create bindgroup wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), {{0, texture.CreateView()}}); // Encode pass and submit encoder = device.CreateCommandEncoder(); utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()}); renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0}; renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear; wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); pass.SetPipeline(renderPipeline); pass.SetBindGroup(0, bindGroup); pass.Draw(6); pass.EndPass(); commands = encoder.Finish(); // Expect 0 lazy clears, sample texture is initialized by copyBufferToTexture and render texture // is cleared by loadop EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); // Expect the rendered texture to be cleared std::vector expectedWithZeros(kSize * kSize, {0, 0, 0, 0}); EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0, kSize, kSize, 0, 0)); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(renderTexture.Get(), 0, 1, 0, 1)); } // This tests storeOp Clear on depth and stencil textures. // We put the depth stencil texture through 2 passes: // 1) LoadOp::Clear and StoreOp::Clear, fail the depth and stencil test set in the render pipeline. // This means nothing is drawn and subresource is set as uninitialized. // 2) LoadOp::Load and StoreOp::Clear, pass the depth and stencil test set in the render pipeline. // Because LoadOp is Load and the subresource is uninitialized, the texture will be cleared to // 0's This means the depth and stencil test will pass and the red square is drawn. TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencilStoreOpClear) { wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor(1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst, kDepthStencilFormat); wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); // Setup the renderPass for the first pass. // We want to fail the depth and stencil test here so that nothing gets drawn and we can // see that the subresource correctly gets set as unintialized in the second pass utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, depthStencilTexture.CreateView()); renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 1.0f; renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 1u; renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Clear; { wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest()); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); // Expect 0 lazy clears, depth stencil texture will clear using loadop EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); // The depth stencil test should fail and not draw because the depth stencil texture is // cleared to 1's by using loadOp clear and set values from descriptor. std::vector expectedBlack(kSize * kSize, {0, 0, 0, 0}); EXPECT_TEXTURE_RGBA8_EQ(expectedBlack.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); // Expect texture subresource initialized to be false since storeop is clear, sets // subresource as uninitialized EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, 1)); } // Now we put the depth stencil texture back into renderpass, it should be cleared by loadop // because storeOp clear sets the subresource as uninitialized { renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.SetPipeline(CreatePipelineForTest()); pass.Draw(6); pass.EndPass(); wgpu::CommandBuffer commandBuffer = encoder.Finish(); // Expect 0 lazy clears, depth stencil texture will clear using loadop EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); // Now the depth stencil test should pass since depth stencil texture is cleared to 0's by // loadop load and uninitialized subresource, so we should have a red square std::vector expectedRed(kSize * kSize, {255, 0, 0, 255}); EXPECT_TEXTURE_RGBA8_EQ(expectedRed.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); // Expect texture subresource initialized to be false since storeop is clear, sets // subresource as uninitialized EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0, 1)); } } // Test that if one mip of a texture is initialized and another is uninitialized, lazy clearing the // uninitialized mip does not clear the initialized mip. TEST_P(TextureZeroInitTest, PreservesInitializedMip) { wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor( 2, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled, kColorFormat); wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor); wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); // Fill the sample texture's second mip with data uint32_t mipSize = kSize >> 1; std::vector data(kFormatBlockByteSize * mipSize * mipSize, 2); wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(stagingBuffer, 0, mipSize * kFormatBlockByteSize); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(sampleTexture, 1, {0, 0, 0}); wgpu::Extent3D copySize = {mipSize, mipSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size); wgpu::CommandBuffer commands = encoder.Finish(); // Expect 0 lazy clears because the texture subresource will be completely copied to EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); // Create render pipeline utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor; renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest(); renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest(); renderPipelineDescriptor.cTargets[0].format = kColorFormat; wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline2(&renderPipelineDescriptor); // Create bindgroup wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), {{0, sampleTexture.CreateView()}}); // Encode pass and submit encoder = device.CreateCommandEncoder(); utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()}); renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0}; renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear; wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); pass.SetPipeline(renderPipeline); pass.SetBindGroup(0, bindGroup); pass.Draw(6); pass.EndPass(); commands = encoder.Finish(); // Expect 1 lazy clears, because not all mips of the sample texture are initialized by // copyBufferToTexture. EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); // Expect the rendered texture to be cleared since we copied from the uninitialized first // mip. std::vector expectedWithZeros(kSize * kSize, {0, 0, 0, 0}); EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0, kSize, kSize, 0, 0)); // Expect the first mip to have been lazy cleared to 0. EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), sampleTexture, 0, 0, kSize, kSize, 0, 0)); // Expect the second mip to still be filled with 2. std::vector expectedWithTwos(mipSize * mipSize, {2, 2, 2, 2}); EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0, mipSize, mipSize, 1, 0)); // Expect the whole texture to be initialized EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(sampleTexture.Get(), 0, 2, 0, 1)); } // Test that if one layer of a texture is initialized and another is uninitialized, lazy clearing // the uninitialized layer does not clear the initialized layer. TEST_P(TextureZeroInitTest, PreservesInitializedArrayLayer) { // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES. DAWN_SKIP_TEST_IF(IsOpenGLES()); wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor( 1, 2, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled, kColorFormat); wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor); wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat); wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); // Fill the sample texture's second array layer with data std::vector data(kFormatBlockByteSize * kSize * kSize, 2); wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * kFormatBlockByteSize); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(sampleTexture, 0, {0, 0, 1}); wgpu::Extent3D copySize = {kSize, kSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size); wgpu::CommandBuffer commands = encoder.Finish(); // Expect 0 lazy clears because the texture subresource will be completely copied to EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); // Create render pipeline utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor; renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest(); renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest(); renderPipelineDescriptor.cTargets[0].format = kColorFormat; wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline2(&renderPipelineDescriptor); // Only sample from the uninitialized first layer. wgpu::TextureViewDescriptor textureViewDescriptor; textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; textureViewDescriptor.arrayLayerCount = 1; // Create bindgroup wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), {{0, sampleTexture.CreateView(&textureViewDescriptor)}}); // Encode pass and submit encoder = device.CreateCommandEncoder(); utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()}); renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0}; renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear; wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); pass.SetPipeline(renderPipeline); pass.SetBindGroup(0, bindGroup); pass.Draw(6); pass.EndPass(); commands = encoder.Finish(); // Expect 1 lazy clears, because not all array layers of the sample texture are initialized by // copyBufferToTexture. EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); // Expect the rendered texture to be cleared since we copied from the uninitialized first // array layer. std::vector expectedWithZeros(kSize * kSize, {0, 0, 0, 0}); EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0, kSize, kSize, 0, 0)); // Expect the first array layer to have been lazy cleared to 0. EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), sampleTexture, 0, 0, kSize, kSize, 0, 0)); // Expect the second array layer to still be filled with 2. std::vector expectedWithTwos(kSize * kSize, {2, 2, 2, 2}); EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0, kSize, kSize, 0, 1)); // Expect the whole texture to be initialized EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(sampleTexture.Get(), 0, 1, 0, 2)); } // This is a regression test for crbug.com/dawn/451 where the lazy texture // init path on D3D12 had a divide-by-zero exception in the copy split logic. TEST_P(TextureZeroInitTest, CopyTextureToBufferNonRenderableUnaligned) { // TODO(crbug.com/dawn/667): Work around the fact that some platforms do not support reading // from Snorm textures. DAWN_SKIP_TEST_IF(HasToggleEnabled("disable_snorm_read")); wgpu::TextureDescriptor descriptor; descriptor.size.width = kUnalignedSize; descriptor.size.height = kUnalignedSize; descriptor.size.depthOrArrayLayers = 1; descriptor.format = wgpu::TextureFormat::R8Snorm; descriptor.usage = wgpu::TextureUsage::CopySrc; wgpu::Texture texture = device.CreateTexture(&descriptor); { uint32_t bytesPerRow = Align(kUnalignedSize, kTextureBytesPerRowAlignment); // Create and initialize the destination buffer to ensure we only count the times of // texture lazy initialization in this test. const uint64_t bufferSize = kUnalignedSize * bytesPerRow; const std::vector initialBufferData(bufferSize, 0u); wgpu::Buffer buffer = utils::CreateBufferFromData(device, initialBufferData.data(), bufferSize, wgpu::BufferUsage::CopyDst); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(buffer, 0, bytesPerRow); wgpu::Extent3D copySize = {kUnalignedSize, kUnalignedSize, 1}; wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size); wgpu::CommandBuffer commands = encoder.Finish(); EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); } // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); } // In this test WriteTexture fully overwrites a texture TEST_P(TextureZeroInitTest, WriteWholeTexture) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 1, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kSize, kSize, 1}; wgpu::TextureDataLayout textureDataLayout; textureDataLayout.offset = 0; textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize; textureDataLayout.rowsPerImage = kSize; std::vector data( utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, copySize, kColorFormat) / sizeof(RGBA8), {100, 100, 100, 100}); // The write overwrites the whole texture so we don't need to do lazy initialization. EXPECT_LAZY_CLEAR( 0u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8), &textureDataLayout, ©Size)); // Expect texture initialized to be true EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kSize, kSize, 0, 0); } // Test WriteTexture to a subset of the texture, lazy init is necessary to clear the other // half. TEST_P(TextureZeroInitTest, WriteTextureHalf) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 4, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); wgpu::Extent3D copySize = {kSize / 2, kSize, 1}; wgpu::TextureDataLayout textureDataLayout; textureDataLayout.offset = 0; textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize / 2; textureDataLayout.rowsPerImage = kSize; std::vector data( utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, copySize, kColorFormat) / sizeof(RGBA8), {100, 100, 100, 100}); EXPECT_LAZY_CLEAR( 1u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8), &textureDataLayout, ©Size)); // Expect texture initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); std::vector expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0}); // first half filled with 100, by the data EXPECT_TEXTURE_RGBA8_EQ(data.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); } // In this test WriteTexture fully overwrites a range of subresources, so lazy initialization // is needed for neither the subresources involved in the write nor the other subresources. TEST_P(TextureZeroInitTest, WriteWholeTextureArray) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 1, 6, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); constexpr uint32_t kBaseArrayLayer = 2u; constexpr uint32_t kCopyLayerCount = 3u; wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, kBaseArrayLayer}); wgpu::Extent3D copySize = {kSize, kSize, kCopyLayerCount}; wgpu::TextureDataLayout textureDataLayout; textureDataLayout.offset = 0; textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize; textureDataLayout.rowsPerImage = kSize; std::vector data( utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, copySize, kColorFormat) / sizeof(RGBA8), {100, 100, 100, 100}); // The write overwrites the whole subresources so we don't need to do lazy initialization on // them. EXPECT_LAZY_CLEAR( 0u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8), &textureDataLayout, ©Size)); // Expect texture subresource initialized to be true EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, kBaseArrayLayer, kCopyLayerCount)); for (uint32_t layer = kBaseArrayLayer; layer < kBaseArrayLayer + kCopyLayerCount; ++layer) { EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kSize, kSize, 0, layer); } } // Test WriteTexture to a subset of the subresource, lazy init is necessary to clear the other // half. TEST_P(TextureZeroInitTest, WriteTextureArrayHalf) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 4, 6, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); constexpr uint32_t kBaseArrayLayer = 2u; constexpr uint32_t kCopyLayerCount = 3u; wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, kBaseArrayLayer}); wgpu::Extent3D copySize = {kSize / 2, kSize, kCopyLayerCount}; wgpu::TextureDataLayout textureDataLayout; textureDataLayout.offset = 0; textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize / 2; textureDataLayout.rowsPerImage = kSize; std::vector data( utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, copySize, kColorFormat) / sizeof(RGBA8), {100, 100, 100, 100}); EXPECT_LAZY_CLEAR( 1u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8), &textureDataLayout, ©Size)); // Expect texture subresource initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, kBaseArrayLayer, kCopyLayerCount)); std::vector expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0}); for (uint32_t layer = kBaseArrayLayer; layer < kBaseArrayLayer + kCopyLayerCount; ++layer) { // first half filled with 100, by the data EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kSize / 2, kSize, 0, layer); // second half should be cleared EXPECT_TEXTURE_RGBA8_EQ(expectedZeros.data(), texture, kSize / 2, 0, kSize / 2, kSize, 0, layer); } } // In this test WriteTexture fully overwrites a texture at mip level. TEST_P(TextureZeroInitTest, WriteWholeTextureAtMipLevel) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 4, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); constexpr uint32_t kMipLevel = 2; constexpr uint32_t kMipSize = kSize >> kMipLevel; wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, kMipLevel, {0, 0, 0}); wgpu::Extent3D copySize = {kMipSize, kMipSize, 1}; wgpu::TextureDataLayout textureDataLayout; textureDataLayout.offset = 0; textureDataLayout.bytesPerRow = kMipSize * kFormatBlockByteSize; textureDataLayout.rowsPerImage = kMipSize; std::vector data( utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, copySize, kColorFormat) / sizeof(RGBA8), {100, 100, 100, 100}); // The write overwrites the whole texture so we don't need to do lazy initialization. EXPECT_LAZY_CLEAR( 0u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8), &textureDataLayout, ©Size)); // Expect texture initialized to be true EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), kMipLevel, 1, 0, 1)); EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kMipSize, kMipSize, kMipLevel, 0); } // Test WriteTexture to a subset of the texture at mip level, lazy init is necessary to clear the // other half. TEST_P(TextureZeroInitTest, WriteTextureHalfAtMipLevel) { wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( 4, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, kColorFormat); wgpu::Texture texture = device.CreateTexture(&descriptor); constexpr uint32_t kMipLevel = 2; constexpr uint32_t kMipSize = kSize >> kMipLevel; wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, kMipLevel, {0, 0, 0}); wgpu::Extent3D copySize = {kMipSize / 2, kMipSize, 1}; wgpu::TextureDataLayout textureDataLayout; textureDataLayout.offset = 0; textureDataLayout.bytesPerRow = kMipSize * kFormatBlockByteSize / 2; textureDataLayout.rowsPerImage = kMipSize; std::vector data( utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, copySize, kColorFormat) / sizeof(RGBA8), {100, 100, 100, 100}); EXPECT_LAZY_CLEAR( 1u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8), &textureDataLayout, ©Size)); // Expect texture initialized to be true EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), kMipLevel, 1, 0, 1)); std::vector expectedZeros((kMipSize / 2) * kMipSize, {0, 0, 0, 0}); // first half filled with 100, by the data EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kMipSize / 2, kMipSize, kMipLevel, 0); // second half should be cleared EXPECT_TEXTURE_RGBA8_EQ(expectedZeros.data(), texture, kMipSize / 2, 0, kMipSize / 2, kMipSize, kMipLevel, 0); } DAWN_INSTANTIATE_TEST(TextureZeroInitTest, D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}), D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}, {"use_d3d12_render_pass"}), OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}), OpenGLESBackend({"nonzero_clear_resources_on_creation_for_testing"}), MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}), VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"})); class CompressedTextureZeroInitTest : public TextureZeroInitTest { protected: void SetUp() override { DawnTest::SetUp(); DAWN_SKIP_TEST_IF(UsesWire()); DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); } std::vector GetRequiredExtensions() override { mIsBCFormatSupported = SupportsExtensions({"texture_compression_bc"}); if (!mIsBCFormatSupported) { return {}; } return {"texture_compression_bc"}; } bool IsBCFormatSupported() const { return mIsBCFormatSupported; } // Copy the compressed texture data into the destination texture. void InitializeDataInCompressedTextureAndExpectLazyClear( wgpu::Texture bcCompressedTexture, wgpu::TextureDescriptor textureDescriptor, wgpu::Extent3D copyExtent3D, uint32_t viewMipmapLevel, uint32_t baseArrayLayer, size_t lazyClearCount) { uint32_t copyWidthInBlock = copyExtent3D.width / kFormatBlockByteSize; uint32_t copyHeightInBlock = copyExtent3D.height / kFormatBlockByteSize; uint32_t copyBytesPerRow = Align(copyWidthInBlock * utils::GetTexelBlockSizeInBytes(textureDescriptor.format), kTextureBytesPerRowAlignment); std::vector data( utils::RequiredBytesInCopy(copyBytesPerRow, copyHeightInBlock, copyExtent3D, textureDescriptor.format), 1); // Copy texture data from a staging buffer to the destination texture. wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::CopySrc); wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(stagingBuffer, 0, copyBytesPerRow, copyHeightInBlock); wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture( bcCompressedTexture, viewMipmapLevel, {0, 0, baseArrayLayer}); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Extent3D); wgpu::CommandBuffer copy = encoder.Finish(); EXPECT_LAZY_CLEAR(lazyClearCount, queue.Submit(1, ©)); } // Run the tests that copies pre-prepared BC format data into a BC texture and verifies if we // can render correctly with the pixel values sampled from the BC texture. // Expect that the texture subresource is initialized void TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( wgpu::TextureDescriptor textureDescriptor, wgpu::Extent3D copyExtent3D, wgpu::Extent3D nonPaddedCopyExtent, uint32_t viewMipmapLevel, uint32_t baseArrayLayer, size_t lazyClearCount, bool halfCopyTest = false) { wgpu::Texture bcTexture = device.CreateTexture(&textureDescriptor); InitializeDataInCompressedTextureAndExpectLazyClear(bcTexture, textureDescriptor, copyExtent3D, viewMipmapLevel, baseArrayLayer, lazyClearCount); SampleCompressedTextureAndVerifyColor(bcTexture, textureDescriptor, copyExtent3D, nonPaddedCopyExtent, viewMipmapLevel, baseArrayLayer, halfCopyTest); } void SampleCompressedTextureAndVerifyColor(wgpu::Texture bcTexture, wgpu::TextureDescriptor textureDescriptor, wgpu::Extent3D copyExtent3D, wgpu::Extent3D nonPaddedCopyExtent, uint32_t viewMipmapLevel, uint32_t baseArrayLayer, bool halfCopyTest = false) { // Sample the compressed texture and verify the texture colors in the render target utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, textureDescriptor.size.width >> viewMipmapLevel, textureDescriptor.size.height >> viewMipmapLevel); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); utils::ComboRenderPipelineDescriptor2 renderPipelineDescriptor; renderPipelineDescriptor.cTargets[0].format = kColorFormat; renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest(); renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest(); wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline2(&renderPipelineDescriptor); pass.SetPipeline(renderPipeline); wgpu::TextureViewDescriptor textureViewDescriptor = CreateTextureViewDescriptor( viewMipmapLevel, baseArrayLayer, textureDescriptor.format); wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), {{0, bcTexture.CreateView(&textureViewDescriptor)}}); pass.SetBindGroup(0, bindGroup); pass.Draw(6); pass.EndPass(); } wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); std::vector expected(nonPaddedCopyExtent.width * nonPaddedCopyExtent.height, {0x00, 0x20, 0x08, 0xFF}); EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, nonPaddedCopyExtent.width, nonPaddedCopyExtent.height, 0, 0); EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(bcTexture.Get(), viewMipmapLevel, 1, baseArrayLayer, 1)); // If we only copied to half the texture, check the other half is initialized to black if (halfCopyTest) { std::vector expectBlack(nonPaddedCopyExtent.width * nonPaddedCopyExtent.height, {0x00, 0x00, 0x00, 0xFF}); EXPECT_TEXTURE_RGBA8_EQ(expectBlack.data(), renderPass.color, copyExtent3D.width, 0, nonPaddedCopyExtent.width, nonPaddedCopyExtent.height, 0, 0); } } bool mIsBCFormatSupported = false; }; // Test that the clearing is skipped when we use a full mip copy (with the physical size different // than the virtual mip size) TEST_P(CompressedTextureZeroInitTest, FullMipCopy) { wgpu::TextureDescriptor textureDescriptor; textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; textureDescriptor.size = {60, 60, 1}; textureDescriptor.mipLevelCount = 1; textureDescriptor.format = utils::kBCFormats[0]; TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( textureDescriptor, textureDescriptor.size, textureDescriptor.size, 0, 0, 0u); } // Test that 1 lazy clear count happens when we copy to half the texture TEST_P(CompressedTextureZeroInitTest, HalfCopyBufferToTexture) { // TODO(crbug.com/dawn/643): diagnose and fix this failure on OpenGL. DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); wgpu::TextureDescriptor textureDescriptor; textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; constexpr static uint32_t kSize = 16; textureDescriptor.size = {kSize, kSize, 1}; textureDescriptor.mipLevelCount = 1; textureDescriptor.format = utils::kBCFormats[0]; wgpu::Extent3D copyExtent3D = {kSize / 2, kSize, 1}; TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( textureDescriptor, copyExtent3D, copyExtent3D, 0, 0, 1u, true); } // Test that 0 lazy clear count happens when we copy buffer to texture to a nonzero mip level // (with physical size different from the virtual mip size) TEST_P(CompressedTextureZeroInitTest, FullCopyToNonZeroMipLevel) { // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES. DAWN_SKIP_TEST_IF(IsOpenGLES()); wgpu::TextureDescriptor textureDescriptor; textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; constexpr static uint32_t kSize = 60; textureDescriptor.size = {kSize, kSize, 1}; textureDescriptor.mipLevelCount = 3; textureDescriptor.format = utils::kBCFormats[0]; const uint32_t kViewMipLevel = 2; const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel; const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize); wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel, kCopySizeAtLevel, 1}; TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( textureDescriptor, copyExtent3D, {kActualSizeAtLevel, kActualSizeAtLevel, 1}, kViewMipLevel, 0, 0u); } // Test that 1 lazy clear count happens when we copy buffer to half texture to a nonzero mip level // (with physical size different from the virtual mip size) TEST_P(CompressedTextureZeroInitTest, HalfCopyToNonZeroMipLevel) { // TODO(crbug.com/dawn/643): diagnose and fix this failure on OpenGL. DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); wgpu::TextureDescriptor textureDescriptor; textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; constexpr static uint32_t kSize = 60; textureDescriptor.size = {kSize, kSize, 1}; textureDescriptor.mipLevelCount = 3; textureDescriptor.format = utils::kBCFormats[0]; const uint32_t kViewMipLevel = 2; const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel; const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize); wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel / 2, kCopySizeAtLevel, 1}; TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( textureDescriptor, copyExtent3D, {kActualSizeAtLevel / 2, kActualSizeAtLevel, 1}, kViewMipLevel, 0, 1u, true); } // Test that 0 lazy clear count happens when we copy buffer to nonzero array layer TEST_P(CompressedTextureZeroInitTest, FullCopyToNonZeroArrayLayer) { // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES. DAWN_SKIP_TEST_IF(IsOpenGLES()); wgpu::TextureDescriptor textureDescriptor; textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; constexpr static uint32_t kSize = 16; constexpr static uint32_t kArrayLayers = 4; textureDescriptor.size = {kSize, kSize, kArrayLayers}; textureDescriptor.mipLevelCount = 1; textureDescriptor.format = utils::kBCFormats[0]; wgpu::Extent3D copyExtent3D = {kSize, kSize, 1}; TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( textureDescriptor, copyExtent3D, copyExtent3D, 0, kArrayLayers - 2, 0u); } // Test that 1 lazy clear count happens when we copy buffer to half texture to a nonzero array layer TEST_P(CompressedTextureZeroInitTest, HalfCopyToNonZeroArrayLayer) { // TODO(crbug.com/dawn/643): diagnose and fix this failure on OpenGL. DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); wgpu::TextureDescriptor textureDescriptor; textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; constexpr static uint32_t kSize = 16; constexpr static uint32_t kArrayLayers = 4; textureDescriptor.size = {kSize, kSize, kArrayLayers}; textureDescriptor.mipLevelCount = 3; textureDescriptor.format = utils::kBCFormats[0]; wgpu::Extent3D copyExtent3D = {kSize / 2, kSize, 1}; TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized( textureDescriptor, copyExtent3D, copyExtent3D, 0, kArrayLayers - 2, 1u, true); } // full copy texture to texture, 0 lazy clears are needed TEST_P(CompressedTextureZeroInitTest, FullCopyTextureToTextureMipLevel) { // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES. DAWN_SKIP_TEST_IF(IsOpenGLES()); // create srcTexture and fill it with data wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor( 3, 1, wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst, utils::kBCFormats[0]); wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); const uint32_t kViewMipLevel = 2; const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel; const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize); wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel, kCopySizeAtLevel, 1}; // fill srcTexture with data InitializeDataInCompressedTextureAndExpectLazyClear(srcTexture, srcDescriptor, copyExtent3D, kViewMipLevel, 0, 0u); wgpu::ImageCopyTexture srcImageCopyTexture = utils::CreateImageCopyTexture(srcTexture, kViewMipLevel, {0, 0, 0}); // create dstTexture that we will copy to wgpu::TextureDescriptor dstDescriptor = CreateTextureDescriptor( 3, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::Sampled, utils::kBCFormats[0]); wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor); wgpu::ImageCopyTexture dstImageCopyTexture = utils::CreateImageCopyTexture(dstTexture, kViewMipLevel, {0, 0, 0}); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, ©Extent3D); wgpu::CommandBuffer commands = encoder.Finish(); // the dstTexture does not need to be lazy cleared since it's fully copied to EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); SampleCompressedTextureAndVerifyColor(dstTexture, dstDescriptor, copyExtent3D, {kActualSizeAtLevel, kActualSizeAtLevel, 1}, kViewMipLevel, 0); } // half copy texture to texture, lazy clears are needed for noncopied half TEST_P(CompressedTextureZeroInitTest, HalfCopyTextureToTextureMipLevel) { // TODO(crbug.com/dawn/643): diagnose and fix this failure on OpenGL. DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); // create srcTexture with data wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor( 3, 1, wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst, utils::kBCFormats[0]); wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); const uint32_t kViewMipLevel = 2; const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel; const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize); wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel / 2, kCopySizeAtLevel, 1}; // fill srcTexture with data InitializeDataInCompressedTextureAndExpectLazyClear(srcTexture, srcDescriptor, copyExtent3D, kViewMipLevel, 0, 1u); wgpu::ImageCopyTexture srcImageCopyTexture = utils::CreateImageCopyTexture(srcTexture, kViewMipLevel, {0, 0, 0}); // create dstTexture that we will copy to wgpu::TextureDescriptor dstDescriptor = CreateTextureDescriptor( 3, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::Sampled, utils::kBCFormats[0]); wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor); wgpu::ImageCopyTexture dstImageCopyTexture = utils::CreateImageCopyTexture(dstTexture, kViewMipLevel, {0, 0, 0}); wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, ©Extent3D); wgpu::CommandBuffer commands = encoder.Finish(); // expect 1 lazy clear count since the dstTexture needs to be lazy cleared when we only copy to // half texture EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); SampleCompressedTextureAndVerifyColor(dstTexture, dstDescriptor, copyExtent3D, {kActualSizeAtLevel / 2, kActualSizeAtLevel, 1}, kViewMipLevel, 0, true); } DAWN_INSTANTIATE_TEST(CompressedTextureZeroInitTest, D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}), MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}), OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}), OpenGLESBackend({"nonzero_clear_resources_on_creation_for_testing"}), VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"}));