Fix clearing of 3D textures with Vulkan backend
Fixes an internal validation error that was being raised when attempting
to do a clear of a 3D texture. A 2D view of the 3D texture was being
created, which was not allowed by Dawn's validation. Instead using a
3D texture and passing the depthSlice to the render pass.
Bug: 443950688
Change-Id: I9694ad5b42c66f9065c0d42bd20decee0565ffb7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/263595
Commit-Queue: Brandon Jones <bajones@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index 3e2cf3a..3a2bb62 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -1162,8 +1162,25 @@
beginCmd.height = mipSize.height;
TextureViewDescriptor viewDesc = {};
+ viewDesc.label = "Dawn_ClearTexture_View";
viewDesc.format = GetFormat().format;
- viewDesc.dimension = wgpu::TextureViewDimension::e2D;
+
+ uint32_t depthSliceCount = 1;
+ switch (GetDimension()) {
+ case wgpu::TextureDimension::e2D:
+ viewDesc.dimension = wgpu::TextureViewDimension::e2D;
+ break;
+ case wgpu::TextureDimension::e3D:
+ viewDesc.dimension = wgpu::TextureViewDimension::e3D;
+ depthSliceCount = mipSize.depthOrArrayLayers;
+ DAWN_ASSERT(layer == 0);
+ break;
+ case wgpu::TextureDimension::e1D:
+ case wgpu::TextureDimension::Undefined:
+ DAWN_UNREACHABLE();
+ break;
+ }
+
viewDesc.baseMipLevel = level;
viewDesc.mipLevelCount = 1u;
viewDesc.baseArrayLayer = layer;
@@ -1177,6 +1194,7 @@
RenderPassColorAttachment colorAttachment{};
colorAttachment.view = beginCmd.colorAttachments[ca0].view.Get();
+
beginCmd.colorAttachments[ca0].clearColor = colorAttachment.clearValue = {
fClearColor, fClearColor, fClearColor, fClearColor};
beginCmd.colorAttachments[ca0].loadOp = colorAttachment.loadOp =
@@ -1187,11 +1205,18 @@
RenderPassDescriptor passDesc{};
passDesc.colorAttachmentCount = 1u;
passDesc.colorAttachments = &colorAttachment;
- beginCmd.attachmentState = device->GetOrCreateAttachmentState(Unpack(&passDesc));
- DAWN_TRY(
- RecordBeginRenderPass(recordingContext, ToBackend(GetDevice()), &beginCmd));
- ToBackend(GetDevice())->fn.CmdEndRenderPass(recordingContext->commandBuffer);
+ for (uint32_t depthSlice = 0; depthSlice < depthSliceCount; ++depthSlice) {
+ beginCmd.colorAttachments[ca0].depthSlice = colorAttachment.depthSlice =
+ depthSlice;
+
+ beginCmd.attachmentState =
+ device->GetOrCreateAttachmentState(Unpack(&passDesc));
+
+ DAWN_TRY(
+ RecordBeginRenderPass(recordingContext, ToBackend(GetDevice()), &beginCmd));
+ ToBackend(GetDevice())->fn.CmdEndRenderPass(recordingContext->commandBuffer);
+ }
}
}
} else if (GetFormat().HasDepthOrStencil()) {
diff --git a/src/dawn/tests/end2end/TextureZeroInitTests.cpp b/src/dawn/tests/end2end/TextureZeroInitTests.cpp
index 346c432..8276ba2 100644
--- a/src/dawn/tests/end2end/TextureZeroInitTests.cpp
+++ b/src/dawn/tests/end2end/TextureZeroInitTests.cpp
@@ -55,15 +55,17 @@
DawnTest::SetUp();
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
}
- wgpu::TextureDescriptor CreateTextureDescriptor(uint32_t mipLevelCount,
- uint32_t arrayLayerCount,
- wgpu::TextureUsage usage,
- wgpu::TextureFormat format) {
+ wgpu::TextureDescriptor CreateTextureDescriptor(
+ uint32_t mipLevelCount,
+ uint32_t depthOrArrayLayers,
+ wgpu::TextureUsage usage,
+ wgpu::TextureFormat format,
+ wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
wgpu::TextureDescriptor descriptor;
- descriptor.dimension = wgpu::TextureDimension::e2D;
+ descriptor.dimension = dimension;
descriptor.size.width = kSize;
- descriptor.size.height = kSize;
- descriptor.size.depthOrArrayLayers = arrayLayerCount;
+ descriptor.size.height = dimension == wgpu::TextureDimension::e1D ? 1 : kSize;
+ descriptor.size.depthOrArrayLayers = depthOrArrayLayers;
descriptor.sampleCount = 1;
descriptor.format = format;
descriptor.mipLevelCount = mipLevelCount;
@@ -74,14 +76,15 @@
wgpu::TextureViewDescriptor CreateTextureViewDescriptor(
uint32_t baseMipLevel,
uint32_t baseArrayLayer,
- wgpu::TextureFormat format = kColorFormat) {
+ wgpu::TextureFormat format = kColorFormat,
+ wgpu::TextureViewDimension dimension = wgpu::TextureViewDimension::e2D) {
wgpu::TextureViewDescriptor descriptor;
descriptor.format = format;
descriptor.baseArrayLayer = baseArrayLayer;
descriptor.arrayLayerCount = 1;
descriptor.baseMipLevel = baseMipLevel;
descriptor.mipLevelCount = 1;
- descriptor.dimension = wgpu::TextureViewDimension::e2D;
+ descriptor.dimension = dimension;
return descriptor;
}
wgpu::RenderPipeline CreatePipelineForTest(float depth = 0.f) {
@@ -117,19 +120,55 @@
})";
return utils::CreateShaderModule(device, source.c_str());
}
- wgpu::ShaderModule CreateSampledTextureFragmentShaderForTest() {
- return utils::CreateShaderModule(device, R"(
- @group(0) @binding(0) var texture0 : texture_2d<f32>;
- struct FragmentOut {
- @location(0) color : vec4f
- }
- @fragment
- fn main(@builtin(position) FragCoord : vec4f) -> FragmentOut {
- var output : FragmentOut;
- output.color = textureLoad(texture0, vec2i(FragCoord.xy), 0);
- return output;
- }
- )");
+ wgpu::ShaderModule CreateSampledTextureFragmentShaderForTest(
+ wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
+ // - 1D duplicates the texture on every row of the output.
+ // - 2D copies the texture verbatim.
+ // - 3D takes a diagonal slice to make sure it checks some texels in every slice.
+ switch (dimension) {
+ case wgpu::TextureDimension::e1D:
+ return utils::CreateShaderModule(device, R"(
+ @group(0) @binding(0) var texture0 : texture_1d<f32>;
+ struct FragmentOut {
+ @location(0) color : vec4f
+ }
+ @fragment
+ fn main(@builtin(position) FragCoord : vec4f) -> FragmentOut {
+ var output : FragmentOut;
+ output.color = textureLoad(texture0, i32(FragCoord.x), 0);
+ return output;
+ }
+ )");
+ case wgpu::TextureDimension::e2D:
+ return utils::CreateShaderModule(device, R"(
+ @group(0) @binding(0) var texture0 : texture_2d<f32>;
+ struct FragmentOut {
+ @location(0) color : vec4f
+ }
+ @fragment
+ fn main(@builtin(position) FragCoord : vec4f) -> FragmentOut {
+ var output : FragmentOut;
+ output.color = textureLoad(texture0, vec2i(FragCoord.xy), 0);
+ return output;
+ }
+ )");
+ case wgpu::TextureDimension::e3D:
+ return utils::CreateShaderModule(device, R"(
+ @group(0) @binding(0) var texture0 : texture_3d<f32>;
+ struct FragmentOut {
+ @location(0) color : vec4f
+ }
+ @fragment
+ fn main(@builtin(position) FragCoord : vec4f) -> FragmentOut {
+ var output : FragmentOut;
+ output.color = textureLoad(texture0, vec3i(FragCoord.xyy), 0);
+ return output;
+ }
+ )");
+ case wgpu::TextureDimension::Undefined:
+ DAWN_UNREACHABLE();
+ break;
+ }
}
wgpu::Texture CreateAndFillStencilTexture(wgpu::TextureFormat format) {
@@ -162,6 +201,53 @@
return depthStencilTexture;
}
+ void DoRenderableSampledTextureClearTest(wgpu::TextureDimension dimension,
+ wgpu::TextureUsage usage) {
+ // Create needed resources
+ uint32_t depthOrArrayLayers = dimension == wgpu::TextureDimension::e3D ? kSize : 1;
+ wgpu::TextureDescriptor descriptor =
+ CreateTextureDescriptor(1, depthOrArrayLayers, usage, kColorFormat, dimension);
+ wgpu::Texture texture = device.CreateTexture(&descriptor);
+
+ wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
+ 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat);
+ wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
+
+ // Create render pipeline
+ utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
+ renderPipelineDescriptor.cTargets[0].format = kColorFormat;
+ renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest();
+ renderPipelineDescriptor.cFragment.module =
+ CreateSampledTextureFragmentShaderForTest(dimension);
+ wgpu::RenderPipeline renderPipeline =
+ device.CreateRenderPipeline(&renderPipelineDescriptor);
+
+ // Create bindgroup
+ wgpu::BindGroup bindGroup = utils::MakeBindGroup(
+ device, renderPipeline.GetBindGroupLayout(0), {{0, texture.CreateView()}});
+
+ // Encode pass and submit
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
+ renderPassDesc.cColorAttachments[0].clearValue = {1.0, 1.0, 1.0, 1.0};
+ renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
+ pass.SetPipeline(renderPipeline);
+ pass.SetBindGroup(0, bindGroup);
+ pass.Draw(6);
+ pass.End();
+ wgpu::CommandBuffer commands = encoder.Finish();
+ // Expect 1 lazy clear for sampled texture
+ EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
+
+ // Expect the rendered texture to be cleared
+ std::vector<utils::RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
+ EXPECT_TEXTURE_EQ(expectedWithZeros.data(), renderTexture, {0, 0}, {kSize, kSize});
+
+ // Expect texture subresource initialized to be true
+ EXPECT_EQ(true, native::IsTextureSubresourceInitialized(renderTexture.Get(), 0, 1, 0, 1));
+ }
+
constexpr static uint32_t kSize = 128;
constexpr static uint32_t kUnalignedSize = 127;
// All texture formats used (RGBA8Unorm, Depth24PlusStencil8, and RGBA8Snorm, BC formats)
@@ -1020,48 +1106,42 @@
EXPECT_EQ(true, native::IsTextureSubresourceInitialized(renderPass.color.Get(), 0, 1, 0, 1));
}
-// This tests the clearing of sampled textures in render pass
-TEST_P(TextureZeroInitTest, RenderPassSampledTextureClear) {
- // Create needed resources
- wgpu::TextureDescriptor descriptor =
- CreateTextureDescriptor(1, 1, wgpu::TextureUsage::TextureBinding, kColorFormat);
- wgpu::Texture texture = device.CreateTexture(&descriptor);
+// This tests the clearing of sampled 1D textures in render pass
+TEST_P(TextureZeroInitTest, RenderPassSampled1DTextureClear) {
+ DoRenderableSampledTextureClearTest(wgpu::TextureDimension::e1D,
+ wgpu::TextureUsage::TextureBinding);
+}
- wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
- 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat);
- wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
+// This tests the clearing of sampled 2D textures in render pass
+TEST_P(TextureZeroInitTest, RenderPassSampled2DTextureClear) {
+ DoRenderableSampledTextureClearTest(wgpu::TextureDimension::e2D,
+ wgpu::TextureUsage::TextureBinding);
+}
- // Create render pipeline
- utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
- renderPipelineDescriptor.cTargets[0].format = kColorFormat;
- renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest();
- renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest();
- wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
+// This tests the clearing of renderable 2D textures in render pass
+TEST_P(TextureZeroInitTest, RenderPassRenderable2DTextureClear) {
+ DoRenderableSampledTextureClearTest(
+ wgpu::TextureDimension::e2D,
+ wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment);
+}
- // Create bindgroup
- wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
- {{0, texture.CreateView()}});
+// This tests the clearing of sampled 3D textures in render pass
+TEST_P(TextureZeroInitTest, RenderPassSampled3DTextureClear) {
+ // TODO(448982392): Failing in compat mode.
+ DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode());
- // Encode pass and submit
- wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
- renderPassDesc.cColorAttachments[0].clearValue = {1.0, 1.0, 1.0, 1.0};
- renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
- pass.SetPipeline(renderPipeline);
- pass.SetBindGroup(0, bindGroup);
- pass.Draw(6);
- pass.End();
- wgpu::CommandBuffer commands = encoder.Finish();
- // Expect 1 lazy clear for sampled texture
- EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
+ DoRenderableSampledTextureClearTest(wgpu::TextureDimension::e3D,
+ wgpu::TextureUsage::TextureBinding);
+}
- // Expect the rendered texture to be cleared
- std::vector<utils::RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
- EXPECT_TEXTURE_EQ(expectedWithZeros.data(), renderTexture, {0, 0}, {kSize, kSize});
+// This tests the clearing of renderable 3D textures in render pass
+TEST_P(TextureZeroInitTest, RenderPassRenderable3DTextureClear) {
+ // TODO(448982392): Failing in compat mode.
+ DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode());
- // Expect texture subresource initialized to be true
- EXPECT_EQ(true, native::IsTextureSubresourceInitialized(renderTexture.Get(), 0, 1, 0, 1));
+ DoRenderableSampledTextureClearTest(
+ wgpu::TextureDimension::e3D,
+ wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment);
}
// This is a regression test for a bug where a texture wouldn't get clear for a pass if at least