Fix Norm16 formats being available without the extension.

This changes SharedTextureMemoryTests to request the extension, but also
skip over creating the backing SharedTextureMemory if the format isn't
supported.

Bug: dawn:1982
Change-Id: I1d1955b2f40bc62dd99f6c9d738e8c6f4efc943d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/161685
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/native/Format.cpp b/src/dawn/native/Format.cpp
index 3718433..5ec9cc4 100644
--- a/src/dawn/native/Format.cpp
+++ b/src/dawn/native/Format.cpp
@@ -480,13 +480,13 @@
     AddColorFormat(wgpu::TextureFormat::RGBA32Float, Cap::Renderable | Cap::StorageW, ByteSize(16), sampleTypeFor32BitFloatFormats, ComponentCount(4), RenderTargetPixelByteCost(16), RenderTargetComponentAlignment(4));
 
     // Norm16 color formats
-    auto norm16Capabilities = device->HasFeature(Feature::Norm16TextureFormats) ? Cap::Renderable | Cap::Multisample | Cap::Resolve : Cap::None;
-    AddColorFormat(wgpu::TextureFormat::R16Unorm, norm16Capabilities, ByteSize(2), kAnyFloat, ComponentCount(1), RenderTargetPixelByteCost(2), RenderTargetComponentAlignment(2));
-    AddColorFormat(wgpu::TextureFormat::RG16Unorm, norm16Capabilities, ByteSize(4), kAnyFloat, ComponentCount(2), RenderTargetPixelByteCost(4), RenderTargetComponentAlignment(2));
-    AddColorFormat(wgpu::TextureFormat::RGBA16Unorm, norm16Capabilities, ByteSize(8), kAnyFloat, ComponentCount(4), RenderTargetPixelByteCost(8), RenderTargetComponentAlignment(2));
-    AddColorFormat(wgpu::TextureFormat::R16Snorm, norm16Capabilities, ByteSize(2), kAnyFloat, ComponentCount(1), RenderTargetPixelByteCost(2), RenderTargetComponentAlignment(2));
-    AddColorFormat(wgpu::TextureFormat::RG16Snorm, norm16Capabilities, ByteSize(4), kAnyFloat, ComponentCount(2), RenderTargetPixelByteCost(4), RenderTargetComponentAlignment(2));
-    AddColorFormat(wgpu::TextureFormat::RGBA16Snorm, norm16Capabilities, ByteSize(8), kAnyFloat, ComponentCount(4), RenderTargetPixelByteCost(8), RenderTargetComponentAlignment(2));
+    auto norm16Supported = device->HasFeature(Feature::Norm16TextureFormats) ? Format::supported : RequiresFeature{wgpu::FeatureName::Norm16TextureFormats};
+    AddConditionalColorFormat(wgpu::TextureFormat::R16Unorm, norm16Supported, Cap::Renderable | Cap::Multisample | Cap::Resolve, ByteSize(2), kAnyFloat, ComponentCount(1), RenderTargetPixelByteCost(2), RenderTargetComponentAlignment(2));
+    AddConditionalColorFormat(wgpu::TextureFormat::RG16Unorm, norm16Supported, Cap::Renderable | Cap::Multisample | Cap::Resolve, ByteSize(4), kAnyFloat, ComponentCount(2), RenderTargetPixelByteCost(4), RenderTargetComponentAlignment(2));
+    AddConditionalColorFormat(wgpu::TextureFormat::RGBA16Unorm, norm16Supported, Cap::Renderable | Cap::Multisample | Cap::Resolve, ByteSize(8), kAnyFloat, ComponentCount(4), RenderTargetPixelByteCost(8), RenderTargetComponentAlignment(2));
+    AddConditionalColorFormat(wgpu::TextureFormat::R16Snorm, norm16Supported, Cap::Renderable | Cap::Multisample | Cap::Resolve, ByteSize(2), kAnyFloat, ComponentCount(1), RenderTargetPixelByteCost(2), RenderTargetComponentAlignment(2));
+    AddConditionalColorFormat(wgpu::TextureFormat::RG16Snorm, norm16Supported, Cap::Renderable | Cap::Multisample | Cap::Resolve, ByteSize(4), kAnyFloat, ComponentCount(2), RenderTargetPixelByteCost(4), RenderTargetComponentAlignment(2));
+    AddConditionalColorFormat(wgpu::TextureFormat::RGBA16Snorm, norm16Supported, Cap::Renderable | Cap::Multisample | Cap::Resolve, ByteSize(8), kAnyFloat, ComponentCount(4), RenderTargetPixelByteCost(8), RenderTargetComponentAlignment(2));
 
     // Depth-stencil formats
     AddStencilFormat(wgpu::TextureFormat::Stencil8, Format::supported);
diff --git a/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp b/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp
index 5d6dd99..77dbf8e 100644
--- a/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp
+++ b/src/dawn/tests/end2end/SharedTextureMemoryTests.cpp
@@ -40,7 +40,7 @@
 }
 
 std::vector<wgpu::FeatureName> SharedTextureMemoryTests::GetRequiredFeatures() {
-    auto features = GetParam().mBackend->RequiredFeatures();
+    auto features = GetParam().mBackend->RequiredFeatures(GetAdapter().Get());
     if (!SupportsFeatures(features)) {
         return {};
     }
@@ -49,6 +49,7 @@
         wgpu::FeatureName::MultiPlanarFormatExtendedUsages,
         wgpu::FeatureName::MultiPlanarRenderTargets,
         wgpu::FeatureName::TransientAttachments,
+        wgpu::FeatureName::Norm16TextureFormats,
     };
     for (auto feature : kOptionalFeatures) {
         if (SupportsFeatures({feature})) {
@@ -62,7 +63,8 @@
 void SharedTextureMemoryTests::SetUp() {
     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
     DawnTestWithParams<SharedTextureMemoryTestParams>::SetUp();
-    DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures(GetParam().mBackend->RequiredFeatures()));
+    DAWN_TEST_UNSUPPORTED_IF(
+        !SupportsFeatures(GetParam().mBackend->RequiredFeatures(GetAdapter().Get())));
 }
 
 std::vector<wgpu::SharedTextureMemory> SharedTextureMemoryTestBackend::CreateSharedTextureMemories(
@@ -305,6 +307,7 @@
             EXPECT_TEXTURE_EQ(deviceObj, &utils::RGBA8::kYellow, colorTarget, br, {1, 1});
             break;
         case wgpu::TextureFormat::RG16Float:
+        case wgpu::TextureFormat::RG16Unorm:
         case wgpu::TextureFormat::RG8Unorm:
             EXPECT_TEXTURE_EQ(deviceObj, &utils::RGBA8::kGreen, colorTarget, tl, {1, 1});
             EXPECT_TEXTURE_EQ(deviceObj, &utils::RGBA8::kRed, colorTarget, bl, {1, 1});
@@ -312,6 +315,7 @@
             EXPECT_TEXTURE_EQ(deviceObj, &utils::RGBA8::kYellow, colorTarget, br, {1, 1});
             break;
         case wgpu::TextureFormat::R16Float:
+        case wgpu::TextureFormat::R16Unorm:
         case wgpu::TextureFormat::R8Unorm:
             EXPECT_TEXTURE_EQ(deviceObj, &utils::RGBA8::kBlack, colorTarget, tl, {1, 1});
             EXPECT_TEXTURE_EQ(deviceObj, &utils::RGBA8::kRed, colorTarget, bl, {1, 1});
diff --git a/src/dawn/tests/end2end/SharedTextureMemoryTests.h b/src/dawn/tests/end2end/SharedTextureMemoryTests.h
index 340e75e..c46d628 100644
--- a/src/dawn/tests/end2end/SharedTextureMemoryTests.h
+++ b/src/dawn/tests/end2end/SharedTextureMemoryTests.h
@@ -43,10 +43,10 @@
     virtual std::string Name() const = 0;
 
     // The required features for testing this backend.
-    virtual std::vector<wgpu::FeatureName> RequiredFeatures() const = 0;
+    virtual std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter& device) const = 0;
 
     // Create one basic shared texture memory. It should support most operations.
-    virtual wgpu::SharedTextureMemory CreateSharedTextureMemory(wgpu::Device& device) = 0;
+    virtual wgpu::SharedTextureMemory CreateSharedTextureMemory(const wgpu::Device& device) = 0;
 
     // Create a variety of valid SharedTextureMemory for testing, one on each device.
     // Backends should return all interesting types of shared texture memory here, including
diff --git a/src/dawn/tests/end2end/SharedTextureMemoryTests_apple.mm b/src/dawn/tests/end2end/SharedTextureMemoryTests_apple.mm
index 51caf7c..25c375e 100644
--- a/src/dawn/tests/end2end/SharedTextureMemoryTests_apple.mm
+++ b/src/dawn/tests/end2end/SharedTextureMemoryTests_apple.mm
@@ -53,15 +53,20 @@
 
     std::string Name() const override { return "IOSurface"; }
 
-    std::vector<wgpu::FeatureName> RequiredFeatures() const override {
-        return {wgpu::FeatureName::SharedTextureMemoryIOSurface,
-                wgpu::FeatureName::SharedFenceMTLSharedEvent,
-                wgpu::FeatureName::DawnMultiPlanarFormats,
-                wgpu::FeatureName::MultiPlanarFormatNv12a};
+    std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter& device) const override {
+        std::vector<wgpu::FeatureName> features = {wgpu::FeatureName::SharedTextureMemoryIOSurface,
+                                                   wgpu::FeatureName::SharedFenceMTLSharedEvent,
+                                                   wgpu::FeatureName::DawnMultiPlanarFormats};
+
+        if (device.HasFeature(wgpu::FeatureName::MultiPlanarFormatNv12a)) {
+            features.push_back(wgpu::FeatureName::MultiPlanarFormatNv12a);
+        }
+
+        return features;
     }
 
     // Create one basic shared texture memory. It should support most operations.
-    wgpu::SharedTextureMemory CreateSharedTextureMemory(wgpu::Device& device) override {
+    wgpu::SharedTextureMemory CreateSharedTextureMemory(const wgpu::Device& device) override {
         auto dict = AcquireCFRef(CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                                            &kCFTypeDictionaryKeyCallBacks,
                                                            &kCFTypeDictionaryValueCallBacks));
@@ -82,30 +87,39 @@
     std::vector<std::vector<wgpu::SharedTextureMemory>> CreatePerDeviceSharedTextureMemories(
         const std::vector<wgpu::Device>& devices) override {
         std::vector<std::vector<wgpu::SharedTextureMemory>> memories;
-        for (auto [format, bytesPerElement] : {
-                 std::make_pair(kCVPixelFormatType_64RGBAHalf, 8),
-                 std::make_pair(kCVPixelFormatType_TwoComponent16Half, 4),
-                 std::make_pair(kCVPixelFormatType_OneComponent16Half, 2),
-                 std::make_pair(kCVPixelFormatType_TwoComponent16, 4),
-                 std::make_pair(kCVPixelFormatType_OneComponent16, 2),
-                 std::make_pair(kCVPixelFormatType_ARGB2101010LEPacked, 4),
-                 std::make_pair(kCVPixelFormatType_32RGBA, 4),
-                 std::make_pair(kCVPixelFormatType_32BGRA, 4),
-                 std::make_pair(kCVPixelFormatType_TwoComponent8, 2),
-                 std::make_pair(kCVPixelFormatType_OneComponent8, 1),
-                 // Below bytes per element isn't correct.
-                 std::make_pair(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, 4),
-                 std::make_pair(kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar, 4),
-                 // TODO(dawn:551): Add R10X6BG10X6Biplanar420Unorm support.
-             }) {
+
+        struct IOSurfaceFormat {
+            uint32_t format;
+            uint32_t bytesPerElement;
+            wgpu::FeatureName requiredFeature = wgpu::FeatureName::Undefined;
+        };
+        const std::array<IOSurfaceFormat, 12> kFormats{{
+            {kCVPixelFormatType_64RGBAHalf, 8},
+            {kCVPixelFormatType_TwoComponent16Half, 4},
+            {kCVPixelFormatType_OneComponent16Half, 2},
+            {kCVPixelFormatType_TwoComponent16, 4, wgpu::FeatureName::Norm16TextureFormats},
+            {kCVPixelFormatType_OneComponent16, 2, wgpu::FeatureName::Norm16TextureFormats},
+            {kCVPixelFormatType_ARGB2101010LEPacked, 4},
+            {kCVPixelFormatType_32RGBA, 4},
+            {kCVPixelFormatType_32BGRA, 4},
+            {kCVPixelFormatType_TwoComponent8, 2},
+            {kCVPixelFormatType_OneComponent8, 1},
+            // Below bytes per element isn't correct.
+            {kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, 4},
+            {kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar, 4,
+             wgpu::FeatureName::MultiPlanarFormatNv12a},
+            // TODO(dawn:551): Add R10X6BG10X6Biplanar420Unorm support.
+        }};
+
+        for (auto f : kFormats) {
             for (uint32_t size : {4, 64}) {
                 auto dict = AcquireCFRef(CFDictionaryCreateMutable(
                     kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
                     &kCFTypeDictionaryValueCallBacks));
                 AddIntegerValue(dict.Get(), kIOSurfaceWidth, size);
                 AddIntegerValue(dict.Get(), kIOSurfaceHeight, size);
-                AddIntegerValue(dict.Get(), kIOSurfacePixelFormat, format);
-                AddIntegerValue(dict.Get(), kIOSurfaceBytesPerElement, bytesPerElement);
+                AddIntegerValue(dict.Get(), kIOSurfacePixelFormat, f.format);
+                AddIntegerValue(dict.Get(), kIOSurfaceBytesPerElement, f.bytesPerElement);
 
                 wgpu::SharedTextureMemoryIOSurfaceDescriptor ioSurfaceDesc;
                 ioSurfaceDesc.ioSurface = IOSurfaceCreate(dict.Get());
@@ -113,7 +127,7 @@
                 // Internally, the CV enums are defined as their fourcc values. Cast to that and use
                 // it as the label. The fourcc value is a four-character name that can be
                 // interpreted as a 32-bit integer enum ('ABGR', 'r011', etc.)
-                std::string label = std::string(reinterpret_cast<char*>(&format), 4) + " " +
+                std::string label = std::string(reinterpret_cast<char*>(&f.format), 4) + " " +
                                     std::to_string(size) + "x" + std::to_string(size);
                 wgpu::SharedTextureMemoryDescriptor desc;
                 desc.label = label.c_str();
@@ -121,9 +135,17 @@
 
                 std::vector<wgpu::SharedTextureMemory> perDeviceMemories;
                 for (auto& device : devices) {
+                    if (f.requiredFeature != wgpu::FeatureName::Undefined &&
+                        !device.HasFeature(f.requiredFeature)) {
+                        continue;
+                    }
+
                     perDeviceMemories.push_back(device.ImportSharedTextureMemory(&desc));
                 }
-                memories.push_back(std::move(perDeviceMemories));
+
+                if (!perDeviceMemories.empty()) {
+                    memories.push_back(std::move(perDeviceMemories));
+                }
             }
         }
 
diff --git a/src/dawn/tests/end2end/SharedTextureMemoryTests_win.cpp b/src/dawn/tests/end2end/SharedTextureMemoryTests_win.cpp
index 7c73eaf..051d53a 100644
--- a/src/dawn/tests/end2end/SharedTextureMemoryTests_win.cpp
+++ b/src/dawn/tests/end2end/SharedTextureMemoryTests_win.cpp
@@ -77,7 +77,7 @@
     bool UseSameDevice() const override { return mMode == Mode::D3D11Texture2D; }
     bool SupportsConcurrentRead() const override { return !mUseKeyedMutex; }
 
-    std::vector<wgpu::FeatureName> RequiredFeatures() const override {
+    std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter& adapter) const override {
         switch (mMode) {
             case Mode::D3D11Texture2D: {
                 return {wgpu::FeatureName::SharedTextureMemoryD3D11Texture2D,
@@ -121,7 +121,7 @@
     }
 
     // Create one basic shared texture memory. It should support most operations.
-    wgpu::SharedTextureMemory CreateSharedTextureMemory(wgpu::Device& device) override {
+    wgpu::SharedTextureMemory CreateSharedTextureMemory(const wgpu::Device& device) override {
         ComPtr<ID3D11Device> d3d11Device = MakeD3D11Device(device);
 
         // Create a DX11 texture with data then wrap it in a shared handle.
@@ -200,18 +200,28 @@
             }
         }
 
-        std::vector<DXGI_FORMAT> formats = {
-            DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16_FLOAT,
-            DXGI_FORMAT_R16_FLOAT,          DXGI_FORMAT_R8G8B8A8_UNORM,
-            DXGI_FORMAT_B8G8R8A8_UNORM,     DXGI_FORMAT_R10G10B10A2_UNORM,
-            DXGI_FORMAT_R8G8_UNORM,         DXGI_FORMAT_R8_UNORM,
+        struct D3DFormat {
+            DXGI_FORMAT format;
+            wgpu::FeatureName requiredFeature = wgpu::FeatureName::Undefined;
         };
+        std::vector<D3DFormat> formats = {{
+            {DXGI_FORMAT_R16G16B16A16_FLOAT},
+            {DXGI_FORMAT_R16G16_FLOAT},
+            {DXGI_FORMAT_R16_FLOAT},
+            {DXGI_FORMAT_R8G8B8A8_UNORM},
+            {DXGI_FORMAT_B8G8R8A8_UNORM},
+            {DXGI_FORMAT_R10G10B10A2_UNORM},
+            {DXGI_FORMAT_R16G16_UNORM, wgpu::FeatureName::Norm16TextureFormats},
+            {DXGI_FORMAT_R16_UNORM, wgpu::FeatureName::Norm16TextureFormats},
+            {DXGI_FORMAT_R8G8_UNORM},
+            {DXGI_FORMAT_R8_UNORM},
+        }};
 
         if (supportsNV12Sharing) {
-            formats.push_back(DXGI_FORMAT_NV12);
+            formats.push_back({DXGI_FORMAT_NV12});
         }
 
-        for (DXGI_FORMAT format : formats) {
+        for (auto f : formats) {
             for (uint32_t size : {4, 64}) {
                 // Create a DX11 texture with data then wrap it in a shared handle.
                 D3D11_TEXTURE2D_DESC d3dDescriptor;
@@ -219,7 +229,7 @@
                 d3dDescriptor.Height = size;
                 d3dDescriptor.MipLevels = 1;
                 d3dDescriptor.ArraySize = 1;
-                d3dDescriptor.Format = format;
+                d3dDescriptor.Format = f.format;
                 d3dDescriptor.SampleDesc.Count = 1;
                 d3dDescriptor.SampleDesc.Quality = 0;
                 d3dDescriptor.Usage = D3D11_USAGE_DEFAULT;
@@ -233,6 +243,7 @@
                 ComPtr<ID3D11Texture2D> d3d11Texture;
                 HRESULT hr = d3d11Device->CreateTexture2D(&d3dDescriptor, nullptr, &d3d11Texture);
 
+                std::vector<wgpu::SharedTextureMemory> perDeviceMemories;
                 switch (mMode) {
                     case Mode::D3D11Texture2D: {
                         native::d3d11::SharedTextureMemoryD3D11Texture2DDescriptor texture2DDesc;
@@ -241,11 +252,14 @@
                         wgpu::SharedTextureMemoryDescriptor desc;
                         desc.nextInChain = &texture2DDesc;
 
-                        std::vector<wgpu::SharedTextureMemory> perDeviceMemories;
                         for (auto& device : devices) {
+                            if (f.requiredFeature != wgpu::FeatureName::Undefined &&
+                                !device.HasFeature(f.requiredFeature)) {
+                                continue;
+                            }
+
                             perDeviceMemories.push_back(device.ImportSharedTextureMemory(&desc));
                         }
-                        memories.push_back(std::move(perDeviceMemories));
                         break;
                     }
                     case Mode::DXGISharedHandle: {
@@ -262,22 +276,28 @@
                         wgpu::SharedTextureMemoryDXGISharedHandleDescriptor sharedHandleDesc;
                         sharedHandleDesc.handle = sharedHandle;
 
-                        std::string label = LabelName(format, size);
+                        std::string label = LabelName(f.format, size);
 
                         wgpu::SharedTextureMemoryDescriptor desc;
                         desc.nextInChain = &sharedHandleDesc;
                         desc.label = label.c_str();
 
-                        std::vector<wgpu::SharedTextureMemory> perDeviceMemories;
                         for (auto& device : devices) {
+                            if (f.requiredFeature != wgpu::FeatureName::Undefined &&
+                                !device.HasFeature(f.requiredFeature)) {
+                                continue;
+                            }
+
                             perDeviceMemories.push_back(device.ImportSharedTextureMemory(&desc));
                         }
-                        memories.push_back(std::move(perDeviceMemories));
 
                         ::CloseHandle(sharedHandle);
                         break;
                     }
                 }
+                if (!perDeviceMemories.empty()) {
+                    memories.push_back(std::move(perDeviceMemories));
+                }
             }
         }
         return memories;
diff --git a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
index 8d8827a..c16cf8f 100644
--- a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
@@ -971,6 +971,32 @@
     device.CreateTexture(&descriptor);
 }
 
+// Test that the Norm16 formats are not available even for just TextureBinding when the optional
+// feature is not specified.
+TEST_F(TextureValidationTest, Norm16NotAvailableWithoutExtension) {
+    wgpu::TextureDescriptor descriptor;
+    descriptor.size = {1, 1, 1};
+    descriptor.usage = wgpu::TextureUsage::TextureBinding;
+
+    descriptor.format = wgpu::TextureFormat::R16Unorm;
+    ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
+
+    descriptor.format = wgpu::TextureFormat::RG16Unorm;
+    ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
+
+    descriptor.format = wgpu::TextureFormat::RGBA16Unorm;
+    ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
+
+    descriptor.format = wgpu::TextureFormat::R16Snorm;
+    ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
+
+    descriptor.format = wgpu::TextureFormat::RG16Snorm;
+    ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
+
+    descriptor.format = wgpu::TextureFormat::RGBA16Snorm;
+    ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
+}
+
 static void CheckTextureMatchesDescriptor(const wgpu::Texture& tex,
                                           const wgpu::TextureDescriptor& desc) {
     EXPECT_EQ(desc.size.width, tex.GetWidth());