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