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>
This commit is contained in:
Natasha Lee 2019-09-25 13:08:28 +00:00 committed by Commit Bot service account
parent 05be0ff348
commit cf0e9d93f1
12 changed files with 310 additions and 77 deletions

View File

@ -708,7 +708,8 @@
"store op": {
"category": "enum",
"values": [
{"value": 0, "name": "store"}
{"value": 0, "name": "store"},
{"value": 1, "name": "clear"}
]
},
"origin 3D": {

View File

@ -404,6 +404,12 @@ namespace dawn_native {
"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();

View File

@ -412,7 +412,8 @@ namespace dawn_native {
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 @@ namespace dawn_native {
++arrayLayer) {
uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
mIsSubresourceContentInitializedAtIndex[subresourceIndex] = true;
mIsSubresourceContentInitializedAtIndex[subresourceIndex] = isInitialized;
}
}
}

View File

@ -62,7 +62,8 @@ namespace dawn_native {
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);

View File

@ -654,7 +654,7 @@ namespace dawn_native { namespace d3d12 {
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 @@ namespace dawn_native { namespace d3d12 {
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 @@ namespace dawn_native { namespace d3d12 {
// 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 @@ namespace dawn_native { namespace d3d12 {
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());
}
}
}

View File

@ -307,7 +307,7 @@ namespace dawn_native { namespace d3d12 {
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 @@ namespace dawn_native { namespace d3d12 {
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 @@ namespace dawn_native { namespace d3d12 {
}
}
if (clearValue == TextureBase::ClearValue::Zero) {
SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
layerCount);
GetDevice()->IncrementLazyClearCountForTesting();
}

View File

@ -467,8 +467,8 @@ namespace dawn_native { namespace opengl {
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 @@ namespace dawn_native { namespace opengl {
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 @@ namespace dawn_native { namespace opengl {
{
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 @@ namespace dawn_native { namespace opengl {
}
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());
}
}
}

View File

@ -313,7 +313,7 @@ namespace dawn_native { namespace opengl {
if (isLazyClear) {
GetDevice()->IncrementLazyClearCountForTesting();
}
SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
layerCount);
}
}

View File

@ -137,14 +137,19 @@ namespace dawn_native { namespace vulkan {
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 @@ namespace dawn_native { namespace vulkan {
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 @@ namespace dawn_native { namespace vulkan {
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 @@ namespace dawn_native { namespace vulkan {
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)

View File

@ -512,7 +512,7 @@ namespace dawn_native { namespace vulkan {
// 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 @@ namespace dawn_native { namespace vulkan {
}
}
if (clearValue == TextureBase::ClearValue::Zero) {
SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
layerCount);
device->IncrementLazyClearCountForTesting();
}

View File

@ -52,22 +52,7 @@ class TextureZeroInitTest : public DawnTest {
}
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 @@ class TextureZeroInitTest : public DawnTest {
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 @@ TEST_P(TextureZeroInitTest, RenderingArrayLayerClearsToZero) {
}
// 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 @@ TEST_P(TextureZeroInitTest, RenderPassSampledTextureClear) {
// 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 @@ TEST_P(TextureZeroInitTest, NonRenderableTextureClearWithMultiArrayLayers) {
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, &copySize);
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"}),

View File

@ -189,6 +189,54 @@ TEST_F(RenderPassDescriptorValidationTest, FormatMismatch) {
}
}
// 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) {