TextureView defaults and validation updates

Spec PR: https://github.com/gpuweb/gpuweb/pull/2687

Fixed: dawn:682
Bug: dawn:1276
Change-Id: Ifa8f94fa4c1a27fb40d0ccfb9f032ca4a28ed24e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/84520
Reviewed-by: Loko Kung <lokokung@google.com>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index 51cd391..e46bdf4 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -49,10 +49,6 @@
                                             wgpu::TextureAspect aspect) {
             const Format& format = texture->GetFormat();
 
-            if (format.format == viewFormat.format) {
-                return {};
-            }
-
             if (aspect != wgpu::TextureAspect::All) {
                 wgpu::TextureFormat aspectFormat = format.GetAspectInfo(aspect).format;
                 if (viewFormat.format == aspectFormat) {
@@ -64,6 +60,10 @@
                 }
             }
 
+            if (format.format == viewFormat.format) {
+                return {};
+            }
+
             const FormatSet& compatibleViewFormats = texture->GetViewFormats();
             if (compatibleViewFormats[viewFormat]) {
                 // Validation of this list is done on texture creation, so we don't need to
@@ -471,8 +471,13 @@
         }
 
         if (desc.format == wgpu::TextureFormat::Undefined) {
-            // TODO(dawn:682): Use GetAspectInfo(aspect).
-            desc.format = texture->GetFormat().format;
+            const Format& format = texture->GetFormat();
+            Aspect aspects = SelectFormatAspects(format, desc.aspect);
+            if (HasOneBit(aspects)) {
+                desc.format = format.GetAspectInfo(aspects).format;
+            } else {
+                desc.format = format.format;
+            }
         }
         if (desc.arrayLayerCount == wgpu::kArrayLayerCountUndefined) {
             switch (desc.dimension) {
diff --git a/src/dawn/native/d3d12/TextureD3D12.cpp b/src/dawn/native/d3d12/TextureD3D12.cpp
index bc80c00..5e79417 100644
--- a/src/dawn/native/d3d12/TextureD3D12.cpp
+++ b/src/dawn/native/d3d12/TextureD3D12.cpp
@@ -1170,13 +1170,12 @@
         mSrvDesc.Format = D3D12TextureFormat(descriptor->format);
         mSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
 
-        // TODO(enga): This will need to be much more nuanced when WebGPU has
-        // texture view compatibility rules.
         UINT planeSlice = 0;
-        if (GetFormat().HasDepthOrStencil()) {
+        const Format& textureFormat = texture->GetFormat();
+        if (textureFormat.HasDepthOrStencil()) {
             // Configure the SRV descriptor to reinterpret the texture allocated as
             // TYPELESS as a single-plane shader-accessible view.
-            switch (descriptor->format) {
+            switch (textureFormat.format) {
                 case wgpu::TextureFormat::Depth32Float:
                 case wgpu::TextureFormat::Depth24Plus:
                     mSrvDesc.Format = DXGI_FORMAT_R32_FLOAT;
@@ -1184,28 +1183,22 @@
                 case wgpu::TextureFormat::Depth16Unorm:
                     mSrvDesc.Format = DXGI_FORMAT_R16_UNORM;
                     break;
-                case wgpu::TextureFormat::Stencil8: {
-                    // Stencil8 is always backed by a DXGI_FORMAT_R24G8_TYPELESS texture in D3D12,
-                    // so always treat it as if the StencilOnly aspect of a Depth24UnormStencil8 was
-                    // selected.
-                    planeSlice = 1;
-                    mSrvDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
-                    // Stencil is accessed using the .g component in the shader.
-                    // Map it to the zeroth component to match other APIs.
-                    mSrvDesc.Shader4ComponentMapping = D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
-                        D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
-                        D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
-                        D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
-                        D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1);
-                    break;
-                }
-                case wgpu::TextureFormat::Depth24UnormStencil8:
-                    switch (descriptor->aspect) {
-                        case wgpu::TextureAspect::DepthOnly:
+                case wgpu::TextureFormat::Stencil8:
+                case wgpu::TextureFormat::Depth24UnormStencil8: {
+                    Aspect aspects = SelectFormatAspects(textureFormat, descriptor->aspect);
+                    ASSERT(aspects != Aspect::None);
+                    if (!HasZeroOrOneBits(aspects)) {
+                        // A single aspect is not selected. The texture view must not be
+                        // sampled.
+                        mSrvDesc.Format = DXGI_FORMAT_UNKNOWN;
+                        break;
+                    }
+                    switch (aspects) {
+                        case Aspect::Depth:
                             planeSlice = 0;
                             mSrvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
                             break;
-                        case wgpu::TextureAspect::StencilOnly:
+                        case Aspect::Stencil:
                             planeSlice = 1;
                             mSrvDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
                             // Stencil is accessed using the .g component in the shader.
@@ -1217,27 +1210,28 @@
                                     D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
                                     D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1);
                             break;
-                        case wgpu::TextureAspect::All:
-                            // A single aspect is not selected. The texture view must not be
-                            // sampled.
-                            mSrvDesc.Format = DXGI_FORMAT_UNKNOWN;
-                            break;
-
-                        // Depth formats cannot use plane aspects.
-                        case wgpu::TextureAspect::Plane0Only:
-                        case wgpu::TextureAspect::Plane1Only:
+                        default:
                             UNREACHABLE();
                             break;
                     }
                     break;
+                }
                 case wgpu::TextureFormat::Depth24PlusStencil8:
-                case wgpu::TextureFormat::Depth32FloatStencil8:
-                    switch (descriptor->aspect) {
-                        case wgpu::TextureAspect::DepthOnly:
+                case wgpu::TextureFormat::Depth32FloatStencil8: {
+                    Aspect aspects = SelectFormatAspects(textureFormat, descriptor->aspect);
+                    ASSERT(aspects != Aspect::None);
+                    if (!HasZeroOrOneBits(aspects)) {
+                        // A single aspect is not selected. The texture view must not be
+                        // sampled.
+                        mSrvDesc.Format = DXGI_FORMAT_UNKNOWN;
+                        break;
+                    }
+                    switch (aspects) {
+                        case Aspect::Depth:
                             planeSlice = 0;
                             mSrvDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
                             break;
-                        case wgpu::TextureAspect::StencilOnly:
+                        case Aspect::Stencil:
                             planeSlice = 1;
                             mSrvDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
                             // Stencil is accessed using the .g component in the shader.
@@ -1249,19 +1243,12 @@
                                     D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
                                     D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1);
                             break;
-                        case wgpu::TextureAspect::All:
-                            // A single aspect is not selected. The texture view must not be
-                            // sampled.
-                            mSrvDesc.Format = DXGI_FORMAT_UNKNOWN;
-                            break;
-
-                        // Depth formats cannot use plane aspects.
-                        case wgpu::TextureAspect::Plane0Only:
-                        case wgpu::TextureAspect::Plane1Only:
+                        default:
                             UNREACHABLE();
                             break;
                     }
                     break;
+                }
                 default:
                     UNREACHABLE();
                     break;
diff --git a/src/dawn/native/metal/TextureMTL.mm b/src/dawn/native/metal/TextureMTL.mm
index 20c5650..c6fd75e 100644
--- a/src/dawn/native/metal/TextureMTL.mm
+++ b/src/dawn/native/metal/TextureMTL.mm
@@ -86,7 +86,10 @@
 
         bool RequiresCreatingNewTextureView(const TextureBase* texture,
                                             const TextureViewDescriptor* textureViewDescriptor) {
-            if (texture->GetFormat().format != textureViewDescriptor->format) {
+            if (texture->GetFormat().format != textureViewDescriptor->format &&
+                !texture->GetFormat().HasDepthOrStencil()) {
+                // Color format reinterpretation required. Note: Depth/stencil formats don't support
+                // reinterpretation.
                 return true;
             }
 
@@ -1059,16 +1062,17 @@
                     "Failed to create MTLTexture view for external texture.");
             }
         } else {
-            MTLPixelFormat format = MetalPixelFormat(descriptor->format);
+            MTLPixelFormat viewFormat = MetalPixelFormat(descriptor->format);
+            MTLPixelFormat textureFormat = MetalPixelFormat(GetTexture()->GetFormat().format);
             if (descriptor->aspect == wgpu::TextureAspect::StencilOnly &&
-                format != MTLPixelFormatStencil8) {
+                textureFormat != MTLPixelFormatStencil8) {
                 if (@available(macOS 10.12, iOS 10.0, *)) {
-                    if (format == MTLPixelFormatDepth32Float_Stencil8) {
-                        format = MTLPixelFormatX32_Stencil8;
+                    if (textureFormat == MTLPixelFormatDepth32Float_Stencil8) {
+                        viewFormat = MTLPixelFormatX32_Stencil8;
                     }
 #if defined(DAWN_PLATFORM_MACOS)
-                    else if (format == MTLPixelFormatDepth24Unorm_Stencil8) {
-                        format = MTLPixelFormatX24_Stencil8;
+                    else if (textureFormat == MTLPixelFormatDepth24Unorm_Stencil8) {
+                        viewFormat = MTLPixelFormatX24_Stencil8;
                     }
 #endif
                     else {
@@ -1082,6 +1086,11 @@
                         DAWN_DEVICE_LOST_ERROR("Cannot create stencil-only texture view of "
                                                "combined depth/stencil format."));
                 }
+            } else if (GetTexture()->GetFormat().HasDepth() &&
+                       GetTexture()->GetFormat().HasStencil()) {
+                // Depth-only views for depth/stencil textures in Metal simply use the original
+                // texture's format.
+                viewFormat = textureFormat;
             }
 
             MTLTextureType textureViewType =
@@ -1091,7 +1100,7 @@
                 NSMakeRange(descriptor->baseArrayLayer, descriptor->arrayLayerCount);
 
             mMtlTextureView =
-                AcquireNSPRef([mtlTexture newTextureViewWithPixelFormat:format
+                AcquireNSPRef([mtlTexture newTextureViewWithPixelFormat:viewFormat
                                                             textureType:textureViewType
                                                                  levels:mipLevelRange
                                                                  slices:arrayLayerRange]);
diff --git a/src/dawn/native/opengl/TextureGL.cpp b/src/dawn/native/opengl/TextureGL.cpp
index 1189007..fc4431f 100644
--- a/src/dawn/native/opengl/TextureGL.cpp
+++ b/src/dawn/native/opengl/TextureGL.cpp
@@ -95,7 +95,10 @@
 
         bool RequiresCreatingNewTextureView(const TextureBase* texture,
                                             const TextureViewDescriptor* textureViewDescriptor) {
-            if (texture->GetFormat().format != textureViewDescriptor->format) {
+            if (texture->GetFormat().format != textureViewDescriptor->format &&
+                !texture->GetFormat().HasDepthOrStencil()) {
+                // Color format reinterpretation required. Note: Depth/stencil formats don't support
+                // reinterpretation.
                 return true;
             }
 
@@ -561,7 +564,14 @@
             const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
             mHandle = GenTexture(gl);
             const Texture* textureGL = ToBackend(texture);
-            const GLFormat& glFormat = ToBackend(GetDevice())->GetGLFormat(GetFormat());
+
+            const Format& textureFormat = GetTexture()->GetFormat();
+            // Depth/stencil don't support reinterpretation, and the aspect is specified at
+            // bind time. In that case, we use the base texture format.
+            const GLFormat& glFormat = textureFormat.HasDepthOrStencil()
+                                           ? ToBackend(GetDevice())->GetGLFormat(textureFormat)
+                                           : ToBackend(GetDevice())->GetGLFormat(GetFormat());
+
             gl.TextureView(mHandle, mTarget, textureGL->GetHandle(), glFormat.internalFormat,
                            descriptor->baseMipLevel, descriptor->mipLevelCount,
                            descriptor->baseArrayLayer, descriptor->arrayLayerCount);
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index 8a76b89..e6adf14 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -1373,7 +1373,20 @@
         createInfo.flags = 0;
         createInfo.image = ToBackend(GetTexture())->GetHandle();
         createInfo.viewType = VulkanImageViewType(descriptor->dimension);
-        createInfo.format = VulkanImageFormat(device, descriptor->format);
+
+        const Format& textureFormat = GetTexture()->GetFormat();
+        if (textureFormat.HasStencil() &&
+            (textureFormat.HasDepth() || !device->IsToggleEnabled(Toggle::VulkanUseS8))) {
+            // Unlike multi-planar formats, depth-stencil formats have multiple aspects but are not
+            // created with VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT.
+            // https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkImageViewCreateInfo.html#VUID-VkImageViewCreateInfo-image-01762
+            // Without, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, the view format must match the texture
+            // format.
+            createInfo.format = VulkanImageFormat(device, textureFormat.format);
+        } else {
+            createInfo.format = VulkanImageFormat(device, descriptor->format);
+        }
+
         createInfo.components = VkComponentMapping{VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
                                                    VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
 
diff --git a/src/dawn/tests/unittests/validation/TextureViewValidationTests.cpp b/src/dawn/tests/unittests/validation/TextureViewValidationTests.cpp
index ea5ab30..060f39b 100644
--- a/src/dawn/tests/unittests/validation/TextureViewValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/TextureViewValidationTests.cpp
@@ -709,6 +709,26 @@
             ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
         }
 
+        // It is invalid to create a texture view with a combined depth-stencil format if only
+        // the depth aspect is selected.
+        {
+            textureDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
+            viewDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
+            viewDesc.aspect = wgpu::TextureAspect::DepthOnly;
+            wgpu::Texture texture = device.CreateTexture(&textureDesc);
+            ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
+        }
+
+        // It is invalid to create a texture view with a combined depth-stencil format if only
+        // the stencil aspect is selected.
+        {
+            textureDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
+            viewDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
+            viewDesc.aspect = wgpu::TextureAspect::StencilOnly;
+            wgpu::Texture texture = device.CreateTexture(&textureDesc);
+            ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc));
+        }
+
         // It is valid to create a texture view with a depth format of a depth-stencil texture
         // if the depth only aspect is selected.
         {