Add Norm16 texture formats

This checks the availability of the formats on each backend to
properly enable the Feature::Norm16TextureFormats.
The new formats include:
    R16Unorm
    RG16Unorm
    RGBA16Unorm
    R16Snorm
    RG16Snorm
    RGBA16Snorm

Bug: dawn:1982

Change-Id: I09600911adea3335a6a691000ab74f08a8fba5e7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/147220
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jie A Chen <jie.a.chen@intel.com>
diff --git a/dawn.json b/dawn.json
index 94152e3..a40a306 100644
--- a/dawn.json
+++ b/dawn.json
@@ -1814,6 +1814,7 @@
             {"value": 1016, "name": "chromium experimental read write storage texture", "tags": ["dawn"]},
             {"value": 1017, "name": "pixel local storage coherent", "tags": ["dawn"]},
             {"value": 1018, "name": "pixel local storage non coherent", "tags": ["dawn"]},
+            {"value": 1019, "name": "norm16 texture formats", "tags": ["dawn"]},
 
             {"value": 1100, "name": "shared texture memory vk dedicated allocation", "tags": ["dawn", "native"]},
             {"value": 1101, "name": "shared texture memory a hardware buffer", "tags": ["dawn", "native"]},
@@ -3292,7 +3293,14 @@
             {"value": 93, "name": "ASTC 12x12 unorm",       "jsrepr": "'astc-12x12-unorm'"},
             {"value": 94, "name": "ASTC 12x12 unorm srgb",  "jsrepr": "'astc-12x12-unorm-srgb'"},
 
-            {"value": 95, "name": "R8 BG8 Biplanar 420 unorm", "tags": ["dawn"]}
+            {"value": 95, "name": "R16 unorm", "tags": ["dawn"]},
+            {"value": 96, "name": "RG16 unorm", "tags": ["dawn"]},
+            {"value": 97, "name": "RGBA16 unorm", "tags": ["dawn"]},
+            {"value": 98, "name": "R16 snorm", "tags": ["dawn"]},
+            {"value": 99, "name": "RG16 snorm", "tags": ["dawn"]},
+            {"value": 100, "name": "RGBA16 snorm", "tags": ["dawn"]},
+
+            {"value": 101, "name": "R8 BG8 Biplanar 420 unorm", "tags": ["dawn"]}
         ]
     },
     "texture usage": {
diff --git a/src/dawn/native/Features.cpp b/src/dawn/native/Features.cpp
index b5aa0c4..7c61f95 100644
--- a/src/dawn/native/Features.cpp
+++ b/src/dawn/native/Features.cpp
@@ -174,6 +174,9 @@
       "overlapping fragments from the same draw cannot be made data race free).",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1704",
       FeatureInfo::FeatureState::Experimental}},
+    {Feature::Norm16TextureFormats,
+     {"norm16-texture-formats", "Supports R/RG/RGBA16 norm texture formats",
+      "https://bugs.chromium.org/p/dawn/issues/detail?id=1982", FeatureInfo::FeatureState::Stable}},
     {Feature::SharedTextureMemoryVkDedicatedAllocation,
      {"shared-texture-memory-vk-dedicated-allocation",
       "Support specifying whether a Vulkan allocation for shared texture memory is a dedicated "
@@ -309,6 +312,8 @@
             return Feature::PixelLocalStorageCoherent;
         case wgpu::FeatureName::PixelLocalStorageNonCoherent:
             return Feature::PixelLocalStorageNonCoherent;
+        case wgpu::FeatureName::Norm16TextureFormats:
+            return Feature::Norm16TextureFormats;
         case wgpu::FeatureName::SharedTextureMemoryVkDedicatedAllocation:
             return Feature::SharedTextureMemoryVkDedicatedAllocation;
         case wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer:
@@ -371,6 +376,8 @@
             return wgpu::FeatureName::DawnInternalUsages;
         case Feature::MultiPlanarFormats:
             return wgpu::FeatureName::DawnMultiPlanarFormats;
+        case Feature::Norm16TextureFormats:
+            return wgpu::FeatureName::Norm16TextureFormats;
         case Feature::DawnNative:
             return wgpu::FeatureName::DawnNative;
         case Feature::ChromiumExperimentalDp4a:
diff --git a/src/dawn/native/Features.h b/src/dawn/native/Features.h
index a93cc50..9014738 100644
--- a/src/dawn/native/Features.h
+++ b/src/dawn/native/Features.h
@@ -58,6 +58,7 @@
     ANGLETextureSharing,
     PixelLocalStorageCoherent,
     PixelLocalStorageNonCoherent,
+    Norm16TextureFormats,
 
     SharedTextureMemoryVkDedicatedAllocation,
     SharedTextureMemoryAHardwareBuffer,
diff --git a/src/dawn/native/Format.cpp b/src/dawn/native/Format.cpp
index 78f4d6e..e1ae867 100644
--- a/src/dawn/native/Format.cpp
+++ b/src/dawn/native/Format.cpp
@@ -456,6 +456,15 @@
         AddColorFormat(wgpu::TextureFormat::RGBA32Sint, Cap::Renderable | Cap::StorageW, ByteSize(16), SampleTypeBit::Sint, ComponentCount(4), RenderTargetPixelByteCost(16), RenderTargetComponentAlignment(4));
         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));
+
         // Depth-stencil formats
         AddStencilFormat(wgpu::TextureFormat::Stencil8, Format::supported);
         AddDepthFormat(wgpu::TextureFormat::Depth16Unorm, 2, Format::supported);
diff --git a/src/dawn/native/Format.h b/src/dawn/native/Format.h
index 8a82fa1..96ff348 100644
--- a/src/dawn/native/Format.h
+++ b/src/dawn/native/Format.h
@@ -93,7 +93,7 @@
 
 // The number of formats Dawn knows about. Asserts in BuildFormatTable ensure that this is the
 // exact number of known format.
-static constexpr uint32_t kKnownFormatCount = 95;
+static constexpr uint32_t kKnownFormatCount = 101;
 
 using FormatIndex = TypedInteger<struct FormatIndexT, uint32_t>;
 
diff --git a/src/dawn/native/d3d/UtilsD3D.cpp b/src/dawn/native/d3d/UtilsD3D.cpp
index 4af1498..b8cb9d1 100644
--- a/src/dawn/native/d3d/UtilsD3D.cpp
+++ b/src/dawn/native/d3d/UtilsD3D.cpp
@@ -92,6 +92,8 @@
         case wgpu::TextureFormat::R8Sint:
             return DXGI_FORMAT_R8_TYPELESS;
 
+        case wgpu::TextureFormat::R16Unorm:
+        case wgpu::TextureFormat::R16Snorm:
         case wgpu::TextureFormat::R16Uint:
         case wgpu::TextureFormat::R16Sint:
         case wgpu::TextureFormat::R16Float:
@@ -109,6 +111,8 @@
         case wgpu::TextureFormat::R32Float:
             return DXGI_FORMAT_R32_TYPELESS;
 
+        case wgpu::TextureFormat::RG16Unorm:
+        case wgpu::TextureFormat::RG16Snorm:
         case wgpu::TextureFormat::RG16Uint:
         case wgpu::TextureFormat::RG16Sint:
         case wgpu::TextureFormat::RG16Float:
@@ -138,6 +142,8 @@
         case wgpu::TextureFormat::RG32Float:
             return DXGI_FORMAT_R32G32_TYPELESS;
 
+        case wgpu::TextureFormat::RGBA16Unorm:
+        case wgpu::TextureFormat::RGBA16Snorm:
         case wgpu::TextureFormat::RGBA16Uint:
         case wgpu::TextureFormat::RGBA16Sint:
         case wgpu::TextureFormat::RGBA16Float:
@@ -245,6 +251,10 @@
         case wgpu::TextureFormat::R8Sint:
             return DXGI_FORMAT_R8_SINT;
 
+        case wgpu::TextureFormat::R16Unorm:
+            return DXGI_FORMAT_R16_UNORM;
+        case wgpu::TextureFormat::R16Snorm:
+            return DXGI_FORMAT_R16_SNORM;
         case wgpu::TextureFormat::R16Uint:
             return DXGI_FORMAT_R16_UINT;
         case wgpu::TextureFormat::R16Sint:
@@ -266,6 +276,11 @@
             return DXGI_FORMAT_R32_SINT;
         case wgpu::TextureFormat::R32Float:
             return DXGI_FORMAT_R32_FLOAT;
+
+        case wgpu::TextureFormat::RG16Unorm:
+            return DXGI_FORMAT_R16G16_UNORM;
+        case wgpu::TextureFormat::RG16Snorm:
+            return DXGI_FORMAT_R16G16_SNORM;
         case wgpu::TextureFormat::RG16Uint:
             return DXGI_FORMAT_R16G16_UINT;
         case wgpu::TextureFormat::RG16Sint:
@@ -299,6 +314,10 @@
             return DXGI_FORMAT_R32G32_SINT;
         case wgpu::TextureFormat::RG32Float:
             return DXGI_FORMAT_R32G32_FLOAT;
+        case wgpu::TextureFormat::RGBA16Unorm:
+            return DXGI_FORMAT_R16G16B16A16_UNORM;
+        case wgpu::TextureFormat::RGBA16Snorm:
+            return DXGI_FORMAT_R16G16B16A16_SNORM;
         case wgpu::TextureFormat::RGBA16Uint:
             return DXGI_FORMAT_R16G16B16A16_UINT;
         case wgpu::TextureFormat::RGBA16Sint:
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
index b453fb0..b1d9437 100644
--- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
@@ -140,6 +140,7 @@
     EnableFeature(Feature::D3D11MultithreadProtected);
     EnableFeature(Feature::MSAARenderToSingleSampled);
     EnableFeature(Feature::DualSourceBlending);
+    EnableFeature(Feature::Norm16TextureFormats);
 
     // To import multi planar textures, we need to at least tier 2 support.
     if (mDeviceInfo.supportsSharedResourceCapabilityTier2) {
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
index e4c8244..dc916ba 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -126,6 +126,7 @@
     EnableFeature(Feature::SurfaceCapabilities);
     EnableFeature(Feature::Float32Filterable);
     EnableFeature(Feature::DualSourceBlending);
+    EnableFeature(Feature::Norm16TextureFormats);
 
     if (AreTimestampQueriesSupported()) {
         EnableFeature(Feature::TimestampQuery);
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index e33914e..8adf17d 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -552,6 +552,8 @@
         if (@available(macOS 10.14, iOS 12.0, *)) {
             EnableFeature(Feature::SharedFenceMTLSharedEvent);
         }
+
+        EnableFeature(Feature::Norm16TextureFormats);
     }
 
     void InitializeVendorArchitectureImpl() override {
diff --git a/src/dawn/native/metal/TextureMTL.mm b/src/dawn/native/metal/TextureMTL.mm
index 369fefa..f4e7d3f 100644
--- a/src/dawn/native/metal/TextureMTL.mm
+++ b/src/dawn/native/metal/TextureMTL.mm
@@ -254,6 +254,10 @@
         case wgpu::TextureFormat::R8Sint:
             return MTLPixelFormatR8Sint;
 
+        case wgpu::TextureFormat::R16Unorm:
+            return MTLPixelFormatR16Unorm;
+        case wgpu::TextureFormat::R16Snorm:
+            return MTLPixelFormatR16Snorm;
         case wgpu::TextureFormat::R16Uint:
             return MTLPixelFormatR16Uint;
         case wgpu::TextureFormat::R16Sint:
@@ -275,6 +279,10 @@
             return MTLPixelFormatR32Sint;
         case wgpu::TextureFormat::R32Float:
             return MTLPixelFormatR32Float;
+        case wgpu::TextureFormat::RG16Unorm:
+            return MTLPixelFormatRG16Unorm;
+        case wgpu::TextureFormat::RG16Snorm:
+            return MTLPixelFormatRG16Snorm;
         case wgpu::TextureFormat::RG16Uint:
             return MTLPixelFormatRG16Uint;
         case wgpu::TextureFormat::RG16Sint:
@@ -308,6 +316,10 @@
             return MTLPixelFormatRG32Sint;
         case wgpu::TextureFormat::RG32Float:
             return MTLPixelFormatRG32Float;
+        case wgpu::TextureFormat::RGBA16Unorm:
+            return MTLPixelFormatRGBA16Unorm;
+        case wgpu::TextureFormat::RGBA16Snorm:
+            return MTLPixelFormatRGBA16Snorm;
         case wgpu::TextureFormat::RGBA16Uint:
             return MTLPixelFormatRGBA16Uint;
         case wgpu::TextureFormat::RGBA16Sint:
diff --git a/src/dawn/native/opengl/GLFormat.cpp b/src/dawn/native/opengl/GLFormat.cpp
index 529cc35..3c7edf6 100644
--- a/src/dawn/native/opengl/GLFormat.cpp
+++ b/src/dawn/native/opengl/GLFormat.cpp
@@ -50,6 +50,8 @@
         AddFormat(wgpu::TextureFormat::R8Sint, GL_R8I, GL_RED_INTEGER, GL_BYTE, Type::Int);
 
         // 2 bytes color formats
+        AddFormat(wgpu::TextureFormat::R16Unorm, GL_R16, GL_RED, GL_UNSIGNED_SHORT, Type::Float);
+        AddFormat(wgpu::TextureFormat::R16Snorm, GL_R16_SNORM, GL_RED, GL_SHORT, Type::Float);
         AddFormat(wgpu::TextureFormat::R16Uint, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, Type::Uint);
         AddFormat(wgpu::TextureFormat::R16Sint, GL_R16I, GL_RED_INTEGER, GL_SHORT, Type::Int);
         AddFormat(wgpu::TextureFormat::R16Float, GL_R16F, GL_RED, GL_HALF_FLOAT, Type::Float);
@@ -62,6 +64,8 @@
         AddFormat(wgpu::TextureFormat::R32Uint, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, Type::Uint);
         AddFormat(wgpu::TextureFormat::R32Sint, GL_R32I, GL_RED_INTEGER, GL_INT, Type::Int);
         AddFormat(wgpu::TextureFormat::R32Float, GL_R32F, GL_RED, GL_FLOAT, Type::Float);
+        AddFormat(wgpu::TextureFormat::RG16Unorm, GL_RG16, GL_RG, GL_UNSIGNED_SHORT, Type::Float);
+        AddFormat(wgpu::TextureFormat::RG16Snorm, GL_RG16_SNORM, GL_RG, GL_SHORT, Type::Float);
         AddFormat(wgpu::TextureFormat::RG16Uint, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, Type::Uint);
         AddFormat(wgpu::TextureFormat::RG16Sint, GL_RG16I, GL_RG_INTEGER, GL_SHORT, Type::Int);
         AddFormat(wgpu::TextureFormat::RG16Float, GL_RG16F, GL_RG, GL_HALF_FLOAT, Type::Float);
@@ -80,6 +84,8 @@
         AddFormat(wgpu::TextureFormat::RG32Uint, GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, Type::Uint);
         AddFormat(wgpu::TextureFormat::RG32Sint, GL_RG32I, GL_RG_INTEGER, GL_INT, Type::Int);
         AddFormat(wgpu::TextureFormat::RG32Float, GL_RG32F, GL_RG, GL_FLOAT, Type::Float);
+        AddFormat(wgpu::TextureFormat::RGBA16Unorm, GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, Type::Float);
+        AddFormat(wgpu::TextureFormat::RGBA16Snorm, GL_RGBA16_SNORM, GL_RGBA, GL_SHORT, Type::Float);
         AddFormat(wgpu::TextureFormat::RGBA16Uint, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, Type::Uint);
         AddFormat(wgpu::TextureFormat::RGBA16Sint, GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT, Type::Int);
         AddFormat(wgpu::TextureFormat::RGBA16Float, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, Type::Float);
diff --git a/src/dawn/native/opengl/PhysicalDeviceGL.cpp b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
index 5ef5e48..483d3a5 100644
--- a/src/dawn/native/opengl/PhysicalDeviceGL.cpp
+++ b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
@@ -218,6 +218,11 @@
         mFunctions.IsAtLeastGL(3, 3)) {
         EnableFeature(Feature::DualSourceBlending);
     }
+
+    // Norm16TextureFormats
+    if (mFunctions.IsGLExtensionSupported("GL_EXT_texture_norm16")) {
+        EnableFeature(Feature::Norm16TextureFormats);
+    }
 }
 
 namespace {
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
index 6e41986..651636e 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -260,6 +260,23 @@
         EnableFeature(Feature::BGRA8UnormStorage);
     }
 
+    bool norm16TextureFormatsSupported = true;
+    for (const auto& norm16Format :
+         {VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16B16A16_UNORM,
+          VK_FORMAT_R16_SNORM, VK_FORMAT_R16G16_SNORM, VK_FORMAT_R16G16B16A16_SNORM}) {
+        VkFormatProperties norm16Properties;
+        mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
+            mVkPhysicalDevice, norm16Format, &norm16Properties);
+        norm16TextureFormatsSupported &= IsSubset(
+            static_cast<VkFormatFeatureFlags>(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
+                                              VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
+                                              VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT),
+            norm16Properties.optimalTilingFeatures);
+    }
+    if (norm16TextureFormatsSupported) {
+        EnableFeature(Feature::Norm16TextureFormats);
+    }
+
     // 32 bit float channel formats.
     VkFormatProperties r32Properties;
     VkFormatProperties rg32Properties;
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index 9af6681..f9a36b4 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -254,6 +254,10 @@
         case wgpu::TextureFormat::R8Sint:
             return VK_FORMAT_R8_SINT;
 
+        case wgpu::TextureFormat::R16Unorm:
+            return VK_FORMAT_R16_UNORM;
+        case wgpu::TextureFormat::R16Snorm:
+            return VK_FORMAT_R16_SNORM;
         case wgpu::TextureFormat::R16Uint:
             return VK_FORMAT_R16_UINT;
         case wgpu::TextureFormat::R16Sint:
@@ -275,6 +279,10 @@
             return VK_FORMAT_R32_SINT;
         case wgpu::TextureFormat::R32Float:
             return VK_FORMAT_R32_SFLOAT;
+        case wgpu::TextureFormat::RG16Unorm:
+            return VK_FORMAT_R16G16_UNORM;
+        case wgpu::TextureFormat::RG16Snorm:
+            return VK_FORMAT_R16G16_SNORM;
         case wgpu::TextureFormat::RG16Uint:
             return VK_FORMAT_R16G16_UINT;
         case wgpu::TextureFormat::RG16Sint:
@@ -308,6 +316,10 @@
             return VK_FORMAT_R32G32_SINT;
         case wgpu::TextureFormat::RG32Float:
             return VK_FORMAT_R32G32_SFLOAT;
+        case wgpu::TextureFormat::RGBA16Unorm:
+            return VK_FORMAT_R16G16B16A16_UNORM;
+        case wgpu::TextureFormat::RGBA16Snorm:
+            return VK_FORMAT_R16G16B16A16_SNORM;
         case wgpu::TextureFormat::RGBA16Uint:
             return VK_FORMAT_R16G16B16A16_UINT;
         case wgpu::TextureFormat::RGBA16Sint:
diff --git a/src/dawn/tests/end2end/TextureFormatTests.cpp b/src/dawn/tests/end2end/TextureFormatTests.cpp
index 421ea3e..f247a15 100644
--- a/src/dawn/tests/end2end/TextureFormatTests.cpp
+++ b/src/dawn/tests/end2end/TextureFormatTests.cpp
@@ -440,6 +440,9 @@
         ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
         ASSERT(formatInfo.type == TextureComponentType::Float);
 
+        DAWN_TEST_UNSUPPORTED_IF((utils::IsNorm16TextureFormat(formatInfo.format)) &&
+                                 !IsNorm16TextureFormatsSupported());
+
         T maxValue = std::numeric_limits<T>::max();
         std::vector<T> textureData = {0, 1, maxValue, maxValue};
         std::vector<float> uncompressedData = {0.0f, 1.0f / maxValue, 1.0f, 1.0f};
@@ -454,6 +457,9 @@
         ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
         ASSERT(formatInfo.type == TextureComponentType::Float);
 
+        DAWN_TEST_UNSUPPORTED_IF((utils::IsNorm16TextureFormat(formatInfo.format)) &&
+                                 !IsNorm16TextureFormatsSupported());
+
         T maxValue = std::numeric_limits<T>::max();
         T minValue = std::numeric_limits<T>::min();
         std::vector<T> textureData = {0, 1, -1, maxValue, minValue, T(minValue + 1), 0, 0};
@@ -523,21 +529,25 @@
                               new ExpectFloat16(textureData));
     }
 
-    // For "rg11b10ufloat-renderable" feature test
     std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        std::vector<wgpu::FeatureName> requiredFeatures = {};
         if (SupportsFeatures({wgpu::FeatureName::RG11B10UfloatRenderable})) {
             mIsRG11B10UfloatRenderableSupported = true;
-            return {wgpu::FeatureName::RG11B10UfloatRenderable};
-        } else {
-            mIsRG11B10UfloatRenderableSupported = false;
-            return {};
+            requiredFeatures.push_back(wgpu::FeatureName::RG11B10UfloatRenderable);
         }
+        if (SupportsFeatures({wgpu::FeatureName::Norm16TextureFormats})) {
+            mIsNorm16TextureFormatsSupported = true;
+            requiredFeatures.push_back(wgpu::FeatureName::Norm16TextureFormats);
+        }
+        return requiredFeatures;
     }
 
     bool IsRG11B10UfloatRenderableSupported() { return mIsRG11B10UfloatRenderableSupported; }
+    bool IsNorm16TextureFormatsSupported() { return mIsNorm16TextureFormatsSupported; }
 
   private:
     bool mIsRG11B10UfloatRenderableSupported = false;
+    bool mIsNorm16TextureFormatsSupported = false;
 };
 
 // Test the R8Unorm format
@@ -550,6 +560,21 @@
     DoUnormTest<uint8_t>({wgpu::TextureFormat::RG8Unorm, 2, TextureComponentType::Float, 2});
 }
 
+// Test the R16Unorm format
+TEST_P(TextureFormatTest, R16Unorm) {
+    DoUnormTest<uint16_t>({wgpu::TextureFormat::R16Unorm, 2, TextureComponentType::Float, 1});
+}
+
+// Test the RG16Unorm format
+TEST_P(TextureFormatTest, RG16Unorm) {
+    DoUnormTest<uint16_t>({wgpu::TextureFormat::RG16Unorm, 4, TextureComponentType::Float, 2});
+}
+
+// Test the RGBA16Unorm format
+TEST_P(TextureFormatTest, RGBA16Unorm) {
+    DoUnormTest<uint16_t>({wgpu::TextureFormat::RGBA16Unorm, 8, TextureComponentType::Float, 4});
+}
+
 // Test the RGBA8Unorm format
 TEST_P(TextureFormatTest, RGBA8Unorm) {
     DoUnormTest<uint8_t>({wgpu::TextureFormat::RGBA8Unorm, 4, TextureComponentType::Float, 4});
@@ -585,6 +610,21 @@
     DoSnormTest<int8_t>({wgpu::TextureFormat::RGBA8Snorm, 4, TextureComponentType::Float, 4});
 }
 
+// Test the R16Snorm format
+TEST_P(TextureFormatTest, R16Snorm) {
+    DoSnormTest<int16_t>({wgpu::TextureFormat::R16Snorm, 2, TextureComponentType::Float, 1});
+}
+
+// Test the RG16Snorm format
+TEST_P(TextureFormatTest, RG16Snorm) {
+    DoSnormTest<int16_t>({wgpu::TextureFormat::RG16Snorm, 4, TextureComponentType::Float, 2});
+}
+
+// Test the RGBA16Snorm format
+TEST_P(TextureFormatTest, RGBA16Snorm) {
+    DoSnormTest<int16_t>({wgpu::TextureFormat::RGBA16Snorm, 8, TextureComponentType::Float, 4});
+}
+
 // Test the R8Uint format
 TEST_P(TextureFormatTest, R8Uint) {
     DoUintTest<uint8_t>({wgpu::TextureFormat::R8Uint, 1, TextureComponentType::Uint, 1});
diff --git a/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
index e15373e..7b2979b 100644
--- a/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
@@ -993,7 +993,7 @@
 // Tests the texture format of the resolve target must support being used as resolve target.
 TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetFormat) {
     for (wgpu::TextureFormat format : utils::kAllTextureFormats) {
-        if (!utils::TextureFormatSupportsMultisampling(format) ||
+        if (!utils::TextureFormatSupportsMultisampling(device, format) ||
             utils::IsDepthOrStencilFormat(format)) {
             continue;
         }
@@ -1006,7 +1006,7 @@
 
         utils::ComboRenderPassDescriptor renderPass({colorTexture.CreateView()});
         renderPass.cColorAttachments[0].resolveTarget = resolveTarget.CreateView();
-        if (utils::TextureFormatSupportsResolveTarget(format)) {
+        if (utils::TextureFormatSupportsResolveTarget(device, format)) {
             AssertBeginRenderPassSuccess(&renderPass);
         } else {
             AssertBeginRenderPassError(&renderPass);
diff --git a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
index 2cb0fc8..7d28eaf 100644
--- a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
@@ -147,7 +147,7 @@
 
         for (wgpu::TextureFormat format : utils::kFormatsInCoreSpec) {
             descriptor.format = format;
-            if (utils::TextureFormatSupportsMultisampling(format)) {
+            if (utils::TextureFormatSupportsMultisampling(device, format)) {
                 device.CreateTexture(&descriptor);
             } else {
                 ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
@@ -921,6 +921,43 @@
     device.CreateTexture(&descriptor);
 }
 
+class Norm16TextureFormatsValidationTests : public TextureValidationTest {
+  protected:
+    WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
+                                wgpu::DeviceDescriptor descriptor) override {
+        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::Norm16TextureFormats};
+        descriptor.requiredFeatures = requiredFeatures;
+        descriptor.requiredFeatureCount = 1;
+        return dawnAdapter.CreateDevice(&descriptor);
+    }
+};
+
+// Test that Norm16 formats are valid as renderable and sample-able texture if
+// 'norm16-texture-formats' is enabled.
+TEST_F(Norm16TextureFormatsValidationTests, RenderAndSample) {
+    wgpu::TextureDescriptor descriptor;
+    descriptor.size = {1, 1, 1};
+    descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding;
+
+    descriptor.format = wgpu::TextureFormat::R16Unorm;
+    device.CreateTexture(&descriptor);
+
+    descriptor.format = wgpu::TextureFormat::RG16Unorm;
+    device.CreateTexture(&descriptor);
+
+    descriptor.format = wgpu::TextureFormat::RGBA16Unorm;
+    device.CreateTexture(&descriptor);
+
+    descriptor.format = wgpu::TextureFormat::R16Snorm;
+    device.CreateTexture(&descriptor);
+
+    descriptor.format = wgpu::TextureFormat::RG16Snorm;
+    device.CreateTexture(&descriptor);
+
+    descriptor.format = wgpu::TextureFormat::RGBA16Snorm;
+    device.CreateTexture(&descriptor);
+}
+
 static void CheckTextureMatchesDescriptor(const wgpu::Texture& tex,
                                           const wgpu::TextureDescriptor& desc) {
     EXPECT_EQ(desc.size.width, tex.GetWidth());
diff --git a/src/dawn/utils/TextureUtils.cpp b/src/dawn/utils/TextureUtils.cpp
index 8f84b9c..9023857 100644
--- a/src/dawn/utils/TextureUtils.cpp
+++ b/src/dawn/utils/TextureUtils.cpp
@@ -121,6 +121,20 @@
     }
 }
 
+bool IsNorm16TextureFormat(wgpu::TextureFormat textureFormat) {
+    switch (textureFormat) {
+        case wgpu::TextureFormat::R16Unorm:
+        case wgpu::TextureFormat::RG16Unorm:
+        case wgpu::TextureFormat::RGBA16Unorm:
+        case wgpu::TextureFormat::R16Snorm:
+        case wgpu::TextureFormat::RG16Snorm:
+        case wgpu::TextureFormat::RGBA16Snorm:
+            return true;
+        default:
+            return false;
+    }
+}
+
 bool IsDepthOnlyFormat(wgpu::TextureFormat textureFormat) {
     switch (textureFormat) {
         case wgpu::TextureFormat::Depth16Unorm:
@@ -155,12 +169,17 @@
     }
 }
 
-bool TextureFormatSupportsMultisampling(wgpu::TextureFormat textureFormat) {
+bool TextureFormatSupportsMultisampling(const wgpu::Device& device,
+                                        wgpu::TextureFormat textureFormat) {
     if (IsBCTextureFormat(textureFormat) || IsETC2TextureFormat(textureFormat) ||
         IsASTCTextureFormat(textureFormat)) {
         return false;
     }
 
+    if (IsNorm16TextureFormat(textureFormat)) {
+        return device.HasFeature(wgpu::FeatureName::Norm16TextureFormats);
+    }
+
     switch (textureFormat) {
         case wgpu::TextureFormat::R32Uint:
         case wgpu::TextureFormat::R32Sint:
@@ -174,15 +193,18 @@
         case wgpu::TextureFormat::R8Snorm:
         case wgpu::TextureFormat::RG8Snorm:
         case wgpu::TextureFormat::RGBA8Snorm:
-        case wgpu::TextureFormat::RG11B10Ufloat:
             return false;
 
+        case wgpu::TextureFormat::RG11B10Ufloat:
+            return device.HasFeature(wgpu::FeatureName::RG11B10UfloatRenderable);
+
         default:
             return true;
     }
 }
 
-bool TextureFormatSupportsResolveTarget(wgpu::TextureFormat textureFormat) {
+bool TextureFormatSupportsResolveTarget(const wgpu::Device& device,
+                                        wgpu::TextureFormat textureFormat) {
     switch (textureFormat) {
         case wgpu::TextureFormat::R8Unorm:
         case wgpu::TextureFormat::RG8Unorm:
@@ -196,6 +218,14 @@
         case wgpu::TextureFormat::RGB10A2Unorm:
             return true;
 
+        case wgpu::TextureFormat::R16Unorm:
+        case wgpu::TextureFormat::R16Snorm:
+        case wgpu::TextureFormat::RG16Unorm:
+        case wgpu::TextureFormat::RG16Snorm:
+        case wgpu::TextureFormat::RGBA16Unorm:
+        case wgpu::TextureFormat::RGBA16Snorm:
+            return device.HasFeature(wgpu::FeatureName::Norm16TextureFormats);
+
         default:
             return false;
     }
@@ -225,6 +255,8 @@
         case wgpu::TextureFormat::Stencil8:
             return 1u;
 
+        case wgpu::TextureFormat::R16Unorm:
+        case wgpu::TextureFormat::R16Snorm:
         case wgpu::TextureFormat::R16Uint:
         case wgpu::TextureFormat::R16Sint:
         case wgpu::TextureFormat::R16Float:
@@ -237,6 +269,8 @@
         case wgpu::TextureFormat::R32Float:
         case wgpu::TextureFormat::R32Uint:
         case wgpu::TextureFormat::R32Sint:
+        case wgpu::TextureFormat::RG16Unorm:
+        case wgpu::TextureFormat::RG16Snorm:
         case wgpu::TextureFormat::RG16Uint:
         case wgpu::TextureFormat::RG16Sint:
         case wgpu::TextureFormat::RG16Float:
@@ -255,6 +289,8 @@
         case wgpu::TextureFormat::RG32Float:
         case wgpu::TextureFormat::RG32Uint:
         case wgpu::TextureFormat::RG32Sint:
+        case wgpu::TextureFormat::RGBA16Unorm:
+        case wgpu::TextureFormat::RGBA16Snorm:
         case wgpu::TextureFormat::RGBA16Uint:
         case wgpu::TextureFormat::RGBA16Sint:
         case wgpu::TextureFormat::RGBA16Float:
@@ -352,6 +388,8 @@
         case wgpu::TextureFormat::R8Snorm:
         case wgpu::TextureFormat::R8Uint:
         case wgpu::TextureFormat::R8Sint:
+        case wgpu::TextureFormat::R16Unorm:
+        case wgpu::TextureFormat::R16Snorm:
         case wgpu::TextureFormat::R16Uint:
         case wgpu::TextureFormat::R16Sint:
         case wgpu::TextureFormat::R16Float:
@@ -362,6 +400,8 @@
         case wgpu::TextureFormat::R32Float:
         case wgpu::TextureFormat::R32Uint:
         case wgpu::TextureFormat::R32Sint:
+        case wgpu::TextureFormat::RG16Unorm:
+        case wgpu::TextureFormat::RG16Snorm:
         case wgpu::TextureFormat::RG16Uint:
         case wgpu::TextureFormat::RG16Sint:
         case wgpu::TextureFormat::RG16Float:
@@ -378,6 +418,8 @@
         case wgpu::TextureFormat::RG32Float:
         case wgpu::TextureFormat::RG32Uint:
         case wgpu::TextureFormat::RG32Sint:
+        case wgpu::TextureFormat::RGBA16Unorm:
+        case wgpu::TextureFormat::RGBA16Snorm:
         case wgpu::TextureFormat::RGBA16Uint:
         case wgpu::TextureFormat::RGBA16Sint:
         case wgpu::TextureFormat::RGBA16Float:
@@ -468,6 +510,8 @@
         case wgpu::TextureFormat::R8Snorm:
         case wgpu::TextureFormat::R8Uint:
         case wgpu::TextureFormat::R8Sint:
+        case wgpu::TextureFormat::R16Unorm:
+        case wgpu::TextureFormat::R16Snorm:
         case wgpu::TextureFormat::R16Uint:
         case wgpu::TextureFormat::R16Sint:
         case wgpu::TextureFormat::R16Float:
@@ -478,6 +522,8 @@
         case wgpu::TextureFormat::R32Float:
         case wgpu::TextureFormat::R32Uint:
         case wgpu::TextureFormat::R32Sint:
+        case wgpu::TextureFormat::RG16Unorm:
+        case wgpu::TextureFormat::RG16Snorm:
         case wgpu::TextureFormat::RG16Uint:
         case wgpu::TextureFormat::RG16Sint:
         case wgpu::TextureFormat::RG16Float:
@@ -494,6 +540,8 @@
         case wgpu::TextureFormat::RG32Float:
         case wgpu::TextureFormat::RG32Uint:
         case wgpu::TextureFormat::RG32Sint:
+        case wgpu::TextureFormat::RGBA16Unorm:
+        case wgpu::TextureFormat::RGBA16Snorm:
         case wgpu::TextureFormat::RGBA16Uint:
         case wgpu::TextureFormat::RGBA16Sint:
         case wgpu::TextureFormat::RGBA16Float:
@@ -582,10 +630,14 @@
     switch (textureFormat) {
         case wgpu::TextureFormat::R8Unorm:
         case wgpu::TextureFormat::R8Snorm:
+        case wgpu::TextureFormat::R16Unorm:
+        case wgpu::TextureFormat::R16Snorm:
         case wgpu::TextureFormat::R16Float:
         case wgpu::TextureFormat::RG8Unorm:
         case wgpu::TextureFormat::RG8Snorm:
         case wgpu::TextureFormat::R32Float:
+        case wgpu::TextureFormat::RG16Unorm:
+        case wgpu::TextureFormat::RG16Snorm:
         case wgpu::TextureFormat::RG16Float:
         case wgpu::TextureFormat::RGBA8Unorm:
         case wgpu::TextureFormat::RGBA8Snorm:
@@ -593,6 +645,8 @@
         case wgpu::TextureFormat::RG11B10Ufloat:
         case wgpu::TextureFormat::RGB9E5Ufloat:
         case wgpu::TextureFormat::RG32Float:
+        case wgpu::TextureFormat::RGBA16Unorm:
+        case wgpu::TextureFormat::RGBA16Snorm:
         case wgpu::TextureFormat::RGBA16Float:
         case wgpu::TextureFormat::RGBA32Float:
         case wgpu::TextureFormat::BGRA8Unorm:
@@ -632,6 +686,8 @@
         case wgpu::TextureFormat::R8Unorm:
         case wgpu::TextureFormat::R8Uint:
         case wgpu::TextureFormat::R8Sint:
+        case wgpu::TextureFormat::R16Unorm:
+        case wgpu::TextureFormat::R16Snorm:
         case wgpu::TextureFormat::R16Uint:
         case wgpu::TextureFormat::R16Sint:
         case wgpu::TextureFormat::R16Float:
@@ -642,6 +698,8 @@
         case wgpu::TextureFormat::RG8Unorm:
         case wgpu::TextureFormat::RG8Uint:
         case wgpu::TextureFormat::RG8Sint:
+        case wgpu::TextureFormat::RG16Unorm:
+        case wgpu::TextureFormat::RG16Snorm:
         case wgpu::TextureFormat::RG16Uint:
         case wgpu::TextureFormat::RG16Sint:
         case wgpu::TextureFormat::RG16Float:
@@ -656,6 +714,8 @@
         case wgpu::TextureFormat::BGRA8Unorm:
         case wgpu::TextureFormat::BGRA8UnormSrgb:
         case wgpu::TextureFormat::RGB10A2Unorm:
+        case wgpu::TextureFormat::RGBA16Unorm:
+        case wgpu::TextureFormat::RGBA16Snorm:
         case wgpu::TextureFormat::RGBA16Uint:
         case wgpu::TextureFormat::RGBA16Sint:
         case wgpu::TextureFormat::RGBA16Float:
@@ -680,6 +740,10 @@
             return "rgba8sint";
         case wgpu::TextureFormat::BGRA8Unorm:
             return "bgra8unorm";
+        case wgpu::TextureFormat::RGBA16Unorm:
+            return "rgba16unorm";
+        case wgpu::TextureFormat::RGBA16Snorm:
+            return "rgba16snorm";
         case wgpu::TextureFormat::RGBA16Uint:
             return "rgba16uint";
         case wgpu::TextureFormat::RGBA16Sint:
@@ -715,6 +779,10 @@
             return "r8uint";
         case wgpu::TextureFormat::R8Sint:
             return "r8sint";
+        case wgpu::TextureFormat::R16Unorm:
+            return "r16unorm";
+        case wgpu::TextureFormat::R16Snorm:
+            return "r16snorm";
         case wgpu::TextureFormat::R16Uint:
             return "r16uint";
         case wgpu::TextureFormat::R16Sint:
@@ -729,6 +797,10 @@
             return "rg8uint";
         case wgpu::TextureFormat::RG8Sint:
             return "rg8sint";
+        case wgpu::TextureFormat::RG16Unorm:
+            return "rg16unorm";
+        case wgpu::TextureFormat::RG16Snorm:
+            return "rg16snorm";
         case wgpu::TextureFormat::RG16Uint:
             return "rg16uint";
         case wgpu::TextureFormat::RG16Sint:
diff --git a/src/dawn/utils/TextureUtils.h b/src/dawn/utils/TextureUtils.h
index be634a3..780152f 100644
--- a/src/dawn/utils/TextureUtils.h
+++ b/src/dawn/utils/TextureUtils.h
@@ -22,11 +22,13 @@
 #include "dawn/common/Assert.h"
 
 namespace dawn::utils {
-static constexpr std::array<wgpu::TextureFormat, 94> kAllTextureFormats = {
+static constexpr std::array<wgpu::TextureFormat, 100> kAllTextureFormats = {
     wgpu::TextureFormat::R8Unorm,
     wgpu::TextureFormat::R8Snorm,
     wgpu::TextureFormat::R8Uint,
     wgpu::TextureFormat::R8Sint,
+    wgpu::TextureFormat::R16Unorm,
+    wgpu::TextureFormat::R16Snorm,
     wgpu::TextureFormat::R16Uint,
     wgpu::TextureFormat::R16Sint,
     wgpu::TextureFormat::R16Float,
@@ -37,6 +39,8 @@
     wgpu::TextureFormat::R32Float,
     wgpu::TextureFormat::R32Uint,
     wgpu::TextureFormat::R32Sint,
+    wgpu::TextureFormat::RG16Unorm,
+    wgpu::TextureFormat::RG16Snorm,
     wgpu::TextureFormat::RG16Uint,
     wgpu::TextureFormat::RG16Sint,
     wgpu::TextureFormat::RG16Float,
@@ -53,6 +57,8 @@
     wgpu::TextureFormat::RG32Float,
     wgpu::TextureFormat::RG32Uint,
     wgpu::TextureFormat::RG32Sint,
+    wgpu::TextureFormat::RGBA16Unorm,
+    wgpu::TextureFormat::RGBA16Snorm,
     wgpu::TextureFormat::RGBA16Uint,
     wgpu::TextureFormat::RGBA16Sint,
     wgpu::TextureFormat::RGBA16Float,
@@ -205,6 +211,11 @@
                   kBCFormats.size() + kETC2Formats.size() + kASTCFormats.size(),
               "Number of compressed format must equal number of BC, ETC2, and ASTC formats.");
 
+static constexpr std::array<wgpu::TextureFormat, 6> kNorm16Formats = {
+    wgpu::TextureFormat::R16Unorm, wgpu::TextureFormat::RG16Unorm, wgpu::TextureFormat::RGBA16Unorm,
+    wgpu::TextureFormat::R16Snorm, wgpu::TextureFormat::RG16Snorm, wgpu::TextureFormat::RGBA16Snorm,
+};
+
 static constexpr std::array<wgpu::TextureFormat, 5> kDepthFormats = {
     wgpu::TextureFormat::Depth16Unorm,         wgpu::TextureFormat::Depth32Float,
     wgpu::TextureFormat::Depth24Plus,          wgpu::TextureFormat::Depth24PlusStencil8,
@@ -226,6 +237,7 @@
 bool IsBCTextureFormat(wgpu::TextureFormat textureFormat);
 bool IsETC2TextureFormat(wgpu::TextureFormat textureFormat);
 bool IsASTCTextureFormat(wgpu::TextureFormat textureFormat);
+bool IsNorm16TextureFormat(wgpu::TextureFormat textureFormat);
 
 bool IsDepthOnlyFormat(wgpu::TextureFormat textureFormat);
 bool IsStencilOnlyFormat(wgpu::TextureFormat textureFormat);
@@ -233,8 +245,10 @@
 
 bool IsMultiPlanarFormat(wgpu::TextureFormat textureFormat);
 
-bool TextureFormatSupportsMultisampling(wgpu::TextureFormat textureFormat);
-bool TextureFormatSupportsResolveTarget(wgpu::TextureFormat textureFormat);
+bool TextureFormatSupportsMultisampling(const wgpu::Device& device,
+                                        wgpu::TextureFormat textureFormat);
+bool TextureFormatSupportsResolveTarget(const wgpu::Device& device,
+                                        wgpu::TextureFormat textureFormat);
 
 uint32_t GetTexelBlockSizeInBytes(wgpu::TextureFormat textureFormat);
 uint32_t GetTextureFormatBlockWidth(wgpu::TextureFormat textureFormat);
diff --git a/src/dawn/wire/SupportedFeatures.cpp b/src/dawn/wire/SupportedFeatures.cpp
index b82e806..f5fbeed 100644
--- a/src/dawn/wire/SupportedFeatures.cpp
+++ b/src/dawn/wire/SupportedFeatures.cpp
@@ -66,6 +66,7 @@
         case WGPUFeatureName_ChromiumExperimentalReadWriteStorageTexture:
         case WGPUFeatureName_PixelLocalStorageCoherent:
         case WGPUFeatureName_PixelLocalStorageNonCoherent:
+        case WGPUFeatureName_Norm16TextureFormats:
             return true;
     }