diff --git a/dawn.json b/dawn.json index a71e9d62ce..ceeba0c8fb 100644 --- a/dawn.json +++ b/dawn.json @@ -708,7 +708,8 @@ "store op": { "category": "enum", "values": [ - {"value": 0, "name": "store"} + {"value": 0, "name": "store"}, + {"value": 1, "name": "clear"} ] }, "origin 3D": { diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp index 0c5208ef04..e0a9cd2e4c 100644 --- a/src/dawn_native/CommandEncoder.cpp +++ b/src/dawn_native/CommandEncoder.cpp @@ -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(); diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp index ea626edf82..bcaa923d91 100644 --- a/src/dawn_native/Texture.cpp +++ b/src/dawn_native/Texture.cpp @@ -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; } } } diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h index c515066f3b..fafd46edcc 100644 --- a/src/dawn_native/Texture.h +++ b/src/dawn_native/Texture.h @@ -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); diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp index f4aa363a5f..09a178bb2f 100644 --- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -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()); } } } diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp index c18bac9433..35ab67c989 100644 --- a/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/src/dawn_native/d3d12/TextureD3D12.cpp @@ -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(); } diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp index b45103962a..3a014d1bf9 100644 --- a/src/dawn_native/opengl/CommandBufferGL.cpp +++ b/src/dawn_native/opengl/CommandBufferGL.cpp @@ -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()); + } } } diff --git a/src/dawn_native/opengl/TextureGL.cpp b/src/dawn_native/opengl/TextureGL.cpp index 5dce58235c..4b4fdde1bc 100644 --- a/src/dawn_native/opengl/TextureGL.cpp +++ b/src/dawn_native/opengl/TextureGL.cpp @@ -313,7 +313,7 @@ namespace dawn_native { namespace opengl { 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 772928610a..c01d5fdeb1 100644 --- a/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -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) diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp index 1f29ea079b..fc412a93a4 100644 --- a/src/dawn_native/vulkan/TextureVk.cpp +++ b/src/dawn_native/vulkan/TextureVk.cpp @@ -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(); } diff --git a/src/tests/end2end/TextureZeroInitTests.cpp b/src/tests/end2end/TextureZeroInitTests.cpp index 64e518bde6..81dfc47ac3 100644 --- a/src/tests/end2end/TextureZeroInitTests.cpp +++ b/src/tests/end2end/TextureZeroInitTests.cpp @@ -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 data(kFormatBlockByteSize * kSize * kSize, 1); + dawn::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), dawn::BufferUsage::CopySrc); + dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); + dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); + dawn::Extent3D copySize = {kSize, kSize, 1}; + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + dawn::CommandBuffer commands = encoder.Finish(); + // Expect 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 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 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 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 d3bae711b1..bf7e67da02 100644 --- a/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp +++ b/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp @@ -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) {