Metal: Implement lazy texture clears

Bug: dawn:145
Change-Id: I73d161002cb09498e41838a10e9ac1db996c955d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/14781
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng
2020-01-16 00:12:10 +00:00
committed by Commit Bot service account
parent 0ecc48ecb7
commit 0d6619848d
13 changed files with 813 additions and 61 deletions

View File

@@ -911,4 +911,5 @@ namespace detail {
template class ExpectEq<uint8_t>;
template class ExpectEq<uint32_t>;
template class ExpectEq<RGBA8>;
template class ExpectEq<float>;
} // namespace detail

View File

@@ -48,6 +48,15 @@
sizeof(RGBA8), \
new detail::ExpectEq<RGBA8>(expected, (width) * (height)))
#define EXPECT_PIXEL_FLOAT_EQ(expected, texture, x, y) \
AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(float), \
new detail::ExpectEq<float>(expected))
#define EXPECT_TEXTURE_FLOAT_EQ(expected, texture, x, y, width, height, level, slice) \
AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \
sizeof(float), \
new detail::ExpectEq<float>(expected, (width) * (height)))
#define EXPECT_LAZY_CLEAR(N, statement) \
if (UsesWire()) { \
statement; \
@@ -377,6 +386,7 @@ namespace detail {
extern template class ExpectEq<uint8_t>;
extern template class ExpectEq<uint32_t>;
extern template class ExpectEq<RGBA8>;
extern template class ExpectEq<float>;
} // namespace detail
#endif // TESTS_DAWNTEST_H_

View File

@@ -14,6 +14,7 @@
#include "tests/DawnTest.h"
#include "common/Constants.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/WGPUHelpers.h"
@@ -26,8 +27,8 @@ class NonzeroTextureCreationTests : public DawnTest {
constexpr static uint32_t kSize = 128;
};
// Test that texture clears to 1's because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, TextureCreationClearsOneBits) {
// Test that texture clears 0xFF because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, TextureCreationClears) {
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.size.width = kSize;
@@ -40,11 +41,34 @@ TEST_P(NonzeroTextureCreationTests, TextureCreationClearsOneBits) {
descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
wgpu::Texture texture = device.CreateTexture(&descriptor);
RGBA8 filledWithOnes(255, 255, 255, 255);
EXPECT_PIXEL_RGBA8_EQ(filledWithOnes, texture, 0, 0);
RGBA8 filled(255, 255, 255, 255);
EXPECT_PIXEL_RGBA8_EQ(filled, texture, 0, 0);
}
// Test that non-zero mip level clears to 1's because toggle is enabled.
// Test that a depth texture clears 0xFF because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, Depth32TextureCreationDepthClears) {
// Copies from depth textures not supported on the OpenGL backend right now.
DAWN_SKIP_TEST_IF(IsOpenGL());
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.size.width = kSize;
descriptor.size.height = kSize;
descriptor.size.depth = 1;
descriptor.arrayLayerCount = 1;
descriptor.sampleCount = 1;
descriptor.mipLevelCount = 1;
descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
descriptor.format = wgpu::TextureFormat::Depth32Float;
// We can only really test Depth32Float here because Depth24Plus(Stencil8)? may be in an unknown
// format.
// TODO(crbug.com/dawn/145): Test other formats via sampling.
wgpu::Texture texture = device.CreateTexture(&descriptor);
EXPECT_PIXEL_FLOAT_EQ(1.f, texture, 0, 0);
}
// Test that non-zero mip level clears 0xFF because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, MipMapClears) {
constexpr uint32_t mipLevels = 4;
@@ -61,15 +85,15 @@ TEST_P(NonzeroTextureCreationTests, MipMapClears) {
wgpu::Texture texture = device.CreateTexture(&descriptor);
std::vector<RGBA8> expected;
RGBA8 filledWithOnes(255, 255, 255, 255);
RGBA8 filled(255, 255, 255, 255);
for (uint32_t i = 0; i < kSize * kSize; ++i) {
expected.push_back(filledWithOnes);
expected.push_back(filled);
}
uint32_t mipSize = kSize >> 2;
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, mipSize, mipSize, 2, 0);
}
// Test that non-zero array layers clears to 1's because toggle is enabled.
// Test that non-zero array layers clears 0xFF because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) {
constexpr uint32_t arrayLayers = 4;
@@ -86,15 +110,15 @@ TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) {
wgpu::Texture texture = device.CreateTexture(&descriptor);
std::vector<RGBA8> expected;
RGBA8 filledWithOnes(255, 255, 255, 255);
RGBA8 filled(255, 255, 255, 255);
for (uint32_t i = 0; i < kSize * kSize; ++i) {
expected.push_back(filledWithOnes);
expected.push_back(filled);
}
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 2);
}
// Test that nonrenderable texture formats clear to 1's because toggle is enabled
// Test that nonrenderable texture formats clear 0x01 because toggle is enabled
TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) {
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
@@ -123,11 +147,11 @@ TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) {
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
std::vector<uint32_t> expected(bufferSize, 1);
std::vector<uint32_t> expected(bufferSize, 0x01010101);
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), bufferDst, 0, 8);
}
// Test that textures with more than 1 array layers and nonrenderable texture formats clear to 1's
// Test that textures with more than 1 array layers and nonrenderable texture formats clear to 0x01
// because toggle is enabled
TEST_P(NonzeroTextureCreationTests, NonRenderableTextureClearWithMultiArrayLayers) {
// TODO(natlee@microsoft.com): skip for now on opengl because TextureClear nonrenderable
@@ -161,14 +185,129 @@ TEST_P(NonzeroTextureCreationTests, NonRenderableTextureClearWithMultiArrayLayer
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
std::vector<uint32_t> expectedWithZeros(bufferSize, 1);
EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, 8);
std::vector<uint32_t> expected(bufferSize, 0x01010101);
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), bufferDst, 0, 8);
}
// Test that all subresources of a renderable texture are filled because the toggle is enabled.
TEST_P(NonzeroTextureCreationTests, AllSubresourcesFilled) {
// TODO(crbug.com/dawn/145): Implement on other platforms.
DAWN_SKIP_TEST_IF(!IsMetal());
wgpu::TextureDescriptor baseDescriptor;
baseDescriptor.dimension = wgpu::TextureDimension::e2D;
baseDescriptor.size.width = kSize;
baseDescriptor.size.height = kSize;
baseDescriptor.size.depth = 1;
baseDescriptor.sampleCount = 1;
baseDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
baseDescriptor.mipLevelCount = 1;
baseDescriptor.arrayLayerCount = 1;
baseDescriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
RGBA8 filled(255, 255, 255, 255);
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
// Some textures may be cleared with render pass load/store ops.
// Test above the max attachment count.
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, 0, i);
}
}
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
descriptor.mipLevelCount = 3;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.mipLevelCount; ++i) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, i, 0);
}
}
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
// Some textures may be cleared with render pass load/store ops.
// Test above the max attachment count.
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
descriptor.mipLevelCount = 3;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
for (uint32_t j = 0; j < descriptor.mipLevelCount; ++j) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, j, i);
}
}
}
}
// Test that all subresources of a nonrenderable texture are filled because the toggle is enabled.
TEST_P(NonzeroTextureCreationTests, NonRenderableAllSubresourcesFilled) {
// TODO(crbug.com/dawn/145): Implement on other platforms.
DAWN_SKIP_TEST_IF(!IsMetal());
wgpu::TextureDescriptor baseDescriptor;
baseDescriptor.dimension = wgpu::TextureDimension::e2D;
baseDescriptor.size.width = kSize;
baseDescriptor.size.height = kSize;
baseDescriptor.size.depth = 1;
baseDescriptor.sampleCount = 1;
baseDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
baseDescriptor.mipLevelCount = 1;
baseDescriptor.arrayLayerCount = 1;
baseDescriptor.usage = wgpu::TextureUsage::CopySrc;
RGBA8 filled(1, 1, 1, 1);
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
// Some textures may be cleared with render pass load/store ops.
// Test above the max attachment count.
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, 0, i);
}
}
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
descriptor.mipLevelCount = 3;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.mipLevelCount; ++i) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, i, 0);
}
}
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
// Some textures may be cleared with render pass load/store ops.
// Test above the max attachment count.
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
descriptor.mipLevelCount = 3;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
for (uint32_t j = 0; j < descriptor.mipLevelCount; ++j) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, j, i);
}
}
}
}
DAWN_INSTANTIATE_TEST(NonzeroTextureCreationTests,
ForceToggles(D3D12Backend,
{"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),
ForceToggles(MetalBackend,
{"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),
ForceToggles(OpenGLBackend,
{"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),

View File

@@ -127,7 +127,12 @@ TEST_P(TextureZeroInitTest, RenderingMipMapClearsToZero) {
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
@@ -155,7 +160,12 @@ TEST_P(TextureZeroInitTest, RenderingArrayLayerClearsToZero) {
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);
@@ -327,6 +337,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepth) {
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;
@@ -366,6 +378,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingStencil) {
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;
@@ -760,6 +774,166 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencilStoreOpClear) {
}
}
// 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) {
// TODO(crbug.com/dawn/145): Fix this on other backends
DAWN_SKIP_TEST_IF(!IsMetal());
wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor(
2, 1,
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled,
kColorFormat);
wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor);
wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat);
wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
// Fill the sample texture's second mip with data
uint32_t mipSize = kSize >> 1;
std::vector<uint8_t> data(kFormatBlockByteSize * mipSize * mipSize, 2);
wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0);
wgpu::TextureCopyView textureCopyView =
utils::CreateTextureCopyView(sampleTexture, 1, 0, {0, 0, 0});
wgpu::Extent3D copySize = {mipSize, mipSize, 1};
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
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::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest();
renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest();
renderPipelineDescriptor.cColorStates[0].format = kColorFormat;
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
// Create bindgroup
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
{{0, sampler}, {1, 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, 1, 0, 0);
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<RGBA8> 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<RGBA8> expectedWithTwos(mipSize * mipSize, {2, 2, 2, 2});
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0,
mipSize, mipSize, 1, 0));
}
// 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/145): Fix this on other backends
DAWN_SKIP_TEST_IF(!IsMetal());
wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor(
1, 2,
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled,
kColorFormat);
wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor);
wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat);
wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
// Fill the sample texture's second array layer with data
std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize, 2);
wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0);
wgpu::TextureCopyView textureCopyView =
utils::CreateTextureCopyView(sampleTexture, 0, 1, {0, 0, 0});
wgpu::Extent3D copySize = {kSize, kSize, 1};
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
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::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest();
renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest();
renderPipelineDescriptor.cColorStates[0].format = kColorFormat;
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&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, sampler}, {1, 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, 1, 0, 0);
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<RGBA8> 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<RGBA8> expectedWithTwos(kSize * kSize, {2, 2, 2, 2});
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0,
kSize, kSize, 0, 1));
}
DAWN_INSTANTIATE_TEST(
TextureZeroInitTest,
ForceToggles(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}),
@@ -767,4 +941,5 @@ DAWN_INSTANTIATE_TEST(
{"nonzero_clear_resources_on_creation_for_testing"},
{"use_d3d12_render_pass"}),
ForceToggles(OpenGLBackend, {"nonzero_clear_resources_on_creation_for_testing"}),
ForceToggles(MetalBackend, {"nonzero_clear_resources_on_creation_for_testing"}),
ForceToggles(VulkanBackend, {"nonzero_clear_resources_on_creation_for_testing"}));