Add e2e tests for Tier1 storage texture access

Add end-to-end tests for STORAGE_BINDING (write-only) for a range of TextureFormatsTier1 formats, including: r16unorm, r16snorm, rg16unorm, r16snorm, rgba16unorm, rgba16snorm, r8unorm, r8snorm, r8uint, r8sint, rg8unorm, rg8snorm, rg8uint, rg8sint, r16uint, r16sint, r16float, rg16uint, rg16sint, rg16float, rgb10a2uint, rgb10a2unorm, rg11b10ufloat. These tests confirm proper GPUStorageTextureAccess when TextureFormatsTier1 is enabled.

Bug: 421941589
Change-Id: I37f190815975e695692dc3fe3ffaa866ddef857c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/253075
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Shanxing Mei <shanxing.mei@intel.com>
diff --git a/src/dawn/tests/end2end/StorageTextureTests.cpp b/src/dawn/tests/end2end/StorageTextureTests.cpp
index 0506051..434fc2a 100644
--- a/src/dawn/tests/end2end/StorageTextureTests.cpp
+++ b/src/dawn/tests/end2end/StorageTextureTests.cpp
@@ -131,21 +131,17 @@
                 break;
             }
 
-            // 16-bit (unsigned integer, signed integer and float) 4-component formats
-            case wgpu::TextureFormat::RGBA16Uint: {
+            // 16-bit float formats
+            case wgpu::TextureFormat::R16Float: {
                 uint16_t* valuePtr = static_cast<uint16_t*>(pixelValuePtr);
-                valuePtr[0] = static_cast<uint16_t>(pixelValue);
-                valuePtr[1] = static_cast<uint16_t>(pixelValue * 2);
-                valuePtr[2] = static_cast<uint16_t>(pixelValue * 3);
-                valuePtr[3] = static_cast<uint16_t>(pixelValue * 4);
+                *valuePtr = Float32ToFloat16(static_cast<float_t>(pixelValue));
                 break;
             }
-            case wgpu::TextureFormat::RGBA16Sint: {
-                int16_t* valuePtr = static_cast<int16_t*>(pixelValuePtr);
-                valuePtr[0] = static_cast<int16_t>(pixelValue);
-                valuePtr[1] = -static_cast<int16_t>(pixelValue);
-                valuePtr[2] = static_cast<int16_t>(pixelValue * 2);
-                valuePtr[3] = -static_cast<int16_t>(pixelValue * 2);
+
+            case wgpu::TextureFormat::RG16Float: {
+                uint16_t* valuePtr = static_cast<uint16_t*>(pixelValuePtr);
+                valuePtr[0] = Float32ToFloat16(static_cast<float_t>(pixelValue));
+                valuePtr[1] = Float32ToFloat16(-static_cast<float_t>(pixelValue));
                 break;
             }
 
@@ -184,12 +180,125 @@
                 break;
             }
 
-            case wgpu::TextureFormat::R8Unorm: {
+            // 16-bit normalized/non-normalized unsigned/signed integer formats
+            case wgpu::TextureFormat::R16Unorm:
+            case wgpu::TextureFormat::R16Uint: {
+                uint16_t* valuePtr = static_cast<uint16_t*>(pixelValuePtr);
+                *valuePtr = static_cast<uint16_t>(pixelValue);
+                break;
+            }
+
+            case wgpu::TextureFormat::RG16Unorm:
+            case wgpu::TextureFormat::RG16Uint: {
+                uint16_t* valuePtr = static_cast<uint16_t*>(pixelValuePtr);
+                valuePtr[0] = static_cast<uint16_t>(pixelValue);
+                valuePtr[1] = static_cast<uint16_t>(pixelValue * 2);
+                break;
+            }
+
+            case wgpu::TextureFormat::R16Snorm:
+            case wgpu::TextureFormat::R16Sint: {
+                int16_t* valuePtr = static_cast<int16_t*>(pixelValuePtr);
+                *valuePtr = static_cast<int16_t>(pixelValue);
+                break;
+            }
+
+            case wgpu::TextureFormat::RG16Snorm:
+            case wgpu::TextureFormat::RG16Sint: {
+                int16_t* valuePtr = static_cast<int16_t*>(pixelValuePtr);
+                valuePtr[0] = static_cast<int16_t>(pixelValue);
+                valuePtr[1] = -static_cast<int16_t>(pixelValue);
+                break;
+            }
+
+            case wgpu::TextureFormat::RGBA16Unorm:
+            case wgpu::TextureFormat::RGBA16Uint: {
+                uint16_t* valuePtr = static_cast<uint16_t*>(pixelValuePtr);
+                valuePtr[0] = static_cast<uint16_t>(pixelValue);
+                valuePtr[1] = static_cast<uint16_t>(pixelValue * 2);
+                valuePtr[2] = static_cast<uint16_t>(pixelValue * 3);
+                valuePtr[3] = static_cast<uint16_t>(pixelValue * 4);
+                break;
+            }
+
+            case wgpu::TextureFormat::RGBA16Snorm:
+            case wgpu::TextureFormat::RGBA16Sint: {
+                int16_t* valuePtr = static_cast<int16_t*>(pixelValuePtr);
+                valuePtr[0] = static_cast<int16_t>(pixelValue);
+                valuePtr[1] = -static_cast<int16_t>(pixelValue);
+                valuePtr[2] = static_cast<int16_t>(pixelValue * 2);
+                valuePtr[3] = -static_cast<int16_t>(pixelValue * 2);
+                break;
+            }
+
+            // 8-bit normalized/non-normalized unsigned/signed integer formats
+            case wgpu::TextureFormat::R8Unorm:
+            case wgpu::TextureFormat::R8Uint: {
                 uint8_t* valuePtr = static_cast<uint8_t*>(pixelValuePtr);
                 *valuePtr = pixelValue;
                 break;
             }
 
+            case wgpu::TextureFormat::RG8Unorm:
+            case wgpu::TextureFormat::RG8Uint: {
+                uint8_t* valuePtr = static_cast<uint8_t*>(pixelValuePtr);
+                valuePtr[0] = static_cast<uint8_t>(pixelValue);
+                valuePtr[1] = static_cast<uint8_t>(pixelValue * 2);
+                break;
+            }
+
+            case wgpu::TextureFormat::R8Snorm:
+            case wgpu::TextureFormat::R8Sint: {
+                int8_t* valuePtr = static_cast<int8_t*>(pixelValuePtr);
+                *valuePtr = static_cast<int8_t>(pixelValue);
+                break;
+            }
+
+            case wgpu::TextureFormat::RG8Snorm:
+            case wgpu::TextureFormat::RG8Sint: {
+                int8_t* valuePtr = static_cast<int8_t*>(pixelValuePtr);
+                valuePtr[0] = static_cast<int8_t>(pixelValue);
+                valuePtr[1] = -static_cast<int8_t>(pixelValue);
+                break;
+            }
+
+            case wgpu::TextureFormat::RGB10A2Uint: {
+                uint32_t* valuePtr = static_cast<uint32_t*>(pixelValuePtr);
+                uint32_t r = static_cast<uint32_t>(pixelValue) % 1024;
+                uint32_t g = static_cast<uint32_t>(pixelValue * 2) % 1024;
+                uint32_t b = static_cast<uint32_t>(pixelValue * 3) % 1024;
+                uint32_t a = static_cast<uint32_t>(3) % 4;
+                *valuePtr = (a << 30) | (b << 20) | (g << 10) | r;
+                break;
+            }
+
+            case wgpu::TextureFormat::RGB10A2Unorm: {
+                uint32_t* valuePtr = static_cast<uint32_t*>(pixelValuePtr);
+                uint32_t r = static_cast<uint32_t>(pixelValue) % 1024;
+                uint32_t g = static_cast<uint32_t>(pixelValue * 2) % 1024;
+                uint32_t b = static_cast<uint32_t>(pixelValue * 3) % 1024;
+                uint32_t a = static_cast<uint32_t>(3);
+                *valuePtr = (a << 30) | (b << 20) | (g << 10) | r;
+                break;
+            }
+
+            case wgpu::TextureFormat::RG11B10Ufloat: {
+                uint32_t* valuePtr = static_cast<uint32_t*>(pixelValuePtr);
+
+                auto MakeRG11B10 = [](uint32_t r, uint32_t g, uint32_t b) {
+                    DAWN_ASSERT((r & 0x7FF) == r);
+                    DAWN_ASSERT((g & 0x7FF) == g);
+                    DAWN_ASSERT((b & 0x3FF) == b);
+                    return r | g << 11 | b << 22;
+                };
+
+                constexpr uint32_t kFloat11One = 0x3C0;
+                constexpr uint32_t kFloat10Zero = 0;
+
+                *valuePtr = MakeRG11B10(kFloat11One, kFloat11One, kFloat10Zero);
+                break;
+            }
+
             default:
                 DAWN_UNREACHABLE();
                 break;
@@ -228,12 +337,19 @@
     const char* GetExpectedPixelValue(wgpu::TextureFormat format) {
         switch (format) {
             // non-normalized unsigned integer formats
+            case wgpu::TextureFormat::R8Uint:
+            case wgpu::TextureFormat::R16Uint:
             case wgpu::TextureFormat::R32Uint:
                 return "vec4u(u32(value), 0u, 0u, 1u)";
 
+            case wgpu::TextureFormat::RG8Uint:
+            case wgpu::TextureFormat::RG16Uint:
             case wgpu::TextureFormat::RG32Uint:
                 return "vec4u(u32(value), u32(value) * 2u, 0u, 1u)";
 
+            case wgpu::TextureFormat::RGB10A2Uint:
+                return "vec4u(u32(value), u32(value) * 2u, u32(value) * 3u, 3u)";
+
             case wgpu::TextureFormat::RGBA8Uint:
             case wgpu::TextureFormat::RGBA16Uint:
             case wgpu::TextureFormat::RGBA32Uint:
@@ -241,9 +357,13 @@
                        "u32(value) * 3u, u32(value) * 4u)";
 
             // non-normalized signed integer formats
+            case wgpu::TextureFormat::R8Sint:
+            case wgpu::TextureFormat::R16Sint:
             case wgpu::TextureFormat::R32Sint:
                 return "vec4i(i32(value), 0, 0, 1)";
 
+            case wgpu::TextureFormat::RG8Sint:
+            case wgpu::TextureFormat::RG16Sint:
             case wgpu::TextureFormat::RG32Sint:
                 return "vec4i(i32(value), -i32(value), 0, 1)";
 
@@ -253,6 +373,13 @@
                 return "vec4i(i32(value), -i32(value), i32(value) * 2, -i32(value) * 2)";
 
             // float formats
+
+            case wgpu::TextureFormat::R16Float:
+                return "vec4f(f32(value), 0.0, 0.0, 1.0)";
+
+            case wgpu::TextureFormat::RG16Float:
+                return "vec4f(f32(value), -f32(value), 0.0, 1.0)";
+
             case wgpu::TextureFormat::R32Float:
                 return "vec4f(f32(value) * 1.1, 0.0, 0.0, 1.0)";
 
@@ -280,6 +407,42 @@
             case wgpu::TextureFormat::R8Unorm:
                 return "vec4f(f32(value) / 255.0, 0.0, 0.0, 1.0)";
 
+            case wgpu::TextureFormat::R8Snorm:
+                return "vec4f(f32(value) / 127.0, 0.0, 0.0, 1.0)";
+
+            case wgpu::TextureFormat::RG8Unorm:
+                return "vec4f(f32(value) / 255.0, f32(value) * 2.0 / 255.0, 0.0, 1.0)";
+
+            case wgpu::TextureFormat::RG8Snorm:
+                return "vec4f(f32(value) / 127.0, -f32(value) / 127.0, 0.0, 1.0)";
+
+            case wgpu::TextureFormat::R16Unorm:
+                return "vec4f(f32(value) / 65535.0, 0.0, 0.0, 1.0)";
+
+            case wgpu::TextureFormat::R16Snorm:
+                return "vec4f(f32(value) / 32767.0, 0.0, 0.0, 1.0)";
+
+            case wgpu::TextureFormat::RG16Unorm:
+                return "vec4f(f32(value) / 65535.0, f32(value) * 2.0 / 65535.0, 0.0, 1.0)";
+
+            case wgpu::TextureFormat::RG16Snorm:
+                return "vec4f(f32(value) / 32767.0, -f32(value) / 32767.0, 0.0, 1.0)";
+
+            case wgpu::TextureFormat::RGBA16Unorm:
+                return "vec4f(f32(value) / 65535.0, f32(value) * 2.0 / 65535.0, "
+                       "f32(value) * 3.0 / 65535.0, f32(value) * 4.0 / 65535.0)";
+
+            case wgpu::TextureFormat::RGBA16Snorm:
+                return "vec4f(f32(value) / 32767.0, -f32(value) / 32767.0, "
+                       "f32(value) * 2.0 / 32767.0, -f32(value) * 2.0 / 32767.0)";
+
+            case wgpu::TextureFormat::RGB10A2Unorm:
+                return "vec4f(f32(value) / 1023.0, f32(value) * 2.0 / 1023.0, "
+                       "f32(value) * 3.0 / 1023.0, 3.0)";
+
+            case wgpu::TextureFormat::RG11B10Ufloat:
+                return "vec4f(1.0, 1.0, 0.0, 1.0)";
+
             default:
                 DAWN_UNREACHABLE();
                 break;
@@ -1847,5 +2010,51 @@
                       MetalBackend(),
                       VulkanBackend());
 
+class Tier1StorageValidationTests : public StorageTextureTests {
+  public:
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        if (SupportsFeatures({wgpu::FeatureName::TextureFormatsTier1})) {
+            mIsTextureFormatsTier1Supported = true;
+            return {wgpu::FeatureName::TextureFormatsTier1};
+        }
+        return {};
+    }
+
+    bool IsTextureFormatsTier1Supported() { return mIsTextureFormatsTier1Supported; }
+
+  private:
+    bool mIsTextureFormatsTier1Supported = false;
+};
+
+// Test that kTier1AdditionalStorageFormats formats have the "write-only" GPUStorageTextureAccess
+//  capability if 'texture-formats-tier1' is enabled.
+TEST_P(Tier1StorageValidationTests, WriteonlyStorageTextureInFragmentShader) {
+    DAWN_TEST_UNSUPPORTED_IF(!IsTextureFormatsTier1Supported());
+    for (const auto format : utils::kTier1AdditionalStorageFormats) {
+        SCOPED_TRACE(
+            absl::StrFormat("Test format: %s", utils::GetWGSLImageFormatQualifier(format)));
+        // Prepare the write-only storage texture.
+        wgpu::Texture writeonlyStorageTexture =
+            CreateTexture(format, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc,
+                          {kWidth, kHeight});
+
+        // Write the expected pixel values into the write-only storage texture.
+        const std::string fragmentShader = CommonWriteOnlyTestCode("fragment", format);
+        WriteIntoStorageTextureInRenderPass(writeonlyStorageTexture, kSimpleVertexShader,
+                                            fragmentShader.c_str());
+
+        // Verify the pixel data in the write-only storage texture is expected.
+        CheckOutputStorageTexture(writeonlyStorageTexture, format, {kWidth, kHeight});
+    }
+}
+
+DAWN_INSTANTIATE_TEST(Tier1StorageValidationTests,
+                      D3D11Backend(),
+                      D3D12Backend(),
+                      OpenGLBackend(),
+                      OpenGLESBackend(),
+                      MetalBackend(),
+                      VulkanBackend());
+
 }  // anonymous namespace
 }  // namespace dawn
diff --git a/src/dawn/utils/TextureUtils.cpp b/src/dawn/utils/TextureUtils.cpp
index 044fee2..8810ee4 100644
--- a/src/dawn/utils/TextureUtils.cpp
+++ b/src/dawn/utils/TextureUtils.cpp
@@ -1000,12 +1000,52 @@
             return "rgba32sint";
         case wgpu::TextureFormat::RGBA32Float:
             return "rgba32float";
-        // For Chromium Internal Graphite
         case wgpu::TextureFormat::R8Unorm:
             return "r8unorm";
 
+        case wgpu::TextureFormat::R8Snorm:
+            return "r8snorm";
+        case wgpu::TextureFormat::R8Uint:
+            return "r8uint";
+        case wgpu::TextureFormat::R8Sint:
+            return "r8sint";
+        case wgpu::TextureFormat::RG8Unorm:
+            return "rg8unorm";
+        case wgpu::TextureFormat::RG8Snorm:
+            return "rg8snorm";
+        case wgpu::TextureFormat::RG8Uint:
+            return "rg8uint";
+        case wgpu::TextureFormat::RG8Sint:
+            return "rg8sint";
+        case wgpu::TextureFormat::R16Uint:
+            return "r16uint";
+        case wgpu::TextureFormat::R16Sint:
+            return "r16sint";
+        case wgpu::TextureFormat::R16Float:
+            return "r16float";
+        case wgpu::TextureFormat::RG16Uint:
+            return "rg16uint";
+        case wgpu::TextureFormat::RG16Sint:
+            return "rg16sint";
+        case wgpu::TextureFormat::RG16Float:
+            return "rg16float";
+        case wgpu::TextureFormat::RGB10A2Uint:
+            return "rgb10a2uint";
+        case wgpu::TextureFormat::RGB10A2Unorm:
+            return "rgb10a2unorm";
+        case wgpu::TextureFormat::RG11B10Ufloat:
+            return "rg11b10ufloat";
+
 #ifndef __EMSCRIPTEN__
         // Unorm and Snorm 16 formats.
+        case wgpu::TextureFormat::R16Unorm:
+            return "r16unorm";
+        case wgpu::TextureFormat::R16Snorm:
+            return "r16snorm";
+        case wgpu::TextureFormat::RG16Unorm:
+            return "rg16unorm";
+        case wgpu::TextureFormat::RG16Snorm:
+            return "rg16snorm";
         case wgpu::TextureFormat::RGBA16Unorm:
             return "rgba16unorm";
         case wgpu::TextureFormat::RGBA16Snorm: