D3D12: Support per plane views with NV12 textures

Adds support for NV12 texture format and per plane view aspects.
Only allows planar sampling of imported DX11 textures. See usage
tests for examples and formats.h for rules.

Bug: dawn:551
Change-Id: I44b89d2c07bb9969638e77ce7c756ef367167f0c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/38781
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp
index 04dfb22..174510b 100644
--- a/src/dawn_native/CommandValidation.cpp
+++ b/src/dawn_native/CommandValidation.cpp
@@ -355,7 +355,8 @@
         return {};
     }
 
-    // Always returns a single aspect (color, stencil, or depth).
+    // Always returns a single aspect (color, stencil, depth, or ith plane for multi-planar
+    // formats).
     ResultOrError<Aspect> SingleAspectUsedByTextureCopyView(const TextureCopyView& view) {
         const Format& format = view.texture->GetFormat();
         switch (view.aspect) {
@@ -375,6 +376,9 @@
             case wgpu::TextureAspect::StencilOnly:
                 ASSERT(format.aspects & Aspect::Stencil);
                 return Aspect::Stencil;
+            case wgpu::TextureAspect::Plane0Only:
+            case wgpu::TextureAspect::Plane1Only:
+                UNREACHABLE();
         }
     }
 
diff --git a/src/dawn_native/Extensions.cpp b/src/dawn_native/Extensions.cpp
index d356616..98d4a61 100644
--- a/src/dawn_native/Extensions.cpp
+++ b/src/dawn_native/Extensions.cpp
@@ -47,7 +47,12 @@
              {Extension::TimestampQuery,
               {"timestamp_query", "Support Timestamp Query",
                "https://bugs.chromium.org/p/dawn/issues/detail?id=434"},
-              &WGPUDeviceProperties::timestampQuery}}};
+              &WGPUDeviceProperties::timestampQuery},
+             {Extension::MultiPlanarFormats,
+              {"multiplanar_formats",
+               "Import and use multi-planar texture formats with per plane views",
+               "https://bugs.chromium.org/p/dawn/issues/detail?id=551"},
+              &WGPUDeviceProperties::multiPlanarFormats}}};
 
     }  // anonymous namespace
 
diff --git a/src/dawn_native/Extensions.h b/src/dawn_native/Extensions.h
index ba32ee1..08689ca 100644
--- a/src/dawn_native/Extensions.h
+++ b/src/dawn_native/Extensions.h
@@ -28,6 +28,7 @@
         ShaderFloat16,
         PipelineStatisticsQuery,
         TimestampQuery,
+        MultiPlanarFormats,
 
         EnumCount,
         InvalidEnum = EnumCount,
diff --git a/src/dawn_native/Format.cpp b/src/dawn_native/Format.cpp
index f1d7d16..9a137ba 100644
--- a/src/dawn_native/Format.cpp
+++ b/src/dawn_native/Format.cpp
@@ -27,6 +27,15 @@
         static const AspectInfo kStencil8AspectInfo = {{1, 1, 1},
                                                        wgpu::TextureComponentType::Uint,
                                                        ComponentTypeBit::Uint};
+
+        // R8BG8Biplanar420Unorm must be specialized since it represents planar data and cannot be
+        // used without a per plane format. In particular, the component type is float since
+        // Dawn does not allow texture format reinterpretion (ex. using R8BG82plane420 with Uint or
+        // Unorm). Block size is always zero since the format is not renderable or copyable.
+        static const AspectInfo kR8BG8Biplanar420UnormAspectInfo = {
+            {0, 0, 0},
+            wgpu::TextureComponentType::Float,
+            ComponentTypeBit::Float};
     }
 
     // Format
@@ -99,6 +108,10 @@
         return (aspects & (Aspect::Depth | Aspect::Stencil)) != 0;
     }
 
+    bool Format::IsMultiPlanar() const {
+        return (aspects & (Aspect::Plane0 | Aspect::Plane1)) != 0;
+    }
+
     const AspectInfo& Format::GetAspectInfo(wgpu::TextureAspect aspect) const {
         return GetAspectInfo(ConvertAspect(*this, aspect));
     }
@@ -111,6 +124,12 @@
         // same aspect information, special case it to return a constant AspectInfo.
         if (aspect == Aspect::Stencil) {
             return kStencil8AspectInfo;
+            // multi-planar formats are specified per plane aspect. Since it does not support
+            // non-planar access, it can always be the same aspect information, special cased to
+            // return a constant AspectInfo.
+            // TODO(dawn:551): Refactor and remove GetAspectFormat.
+        } else if (format == wgpu::TextureFormat::R8BG8Biplanar420Unorm) {
+            return kR8BG8Biplanar420UnormAspectInfo;
         } else {
             return firstAspect;
         }
@@ -120,6 +139,24 @@
         return ComputeFormatIndex(format);
     }
 
+    wgpu::TextureFormat Format::GetAspectFormat(wgpu::TextureAspect aspect) const {
+        switch (format) {
+            case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+                switch (aspect) {
+                    case wgpu::TextureAspect::Plane0Only:
+                        return wgpu::TextureFormat::R8Unorm;
+                    case wgpu::TextureAspect::Plane1Only:
+                        return wgpu::TextureFormat::RG8Unorm;
+                    default:
+                        return wgpu::TextureFormat::Undefined;
+                }
+                break;
+            // TODO(dawn:551): Consider using for depth-stencil formats.
+            default:
+                return format;
+        }
+    }
+
     // Implementation details of the format table of the DeviceBase
 
     // For the enum for formats are packed but this might change when we have a broader extension
@@ -148,7 +185,9 @@
 
             // Vulkan describes bytesPerRow in units of texels. If there's any format for which this
             // ASSERT isn't true, then additional validation on bytesPerRow must be added.
-            ASSERT((kTextureBytesPerRowAlignment % format.firstAspect.block.byteSize) == 0);
+            // Multi-planar formats are not copyable and have no first aspect.
+            ASSERT(format.IsMultiPlanar() ||
+                   (kTextureBytesPerRowAlignment % format.firstAspect.block.byteSize) == 0);
 
             table[index] = format;
             formatsSet.set(index);
@@ -207,6 +246,18 @@
             AddFormat(internalFormat);
         };
 
+        auto AddMultiPlanarFormat = [&AddFormat](wgpu::TextureFormat format, Aspect aspects,
+                                                 bool isSupported) {
+            Format internalFormat;
+            internalFormat.format = format;
+            internalFormat.isRenderable = false;
+            internalFormat.isCompressed = false;
+            internalFormat.isSupported = isSupported;
+            internalFormat.supportsStorageUsage = false;
+            internalFormat.aspects = aspects;
+            AddFormat(internalFormat);
+        };
+
         // clang-format off
 
         // 1 byte color formats
@@ -281,6 +332,10 @@
         AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnorm, 16, 4, 4, isBCFormatSupported);
         AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported);
 
+        // multi-planar formats
+        const bool isMultiPlanarFormatSupported = device->IsExtensionEnabled(Extension::MultiPlanarFormats);
+        AddMultiPlanarFormat(wgpu::TextureFormat::R8BG8Biplanar420Unorm, Aspect::Plane0 | Aspect::Plane1, isMultiPlanarFormatSupported);
+
         // clang-format on
 
         // This checks that each format is set at least once, the second part of checking that all
diff --git a/src/dawn_native/Format.h b/src/dawn_native/Format.h
index fe0c41f..b02d493 100644
--- a/src/dawn_native/Format.h
+++ b/src/dawn_native/Format.h
@@ -23,6 +23,22 @@
 
 #include <array>
 
+// About multi-planar formats.
+//
+// Dawn supports additional multi-planar formats when the multiplanar_formats extension is enabled.
+// When enabled, Dawn treats planar data as sub-resources (ie. 1 sub-resource == 1 view == 1 plane).
+// A multi-planar format name encodes the channel mapping and order of planes. For example,
+// R8BG8Biplanar420Unorm is YUV 4:2:0 where Plane 0 = R8, and Plane 1 = BG8.
+//
+// Requirements:
+// * Plane aspects cannot be combined with color, depth, or stencil aspects.
+// * Only compatible multi-planar formats of planes can be used with multi-planar texture
+// formats.
+// * Can't access multiple planes without creating per plane views (no color conversion).
+// * Multi-planar format cannot be written or read without a per plane view.
+//
+// TODO(dawn:551): Consider moving this comment.
+
 namespace dawn_native {
 
     enum class Aspect : uint8_t;
@@ -56,7 +72,7 @@
 
     // The number of formats Dawn knows about. Asserts in BuildFormatTable ensure that this is the
     // exact number of known format.
-    static constexpr size_t kKnownFormatCount = 53;
+    static constexpr size_t kKnownFormatCount = 54;
 
     struct Format;
     using FormatTable = std::array<Format, kKnownFormatCount>;
@@ -76,6 +92,10 @@
         bool HasStencil() const;
         bool HasDepthOrStencil() const;
 
+        // IsMultiPlanar() returns true if the format allows selecting a plane index. This is only
+        // allowed by multi-planar formats (ex. NV12).
+        bool IsMultiPlanar() const;
+
         const AspectInfo& GetAspectInfo(wgpu::TextureAspect aspect) const;
         const AspectInfo& GetAspectInfo(Aspect aspect) const;
 
@@ -83,6 +103,10 @@
         // in [0, kKnownFormatCount)
         size_t GetIndex() const;
 
+        // Used to lookup the compatible view format using an aspect which corresponds to the
+        // plane index. Returns Undefined if the wrong plane aspect is requested.
+        wgpu::TextureFormat GetAspectFormat(wgpu::TextureAspect aspect) const;
+
       private:
         // The most common aspect: the color aspect for color texture, the depth aspect for
         // depth[-stencil] textures.
diff --git a/src/dawn_native/Subresource.cpp b/src/dawn_native/Subresource.cpp
index ad9e0ee..1114bae 100644
--- a/src/dawn_native/Subresource.cpp
+++ b/src/dawn_native/Subresource.cpp
@@ -31,6 +31,21 @@
         return aspectMask;
     }
 
+    Aspect ConvertViewAspect(const Format& format, wgpu::TextureAspect aspect) {
+        // Color view |format| must be treated as the same plane |aspect|.
+        if (format.aspects == Aspect::Color) {
+            switch (aspect) {
+                case wgpu::TextureAspect::Plane0Only:
+                    return Aspect::Plane0;
+                case wgpu::TextureAspect::Plane1Only:
+                    return Aspect::Plane1;
+                default:
+                    break;
+            }
+        }
+        return ConvertAspect(format, aspect);
+    }
+
     Aspect SelectFormatAspects(const Format& format, wgpu::TextureAspect aspect) {
         switch (aspect) {
             case wgpu::TextureAspect::All:
@@ -39,6 +54,10 @@
                 return format.aspects & Aspect::Depth;
             case wgpu::TextureAspect::StencilOnly:
                 return format.aspects & Aspect::Stencil;
+            case wgpu::TextureAspect::Plane0Only:
+                return format.aspects & Aspect::Plane0;
+            case wgpu::TextureAspect::Plane1Only:
+                return format.aspects & Aspect::Plane1;
         }
     }
 
@@ -47,8 +66,10 @@
         switch (aspect) {
             case Aspect::Color:
             case Aspect::Depth:
+            case Aspect::Plane0:
             case Aspect::CombinedDepthStencil:
                 return 0;
+            case Aspect::Plane1:
             case Aspect::Stencil:
                 return 1;
             default:
@@ -63,6 +84,8 @@
         if (aspects == Aspect::Color || aspects == Aspect::Depth ||
             aspects == Aspect::CombinedDepthStencil) {
             return 1;
+        } else if (aspects == (Aspect::Plane0 | Aspect::Plane1)) {
+            return 2;
         } else {
             ASSERT(aspects == (Aspect::Depth | Aspect::Stencil));
             return 2;
diff --git a/src/dawn_native/Subresource.h b/src/dawn_native/Subresource.h
index 1cf439c..ce9b3f3 100644
--- a/src/dawn_native/Subresource.h
+++ b/src/dawn_native/Subresource.h
@@ -29,14 +29,18 @@
         Depth = 0x2,
         Stencil = 0x4,
 
+        // Aspects used to select individual planes in a multi-planar format.
+        Plane0 = 0x8,
+        Plane1 = 0x10,
+
         // An aspect for that represents the combination of both the depth and stencil aspects. It
         // can be ignored outside of the Vulkan backend.
-        CombinedDepthStencil = 0x8,
+        CombinedDepthStencil = 0x20,
     };
 
     template <>
     struct EnumBitmaskSize<Aspect> {
-        static constexpr unsigned value = 4;
+        static constexpr unsigned value = 6;
     };
 
     // Convert the TextureAspect to an Aspect mask for the format. ASSERTs if the aspect
@@ -53,6 +57,10 @@
     // selected aspects.
     Aspect SelectFormatAspects(const Format& format, wgpu::TextureAspect aspect);
 
+    // Convert TextureAspect to the aspect which corresponds to the view format. This
+    // special cases per plane view formats before calling ConvertAspect.
+    Aspect ConvertViewAspect(const Format& format, wgpu::TextureAspect aspect);
+
     // Helper struct to make it clear that what the parameters of a range mean.
     template <typename T>
     struct FirstAndCountRange {
diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp
index 127dc39..8bfe2c5 100644
--- a/src/dawn_native/Texture.cpp
+++ b/src/dawn_native/Texture.cpp
@@ -29,7 +29,7 @@
         // TODO(jiawei.shao@intel.com): implement texture view format compatibility rule
         MaybeError ValidateTextureViewFormatCompatibility(const TextureBase* texture,
                                                           const TextureViewDescriptor* descriptor) {
-            if (texture->GetFormat().format != descriptor->format) {
+            if (texture->GetFormat().GetAspectFormat(descriptor->aspect) != descriptor->format) {
                 return DAWN_VALIDATION_ERROR(
                     "The format of texture view is not compatible to the original texture");
             }
@@ -227,6 +227,11 @@
                 return DAWN_VALIDATION_ERROR("Format cannot be used in storage textures");
             }
 
+            constexpr wgpu::TextureUsage kValidMultiPlanarUsages = wgpu::TextureUsage::Sampled;
+            if (format->IsMultiPlanar() && !IsSubset(descriptor->usage, kValidMultiPlanarUsages)) {
+                return DAWN_VALIDATION_ERROR("Multi-planar format doesn't have valid usage.");
+            }
+
             return {};
         }
 
@@ -352,7 +357,7 @@
         }
 
         if (desc.format == wgpu::TextureFormat::Undefined) {
-            desc.format = texture->GetFormat().format;
+            desc.format = texture->GetFormat().GetAspectFormat(desc.aspect);
         }
         if (desc.arrayLayerCount == 0) {
             desc.arrayLayerCount = texture->GetArrayLayers() - desc.baseArrayLayer;
@@ -608,7 +613,7 @@
           mTexture(texture),
           mFormat(GetDevice()->GetValidInternalFormat(descriptor->format)),
           mDimension(descriptor->dimension),
-          mRange({ConvertAspect(mFormat, descriptor->aspect),
+          mRange({ConvertViewAspect(mFormat, descriptor->aspect),
                   {descriptor->baseArrayLayer, descriptor->arrayLayerCount},
                   {descriptor->baseMipLevel, descriptor->mipLevelCount}}) {
     }
diff --git a/src/dawn_native/d3d12/AdapterD3D12.cpp b/src/dawn_native/d3d12/AdapterD3D12.cpp
index 3d9f50b..8b31f39 100644
--- a/src/dawn_native/d3d12/AdapterD3D12.cpp
+++ b/src/dawn_native/d3d12/AdapterD3D12.cpp
@@ -122,6 +122,7 @@
         if (mDeviceInfo.supportsShaderFloat16 && GetBackend()->GetFunctions()->IsDXCAvailable()) {
             mSupportedExtensions.EnableExtension(Extension::ShaderFloat16);
         }
+        mSupportedExtensions.EnableExtension(Extension::MultiPlanarFormats);
     }
 
     MaybeError Adapter::InitializeDebugLayerFilters() {
diff --git a/src/dawn_native/d3d12/D3D12Info.cpp b/src/dawn_native/d3d12/D3D12Info.cpp
index b270693..d7a0f5e 100644
--- a/src/dawn_native/d3d12/D3D12Info.cpp
+++ b/src/dawn_native/d3d12/D3D12Info.cpp
@@ -59,6 +59,20 @@
             }
         }
 
+        // Used to share resources cross-API. If we query CheckFeatureSupport for
+        // D3D12_FEATURE_D3D12_OPTIONS4 successfully, then we can use cross-API sharing.
+        info.supportsSharedResourceCapabilityTier1 = false;
+        D3D12_FEATURE_DATA_D3D12_OPTIONS4 featureOptions4 = {};
+        if (SUCCEEDED(adapter.GetDevice()->CheckFeatureSupport(
+                D3D12_FEATURE_D3D12_OPTIONS4, &featureOptions4, sizeof(featureOptions4)))) {
+            // Tier 1 support additionally enables the NV12 format. Since only the NV12 format
+            // is used by Dawn, check for Tier 1.
+            if (featureOptions4.SharedResourceCompatibilityTier >=
+                D3D12_SHARED_RESOURCE_COMPATIBILITY_TIER_1) {
+                info.supportsSharedResourceCapabilityTier1 = true;
+            }
+        }
+
         D3D12_FEATURE_DATA_SHADER_MODEL knownShaderModels[] = {{D3D_SHADER_MODEL_6_2},
                                                                {D3D_SHADER_MODEL_6_1},
                                                                {D3D_SHADER_MODEL_6_0},
diff --git a/src/dawn_native/d3d12/D3D12Info.h b/src/dawn_native/d3d12/D3D12Info.h
index 46b2d09..a719595 100644
--- a/src/dawn_native/d3d12/D3D12Info.h
+++ b/src/dawn_native/d3d12/D3D12Info.h
@@ -32,6 +32,7 @@
         // indicates that current driver supports the maximum shader model is shader model 6.2.
         uint32_t shaderModel;
         PerStage<std::wstring> shaderProfiles;
+        bool supportsSharedResourceCapabilityTier1;
     };
 
     ResultOrError<D3D12DeviceInfo> GatherDeviceInfo(const Adapter& adapter);
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index f7d5d04..a136c4c 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -202,6 +202,7 @@
                 case wgpu::TextureFormat::BC7RGBAUnormSrgb:
                     return DXGI_FORMAT_BC7_TYPELESS;
 
+                case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
                 case wgpu::TextureFormat::Undefined:
                     UNREACHABLE();
             }
@@ -324,6 +325,9 @@
             case wgpu::TextureFormat::BC7RGBAUnormSrgb:
                 return DXGI_FORMAT_BC7_UNORM_SRGB;
 
+            case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+                return DXGI_FORMAT_NV12;
+
             case wgpu::TextureFormat::Undefined:
                 UNREACHABLE();
         }
@@ -379,11 +383,34 @@
         return {};
     }
 
+    // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_shared_resource_compatibility_tier
+    MaybeError ValidateD3D12VideoTextureCanBeShared(Device* device, DXGI_FORMAT textureFormat) {
+        const bool supportsSharedResourceCapabilityTier1 =
+            device->GetDeviceInfo().supportsSharedResourceCapabilityTier1;
+        switch (textureFormat) {
+            // MSDN docs are not correct, NV12 requires at-least tier 1.
+            case DXGI_FORMAT_NV12:
+                if (supportsSharedResourceCapabilityTier1) {
+                    return {};
+                }
+                break;
+            default:
+                break;
+        }
+
+        return DAWN_VALIDATION_ERROR("DXGI format does not support cross-API sharing.");
+    }
+
     // static
     ResultOrError<Ref<Texture>> Texture::Create(Device* device,
                                                 const TextureDescriptor* descriptor) {
         Ref<Texture> dawnTexture =
             AcquireRef(new Texture(device, descriptor, TextureState::OwnedInternal));
+
+        if (dawnTexture->GetFormat().IsMultiPlanar()) {
+            return DAWN_VALIDATION_ERROR("Cannot create a multi-planar formatted texture directly");
+        }
+
         DAWN_TRY(dawnTexture->InitializeAsInternalTexture());
         return std::move(dawnTexture);
     }
@@ -401,6 +428,14 @@
             AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal));
         DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle,
                                                           acquireMutexKey, isSwapChainTexture));
+
+        // Importing a multi-planar format must be initialized. This is required because
+        // a shared multi-planar format cannot be initialized by Dawn.
+        if (!descriptor->isInitialized && dawnTexture->GetFormat().IsMultiPlanar()) {
+            return DAWN_VALIDATION_ERROR(
+                "Cannot create a multi-planar formatted texture without being initialized");
+        }
+
         dawnTexture->SetIsSubresourceContentInitialized(descriptor->isInitialized,
                                                         dawnTexture->GetAllSubresources());
         return std::move(dawnTexture);
@@ -431,6 +466,13 @@
 
         DAWN_TRY(ValidateD3D12TextureCanBeWrapped(d3d12Resource.Get(), descriptor));
 
+        // Shared handle is assumed to support resource sharing capability. The resource
+        // shared capability tier must agree to share resources between D3D devices.
+        if (GetFormat().IsMultiPlanar()) {
+            DAWN_TRY(ValidateD3D12VideoTextureCanBeShared(ToBackend(GetDevice()),
+                                                          D3D12TextureFormat(descriptor->format)));
+        }
+
         ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex;
         DAWN_TRY_ASSIGN(dxgiKeyedMutex,
                         dawnDevice->CreateKeyedMutexForTexture(d3d12Resource.Get()));
@@ -1039,6 +1081,12 @@
                             // sampled.
                             mSrvDesc.Format = DXGI_FORMAT_UNKNOWN;
                             break;
+
+                        // Depth formats cannot use plane aspects.
+                        case wgpu::TextureAspect::Plane0Only:
+                        case wgpu::TextureAspect::Plane1Only:
+                            UNREACHABLE();
+                            break;
                     }
                     break;
                 default:
@@ -1047,6 +1095,12 @@
             }
         }
 
+        // Per plane view formats must have the plane slice number be the index of the plane in the
+        // array of textures.
+        if (texture->GetFormat().IsMultiPlanar()) {
+            planeSlice = GetAspectIndex(ConvertViewAspect(GetFormat(), descriptor->aspect));
+        }
+
         // Currently we always use D3D12_TEX2D_ARRAY_SRV because we cannot specify base array layer
         // and layer count in D3D12_TEX2D_SRV. For 2D texture views, we treat them as 1-layer 2D
         // array textures.
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index 6edc768..c1d7cfe 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -307,6 +307,8 @@
                                         case Aspect::None:
                                         case Aspect::Color:
                                         case Aspect::CombinedDepthStencil:
+                                        case Aspect::Plane0:
+                                        case Aspect::Plane1:
                                             UNREACHABLE();
                                         case Aspect::Depth:
                                             gl.TexParameteri(target, GL_DEPTH_STENCIL_TEXTURE_MODE,
@@ -483,6 +485,8 @@
                             break;
                         case Aspect::CombinedDepthStencil:
                         case Aspect::None:
+                        case Aspect::Plane0:
+                        case Aspect::Plane1:
                             UNREACHABLE();
                     }
                     if (srcTexture->GetArrayLayers() == 1) {
@@ -795,6 +799,8 @@
 
                         case Aspect::CombinedDepthStencil:
                         case Aspect::None:
+                        case Aspect::Plane0:
+                        case Aspect::Plane1:
                             UNREACHABLE();
                     }
 
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index a60efc3..bd80f39 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -338,7 +338,7 @@
                 return VK_FORMAT_BC7_UNORM_BLOCK;
             case wgpu::TextureFormat::BC7RGBAUnormSrgb:
                 return VK_FORMAT_BC7_SRGB_BLOCK;
-
+            case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
             case wgpu::TextureFormat::Undefined:
                 UNREACHABLE();
         }
@@ -757,6 +757,9 @@
             case wgpu::TextureAspect::StencilOnly:
                 ASSERT(GetFormat().aspects & Aspect::Stencil);
                 return VulkanAspectMask(Aspect::Stencil);
+            case wgpu::TextureAspect::Plane0Only:
+            case wgpu::TextureAspect::Plane1Only:
+                UNREACHABLE();
         }
     }
 
diff --git a/src/dawn_native/vulkan/UtilsVulkan.cpp b/src/dawn_native/vulkan/UtilsVulkan.cpp
index b379e25..7e58319 100644
--- a/src/dawn_native/vulkan/UtilsVulkan.cpp
+++ b/src/dawn_native/vulkan/UtilsVulkan.cpp
@@ -65,6 +65,8 @@
                     flags |= VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
                     break;
 
+                case Aspect::Plane0:
+                case Aspect::Plane1:
                 case Aspect::None:
                     UNREACHABLE();
             }