Add StoreOp::Clear
When storeOp is clear, texture subresource is set as not initialized
Bug: dawn:145
Change-Id: I364d7239a7ebdb9d5a28a4af559f3212be7ef15a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/11560
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index 0c5208e..e0a9cd2 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -404,6 +404,12 @@
"depth stencil format");
}
+ // This validates that the depth storeOp and stencil storeOps are the same
+ if (depthStencilAttachment->depthStoreOp != depthStencilAttachment->stencilStoreOp) {
+ return DAWN_VALIDATION_ERROR(
+ "The depth storeOp and stencil storeOp are not the same");
+ }
+
// *sampleCount == 0 must only happen when there is no color attachment. In that case we
// do not need to validate the sample count of the depth stencil attachment.
const uint32_t depthStencilSampleCount = attachment->GetTexture()->GetSampleCount();
diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp
index ea626ed..bcaa923 100644
--- a/src/dawn_native/Texture.cpp
+++ b/src/dawn_native/Texture.cpp
@@ -412,7 +412,8 @@
return true;
}
- void TextureBase::SetIsSubresourceContentInitialized(uint32_t baseMipLevel,
+ void TextureBase::SetIsSubresourceContentInitialized(bool isInitialized,
+ uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) {
@@ -422,7 +423,7 @@
++arrayLayer) {
uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
- mIsSubresourceContentInitializedAtIndex[subresourceIndex] = true;
+ mIsSubresourceContentInitializedAtIndex[subresourceIndex] = isInitialized;
}
}
}
diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h
index c515066..fafd46e 100644
--- a/src/dawn_native/Texture.h
+++ b/src/dawn_native/Texture.h
@@ -62,7 +62,8 @@
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) const;
- void SetIsSubresourceContentInitialized(uint32_t baseMipLevel,
+ void SetIsSubresourceContentInitialized(bool isInitialized,
+ uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount);
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index f4aa363..09a178b 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -654,7 +654,7 @@
if (IsCompleteSubresourceCopiedTo(texture, copy->copySize,
copy->destination.mipLevel)) {
texture->SetIsSubresourceContentInitialized(
- copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1);
+ true, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1);
} else {
texture->EnsureSubresourceContentInitialized(
commandList, copy->destination.mipLevel, 1,
@@ -737,7 +737,7 @@
if (IsCompleteSubresourceCopiedTo(destination, copy->copySize,
copy->destination.mipLevel)) {
destination->SetIsSubresourceContentInitialized(
- copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1);
+ true, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1);
} else {
destination->EnsureSubresourceContentInitialized(
commandList, copy->destination.mipLevel, 1,
@@ -907,14 +907,19 @@
// color attachment, which will be correctly initialized.
ToBackend(resolveView->GetTexture())
->SetIsSubresourceContentInitialized(
- resolveView->GetBaseMipLevel(), resolveView->GetLevelCount(),
+ true, resolveView->GetBaseMipLevel(), resolveView->GetLevelCount(),
resolveView->GetBaseArrayLayer(), resolveView->GetLayerCount());
}
switch (attachmentInfo.storeOp) {
case dawn::StoreOp::Store: {
view->GetTexture()->SetIsSubresourceContentInitialized(
- view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
+ true, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
+ } break;
+
+ case dawn::StoreOp::Clear: {
+ view->GetTexture()->SetIsSubresourceContentInitialized(
+ false, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
} break;
default: { UNREACHABLE(); } break;
@@ -966,12 +971,16 @@
0, nullptr);
}
- // TODO(natlee@microsoft.com): Need to fix when storeop discard is added
if (attachmentInfo.depthStoreOp == dawn::StoreOp::Store &&
attachmentInfo.stencilStoreOp == dawn::StoreOp::Store) {
texture->SetIsSubresourceContentInitialized(
- view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(),
- view->GetLayerCount());
+ true, view->GetBaseMipLevel(), view->GetLevelCount(),
+ view->GetBaseArrayLayer(), view->GetLayerCount());
+ } else if (attachmentInfo.depthStoreOp == dawn::StoreOp::Clear &&
+ attachmentInfo.stencilStoreOp == dawn::StoreOp::Clear) {
+ texture->SetIsSubresourceContentInitialized(
+ false, view->GetBaseMipLevel(), view->GetLevelCount(),
+ view->GetBaseArrayLayer(), view->GetLayerCount());
}
}
}
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index c18bac9..35ab67c 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -307,7 +307,7 @@
const TextureDescriptor* descriptor,
ID3D12Resource* nativeTexture)
: TextureBase(device, descriptor, TextureState::OwnedExternal), mResource(nativeTexture) {
- SetIsSubresourceContentInitialized(0, descriptor->mipLevelCount, 0,
+ SetIsSubresourceContentInitialized(true, 0, descriptor->mipLevelCount, 0,
descriptor->arrayLayerCount);
}
@@ -483,7 +483,7 @@
TextureBase::ClearValue clearValue) {
// TODO(jiawei.shao@intel.com): initialize the textures in compressed formats with copies.
if (GetFormat().isCompressed) {
- SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
+ SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
layerCount);
return {};
}
@@ -579,7 +579,7 @@
}
}
if (clearValue == TextureBase::ClearValue::Zero) {
- SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
+ SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
layerCount);
GetDevice()->IncrementLazyClearCountForTesting();
}
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index b451039..3a014d1 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -467,8 +467,8 @@
GLenum target = texture->GetGLTarget();
const GLFormat& format = texture->GetGLFormat();
if (IsCompleteSubresourceCopiedTo(texture, copySize, dst.mipLevel)) {
- texture->SetIsSubresourceContentInitialized(dst.mipLevel, 1, dst.arrayLayer,
- 1);
+ texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1,
+ dst.arrayLayer, 1);
} else {
texture->EnsureSubresourceContentInitialized(dst.mipLevel, 1,
dst.arrayLayer, 1);
@@ -609,7 +609,7 @@
srcTexture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
1);
if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel)) {
- dstTexture->SetIsSubresourceContentInitialized(dst.mipLevel, 1,
+ dstTexture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1,
dst.arrayLayer, 1);
} else {
dstTexture->EnsureSubresourceContentInitialized(dst.mipLevel, 1,
@@ -783,28 +783,49 @@
{
for (uint32_t i :
IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
- const auto& attachmentInfo = renderPass->colorAttachments[i];
+ auto* attachmentInfo = &renderPass->colorAttachments[i];
+ TextureView* view = ToBackend(attachmentInfo->view.Get());
// Load op - color
// TODO(cwallez@chromium.org): Choose the clear function depending on the
// componentType: things work for now because the clear color is always a float, but
// when that's fixed will lose precision on integer formats when converting to
// float.
- if (attachmentInfo.loadOp == dawn::LoadOp::Clear) {
+ if (attachmentInfo->loadOp == dawn::LoadOp::Clear) {
gl.ColorMaski(i, true, true, true, true);
- gl.ClearBufferfv(GL_COLOR, i, &attachmentInfo.clearColor.r);
+ gl.ClearBufferfv(GL_COLOR, i, &attachmentInfo->clearColor.r);
+ }
+
+ switch (attachmentInfo->storeOp) {
+ case dawn::StoreOp::Store: {
+ view->GetTexture()->SetIsSubresourceContentInitialized(
+ true, view->GetBaseMipLevel(), view->GetLevelCount(),
+ view->GetBaseArrayLayer(), view->GetLayerCount());
+ } break;
+
+ case dawn::StoreOp::Clear: {
+ // TODO(natlee@microsoft.com): call glDiscard to do optimization
+ view->GetTexture()->SetIsSubresourceContentInitialized(
+ false, view->GetBaseMipLevel(), view->GetLevelCount(),
+ view->GetBaseArrayLayer(), view->GetLayerCount());
+ } break;
+
+ default:
+ UNREACHABLE();
+ break;
}
}
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
- const auto& attachmentInfo = renderPass->depthStencilAttachment;
- const Format& attachmentFormat = attachmentInfo.view->GetTexture()->GetFormat();
+ auto* attachmentInfo = &renderPass->depthStencilAttachment;
+ const Format& attachmentFormat = attachmentInfo->view->GetTexture()->GetFormat();
+ TextureView* view = ToBackend(attachmentInfo->view.Get());
// Load op - depth/stencil
bool doDepthClear = attachmentFormat.HasDepth() &&
- (attachmentInfo.depthLoadOp == dawn::LoadOp::Clear);
+ (attachmentInfo->depthLoadOp == dawn::LoadOp::Clear);
bool doStencilClear = attachmentFormat.HasStencil() &&
- (attachmentInfo.stencilLoadOp == dawn::LoadOp::Clear);
+ (attachmentInfo->stencilLoadOp == dawn::LoadOp::Clear);
if (doDepthClear) {
gl.DepthMask(GL_TRUE);
@@ -814,14 +835,26 @@
}
if (doDepthClear && doStencilClear) {
- gl.ClearBufferfi(GL_DEPTH_STENCIL, 0, attachmentInfo.clearDepth,
- attachmentInfo.clearStencil);
+ gl.ClearBufferfi(GL_DEPTH_STENCIL, 0, attachmentInfo->clearDepth,
+ attachmentInfo->clearStencil);
} else if (doDepthClear) {
- gl.ClearBufferfv(GL_DEPTH, 0, &attachmentInfo.clearDepth);
+ gl.ClearBufferfv(GL_DEPTH, 0, &attachmentInfo->clearDepth);
} else if (doStencilClear) {
- const GLint clearStencil = attachmentInfo.clearStencil;
+ const GLint clearStencil = attachmentInfo->clearStencil;
gl.ClearBufferiv(GL_STENCIL, 0, &clearStencil);
}
+
+ if (attachmentInfo->depthStoreOp == dawn::StoreOp::Store &&
+ attachmentInfo->stencilStoreOp == dawn::StoreOp::Store) {
+ view->GetTexture()->SetIsSubresourceContentInitialized(
+ true, view->GetBaseMipLevel(), view->GetLevelCount(),
+ view->GetBaseArrayLayer(), view->GetLayerCount());
+ } else if (attachmentInfo->depthStoreOp == dawn::StoreOp::Clear &&
+ attachmentInfo->stencilStoreOp == dawn::StoreOp::Clear) {
+ view->GetTexture()->SetIsSubresourceContentInitialized(
+ false, view->GetBaseMipLevel(), view->GetLevelCount(),
+ view->GetBaseArrayLayer(), view->GetLayerCount());
+ }
}
}
diff --git a/src/dawn_native/opengl/TextureGL.cpp b/src/dawn_native/opengl/TextureGL.cpp
index 5dce582..4b4fdde 100644
--- a/src/dawn_native/opengl/TextureGL.cpp
+++ b/src/dawn_native/opengl/TextureGL.cpp
@@ -313,7 +313,7 @@
if (isLazyClear) {
GetDevice()->IncrementLazyClearCountForTesting();
}
- SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
+ SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
layerCount);
}
}
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index 7729286..c01d5fd 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -137,14 +137,19 @@
TextureView* resolveView = ToBackend(attachmentInfo.resolveTarget.Get());
ToBackend(resolveView->GetTexture())
->SetIsSubresourceContentInitialized(
- resolveView->GetBaseMipLevel(), resolveView->GetLevelCount(),
+ true, resolveView->GetBaseMipLevel(), resolveView->GetLevelCount(),
resolveView->GetBaseArrayLayer(), resolveView->GetLayerCount());
}
switch (attachmentInfo.storeOp) {
case dawn::StoreOp::Store: {
view->GetTexture()->SetIsSubresourceContentInitialized(
- view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
+ true, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
+ } break;
+
+ case dawn::StoreOp::Clear: {
+ view->GetTexture()->SetIsSubresourceContentInitialized(
+ false, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
} break;
default: { UNREACHABLE(); } break;
@@ -177,11 +182,15 @@
query.SetDepthStencil(view->GetTexture()->GetFormat().format,
attachmentInfo.depthLoadOp, attachmentInfo.stencilLoadOp);
- // TODO(natlee@microsoft.com): Need to fix when storeop discard is added
if (attachmentInfo.depthStoreOp == dawn::StoreOp::Store &&
attachmentInfo.stencilStoreOp == dawn::StoreOp::Store) {
view->GetTexture()->SetIsSubresourceContentInitialized(
- view->GetBaseMipLevel(), view->GetLevelCount(),
+ true, view->GetBaseMipLevel(), view->GetLevelCount(),
+ view->GetBaseArrayLayer(), view->GetLayerCount());
+ } else if (attachmentInfo.depthStoreOp == dawn::StoreOp::Clear &&
+ attachmentInfo.stencilStoreOp == dawn::StoreOp::Clear) {
+ view->GetTexture()->SetIsSubresourceContentInitialized(
+ false, view->GetBaseMipLevel(), view->GetLevelCount(),
view->GetBaseArrayLayer(), view->GetLayerCount());
}
}
@@ -400,7 +409,7 @@
subresource.mipLevel)) {
// Since texture has been overwritten, it has been "initialized"
dst.texture->SetIsSubresourceContentInitialized(
- subresource.mipLevel, 1, subresource.baseArrayLayer, 1);
+ true, subresource.mipLevel, 1, subresource.baseArrayLayer, 1);
} else {
ToBackend(dst.texture)
->EnsureSubresourceContentInitialized(recordingContext,
@@ -459,7 +468,7 @@
if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
dst.mipLevel)) {
// Since destination texture has been overwritten, it has been "initialized"
- dst.texture->SetIsSubresourceContentInitialized(dst.mipLevel, 1,
+ dst.texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1,
dst.arrayLayer, 1);
} else {
ToBackend(dst.texture)
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 1f29ea0..fc412a9 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -512,7 +512,7 @@
// Don't clear imported texture if already cleared
if (descriptor->isCleared) {
- SetIsSubresourceContentInitialized(0, 1, 0, 1);
+ SetIsSubresourceContentInitialized(true, 0, 1, 0, 1);
}
}
@@ -724,7 +724,7 @@
}
}
if (clearValue == TextureBase::ClearValue::Zero) {
- SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
+ SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
layerCount);
device->IncrementLazyClearCountForTesting();
}
diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp
index 64e518b..81dfc47 100644
--- a/src/tests/end2end/TextureZeroInitTests.cpp
+++ b/src/tests/end2end/TextureZeroInitTests.cpp
@@ -52,22 +52,7 @@
}
dawn::RenderPipeline CreatePipelineForTest() {
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
- const char* vs =
- R"(#version 450
- const vec2 pos[6] = vec2[6](vec2(-1.0f, -1.0f),
- vec2(-1.0f, 1.0f),
- vec2( 1.0f, -1.0f),
- vec2( 1.0f, 1.0f),
- vec2(-1.0f, 1.0f),
- vec2( 1.0f, -1.0f)
- );
-
- void main() {
- gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
- })";
- pipelineDescriptor.vertexStage.module =
- utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vs);
-
+ pipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest();
const char* fs =
R"(#version 450
layout(location = 0) out vec4 fragColor;
@@ -83,6 +68,30 @@
return device.CreateRenderPipeline(&pipelineDescriptor);
}
+ dawn::ShaderModule CreateBasicVertexShaderForTest() {
+ return utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(#version 450
+ const vec2 pos[6] = vec2[6](vec2(-1.0f, -1.0f),
+ vec2(-1.0f, 1.0f),
+ vec2( 1.0f, -1.0f),
+ vec2( 1.0f, 1.0f),
+ vec2(-1.0f, 1.0f),
+ vec2( 1.0f, -1.0f)
+ );
+
+ void main() {
+ gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
+ })");
+ }
+ dawn::ShaderModule CreateSampledTextureFragmentShaderForTest() {
+ return utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment,
+ R"(#version 450
+ layout(set = 0, binding = 0) uniform sampler sampler0;
+ layout(set = 0, binding = 1) uniform texture2D texture0;
+ layout(location = 0) out vec4 fragColor;
+ void main() {
+ fragColor = texelFetch(sampler2D(texture0, sampler0), ivec2(gl_FragCoord), 0);
+ })");
+ }
constexpr static uint32_t kSize = 128;
constexpr static uint32_t kUnalignedSize = 127;
// All three texture formats used (RGBA8Unorm, Depth24PlusStencil8, and RGBA8Snorm) have the
@@ -161,8 +170,6 @@
}
// This tests CopyBufferToTexture fully overwrites copy so lazy init is not needed.
-// TODO(natlee@microsoft.com): Add backdoor to dawn native to query the number of zero-inited
-// subresources
TEST_P(TextureZeroInitTest, CopyBufferToTexture) {
dawn::TextureDescriptor descriptor = CreateTextureDescriptor(
4, 1,
@@ -452,29 +459,9 @@
// Create render pipeline
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
renderPipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bindGroupLayout);
- renderPipelineDescriptor.vertexStage.module =
- utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(#version 450
- const vec2 pos[6] = vec2[6](vec2(-1.0f, -1.0f),
- vec2(-1.0f, 1.0f),
- vec2( 1.0f, -1.0f),
- vec2( 1.0f, 1.0f),
- vec2(-1.0f, 1.0f),
- vec2( 1.0f, -1.0f)
- );
-
- void main() {
- gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
- })");
- renderPipelineDescriptor.cFragmentStage.module =
- utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment,
- R"(#version 450
- layout(set = 0, binding = 0) uniform sampler sampler0;
- layout(set = 0, binding = 1) uniform texture2D texture0;
- layout(location = 0) out vec4 fragColor;
- void main() {
- fragColor = texelFetch(sampler2D(texture0, sampler0), ivec2(gl_FragCoord), 0);
- })");
renderPipelineDescriptor.cColorStates[0].format = kColorFormat;
+ renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest();
+ renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest();
dawn::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
// Create bindgroup
@@ -651,6 +638,144 @@
EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, 8);
}
+// 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
+ dawn::TextureDescriptor descriptor = CreateTextureDescriptor(
+ 1, 1, dawn::TextureUsage::Sampled | dawn::TextureUsage::CopyDst, kColorFormat);
+ dawn::Texture texture = device.CreateTexture(&descriptor);
+
+ dawn::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
+ 1, 1, dawn::TextureUsage::CopySrc | dawn::TextureUsage::OutputAttachment, kColorFormat);
+ dawn::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
+
+ dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
+ dawn::Sampler sampler = device.CreateSampler(&samplerDesc);
+
+ dawn::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
+ device, {{0, dawn::ShaderStage::Fragment, dawn::BindingType::Sampler},
+ {1, dawn::ShaderStage::Fragment, dawn::BindingType::SampledTexture}});
+
+ // Fill the sample texture with data
+ std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize, 1);
+ dawn::Buffer stagingBuffer = utils::CreateBufferFromData(
+ device, data.data(), static_cast<uint32_t>(data.size()), dawn::BufferUsage::CopySrc);
+ dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0);
+ dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0});
+ dawn::Extent3D copySize = {kSize, kSize, 1};
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size);
+ dawn::CommandBuffer commands = encoder.Finish();
+ // Expect 0 lazy clears because the texture will be completely copied to
+ EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
+
+ // Create render pipeline
+ utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
+ renderPipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bindGroupLayout);
+ renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest();
+ renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest();
+ renderPipelineDescriptor.cColorStates[0].format = kColorFormat;
+ dawn::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
+
+ // Create bindgroup
+ dawn::BindGroup bindGroup =
+ utils::MakeBindGroup(device, bindGroupLayout, {{0, sampler}, {1, texture.CreateView()}});
+
+ // Encode pass and submit
+ encoder = device.CreateCommandEncoder();
+ utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
+ renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0};
+ renderPassDesc.cColorAttachments[0].loadOp = dawn::LoadOp::Clear;
+ renderPassDesc.cColorAttachments[0].storeOp = dawn::StoreOp::Clear;
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
+ pass.SetPipeline(renderPipeline);
+ pass.SetBindGroup(0, bindGroup, 0, nullptr);
+ pass.Draw(6, 1, 0, 0);
+ 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<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));
+}
+
+// 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) {
+ dawn::TextureDescriptor srcDescriptor =
+ CreateTextureDescriptor(1, 1,
+ dawn::TextureUsage::CopySrc | dawn::TextureUsage::CopyDst |
+ dawn::TextureUsage::OutputAttachment,
+ kColorFormat);
+ dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor);
+
+ dawn::TextureDescriptor depthStencilDescriptor =
+ CreateTextureDescriptor(1, 1,
+ dawn::TextureUsage::OutputAttachment | dawn::TextureUsage::CopySrc |
+ dawn::TextureUsage::CopyDst,
+ kDepthStencilFormat);
+ dawn::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 = dawn::LoadOp::Clear;
+ renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = dawn::LoadOp::Clear;
+ renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 1.0f;
+ renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 1u;
+ renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = dawn::StoreOp::Clear;
+ renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = dawn::StoreOp::Clear;
+ {
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
+ pass.SetPipeline(CreatePipelineForTest());
+ pass.Draw(6, 1, 0, 0);
+ pass.EndPass();
+ dawn::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<RGBA8> expectedBlack(kSize * kSize, {0, 0, 0, 0});
+ EXPECT_TEXTURE_RGBA8_EQ(expectedBlack.data(), srcTexture, 0, 0, kSize, kSize, 0, 0);
+ }
+
+ // 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 = dawn::LoadOp::Load;
+ renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = dawn::LoadOp::Load;
+ dawn::CommandEncoder encoder = device.CreateCommandEncoder();
+ dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
+ pass.SetPipeline(CreatePipelineForTest());
+ pass.Draw(6, 1, 0, 0);
+ pass.EndPass();
+ dawn::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<RGBA8> expectedRed(kSize * kSize, {255, 0, 0, 255});
+ EXPECT_TEXTURE_RGBA8_EQ(expectedRed.data(), srcTexture, 0, 0, kSize, kSize, 0, 0);
+ }
+}
+
DAWN_INSTANTIATE_TEST(
TextureZeroInitTest,
ForceWorkarounds(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}),
diff --git a/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp b/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
index d3bae71..bf7e67d 100644
--- a/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
+++ b/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
@@ -189,6 +189,54 @@
}
}
+// Depth and stencil storeOps must match
+TEST_F(RenderPassDescriptorValidationTest, DepthStencilStoreOpMismatch) {
+ constexpr uint32_t kArrayLayers = 1;
+ constexpr uint32_t kLevelCount = 1;
+ constexpr uint32_t kSize = 32;
+ constexpr dawn::TextureFormat kColorFormat = dawn::TextureFormat::RGBA8Unorm;
+ constexpr dawn::TextureFormat kDepthStencilFormat = dawn::TextureFormat::Depth24PlusStencil8;
+
+ dawn::Texture colorTexture = CreateTexture(device, dawn::TextureDimension::e2D, kColorFormat,
+ kSize, kSize, kArrayLayers, kLevelCount);
+ dawn::Texture depthStencilTexture =
+ CreateTexture(device, dawn::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize,
+ kArrayLayers, kLevelCount);
+
+ dawn::TextureViewDescriptor descriptor;
+ descriptor.dimension = dawn::TextureViewDimension::e2D;
+ descriptor.baseArrayLayer = 0;
+ descriptor.arrayLayerCount = kArrayLayers;
+ descriptor.baseMipLevel = 0;
+ descriptor.mipLevelCount = kLevelCount;
+ dawn::TextureView colorTextureView = colorTexture.CreateView(&descriptor);
+ dawn::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor);
+
+ // StoreOps mismatch causing the render pass to error
+ {
+ utils::ComboRenderPassDescriptor renderPass({}, depthStencilView);
+ renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = dawn::StoreOp::Store;
+ renderPass.cDepthStencilAttachmentInfo.depthStoreOp = dawn::StoreOp::Clear;
+ AssertBeginRenderPassError(&renderPass);
+ }
+
+ // StoreOps match so render pass is a success
+ {
+ utils::ComboRenderPassDescriptor renderPass({}, depthStencilView);
+ renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = dawn::StoreOp::Store;
+ renderPass.cDepthStencilAttachmentInfo.depthStoreOp = dawn::StoreOp::Store;
+ AssertBeginRenderPassSuccess(&renderPass);
+ }
+
+ // StoreOps match so render pass is a success
+ {
+ utils::ComboRenderPassDescriptor renderPass({}, depthStencilView);
+ renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = dawn::StoreOp::Clear;
+ renderPass.cDepthStencilAttachmentInfo.depthStoreOp = dawn::StoreOp::Clear;
+ AssertBeginRenderPassSuccess(&renderPass);
+ }
+}
+
// Currently only texture views with arrayLayerCount == 1 are allowed to be color and depth stencil
// attachments
TEST_F(RenderPassDescriptorValidationTest, TextureViewLayerCountForColorAndDepthStencil) {