Convert StorageTextureValidationTests to WGSL

Bug: tint:140
Bug: tint:368
Change-Id: I6593e40db5db4058bf6fdf3303ec7e99c0a3df12
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/37900
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/tests/unittests/validation/StorageTextureValidationTests.cpp b/src/tests/unittests/validation/StorageTextureValidationTests.cpp
index 11a9b0e..6cc5eef 100644
--- a/src/tests/unittests/validation/StorageTextureValidationTests.cpp
+++ b/src/tests/unittests/validation/StorageTextureValidationTests.cpp
@@ -35,20 +35,21 @@
             })");
     }
 
-    static const char* GetGLSLFloatImageTypeDeclaration(wgpu::TextureViewDimension dimension) {
+    static const char* GetFloatImageTypeDeclaration(wgpu::TextureViewDimension dimension) {
+        // TODO(bclayton): Support and test texture_storage_1d_array
         switch (dimension) {
             case wgpu::TextureViewDimension::e1D:
-                return "image1D";
+                return "texture_storage_1d";
             case wgpu::TextureViewDimension::e2D:
-                return "image2D";
+                return "texture_storage_2d";
             case wgpu::TextureViewDimension::e2DArray:
-                return "image2DArray";
-            case wgpu::TextureViewDimension::Cube:
-                return "imageCube";
-            case wgpu::TextureViewDimension::CubeArray:
-                return "imageCubeArray";
+                return "texture_storage_2d_array";
             case wgpu::TextureViewDimension::e3D:
-                return "image3D";
+                return "texture_storage_3d";
+            case wgpu::TextureViewDimension::Cube:
+                return "texture_storage_cube";  // Note: Doesn't exist in WGSL (yet)
+            case wgpu::TextureViewDimension::CubeArray:
+                return "texture_storage_cube_array";  // Note: Doesn't exist in WGSL (yet)
             case wgpu::TextureViewDimension::Undefined:
             default:
                 UNREACHABLE();
@@ -60,26 +61,22 @@
         wgpu::StorageTextureAccess storageTextureBindingType,
         wgpu::TextureFormat textureFormat,
         wgpu::TextureViewDimension textureViewDimension = wgpu::TextureViewDimension::e2D) {
-        const char* glslImageFormatQualifier = utils::GetGLSLImageFormatQualifier(textureFormat);
-        const char* textureComponentTypePrefix =
-            utils::GetColorTextureComponentGLSLTypePrefix(textureFormat);
         return CreateComputeShaderWithStorageTexture(
-            storageTextureBindingType, glslImageFormatQualifier, textureComponentTypePrefix,
-            GetGLSLFloatImageTypeDeclaration(textureViewDimension));
+            storageTextureBindingType, utils::GetWGSLImageFormatQualifier(textureFormat),
+            GetFloatImageTypeDeclaration(textureViewDimension));
     }
 
     static std::string CreateComputeShaderWithStorageTexture(
         wgpu::StorageTextureAccess storageTextureBindingType,
-        const char* glslImageFormatQualifier,
-        const char* textureComponentTypePrefix,
-        const char* glslImageTypeDeclaration = "image2D") {
-        const char* memoryQualifier = "";
+        const char* imageFormatQualifier,
+        const char* imageTypeDeclaration = "texture_storage_2d") {
+        const char* access = "";
         switch (storageTextureBindingType) {
             case wgpu::StorageTextureAccess::ReadOnly:
-                memoryQualifier = "readonly";
+                access = "read";
                 break;
             case wgpu::StorageTextureAccess::WriteOnly:
-                memoryQualifier = "writeonly";
+                access = "write";
                 break;
             default:
                 UNREACHABLE();
@@ -87,12 +84,11 @@
         }
 
         std::ostringstream ostream;
-        ostream << "#version 450\n"
-                   "layout (set = 0, binding = 0, "
-                << glslImageFormatQualifier << ") uniform " << memoryQualifier << " "
-                << textureComponentTypePrefix << glslImageTypeDeclaration
-                << " image0;\n"
-                   "void main() {\n"
+        ostream << "[[group(0), binding(0)]] var<uniform_constant> image0 : "
+                << "[[access(" << access << ")]] " << imageTypeDeclaration << "<"
+                << imageFormatQualifier
+                << ">;\n"
+                   "[[stage(compute)]] fn main() -> void {\n"
                    "}\n";
 
         return ostream.str();
@@ -126,7 +122,7 @@
     {
         wgpu::ShaderModule vsModule = utils::CreateShaderModuleFromWGSL(device, R"(
             [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read)]] texture_storage_2d<rgba8unorm>;
-            [[builtin(vertex_idx)]] var<in> VertexIndex : u32;
+            [[builtin(vertex_index)]] var<in> VertexIndex : u32;
             [[builtin(position)]] var<out> Position : vec4<f32>;
             [[stage(vertex)]] fn main() -> void {
                 Position = textureLoad(image0, vec2<i32>(i32(VertexIndex), 0));
@@ -157,13 +153,12 @@
     }
 
     // Write-only storage textures cannot be declared in a vertex shader.
-    {
-        wgpu::ShaderModule vsModule =
-            utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
-            #version 450
-            layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0;
-            void main() {
-                imageStore(image0, ivec2(gl_VertexIndex, 0), vec4(1.f, 0.f, 0.f, 1.f));
+    if ((false) /* TODO(https://crbug.com/tint/449) */) {
+        wgpu::ShaderModule vsModule = utils::CreateShaderModuleFromWGSL(device, R"(
+            [[builtin(vertex_index)]] var<in> vertex_index : u32;
+            [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(write)]] texture_storage_2d<rgba8unorm>;
+            [[stage(vertex)]] fn main() -> void {
+                textureStore(image0, vec2<i32>(i32(vertex_index), 0), vec4<f32>(1.0, 0.0, 0.0, 1.0));
             })");
 
         utils::ComboRenderPipelineDescriptor descriptor(device);
@@ -175,12 +170,11 @@
 
     // Write-only storage textures can be declared in a fragment shader.
     {
-        wgpu::ShaderModule fsModule =
-            utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
-            #version 450
-            layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0;
-            void main() {
-                imageStore(image0, ivec2(gl_FragCoord.xy), vec4(1.f, 0.f, 0.f, 1.f));
+        wgpu::ShaderModule fsModule = utils::CreateShaderModuleFromWGSL(device, R"(
+            [[builtin(frag_coord)]] var<in> frag_coord : vec4<f32>;
+            [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(write)]] texture_storage_2d<rgba8unorm>;
+            [[stage(fragment)]] fn main() -> void {
+                textureStore(image0, vec2<i32>(frag_coord.xy), vec4<f32>(1.0, 0.0, 0.0, 1.0));
             })");
 
         utils::ComboRenderPipelineDescriptor descriptor(device);
@@ -219,12 +213,12 @@
 
     // Write-only storage textures can be declared in a compute shader.
     {
-        wgpu::ShaderModule csModule =
-            utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
-            #version 450
-            layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0;
-            void main() {
-                imageStore(image0, ivec2(gl_LocalInvocationID.xy), vec4(0.f, 0.f, 0.f, 0.f));
+        wgpu::ShaderModule csModule = utils::CreateShaderModuleFromWGSL(device, R"(
+            [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(write)]] texture_storage_2d<rgba8unorm>;
+            [[builtin(local_invocation_id)]] var<in> LocalInvocationID : vec3<u32>;
+
+            [[stage(compute)]] fn main() -> void {
+                textureStore(image0, vec2<i32>(LocalInvocationID.xy), vec4<f32>(0.0, 0.0, 0.0, 0.0));
             })");
 
         wgpu::ComputePipelineDescriptor descriptor;
@@ -236,40 +230,29 @@
     }
 }
 
-// Validate read-write storage textures have not been supported yet.
-// TODO(cwallez@chromium.org): Convert them to SPIRV ASM to remove the dependency on glslang.
+// Validate read-write storage textures are not currently supported.
 TEST_F(StorageTextureValidationTests, ReadWriteStorageTexture) {
     // Read-write storage textures cannot be declared in a vertex shader by default.
     {
-        ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
-            #version 450
-            layout(set = 0, binding = 0, rgba8) uniform image2D image0;
-            void main() {
-                vec4 pixel = imageLoad(image0, ivec2(gl_VertexIndex, 0));
-                imageStore(image0, ivec2(gl_VertexIndex, 0), pixel * 2);
+        ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, R"(
+            [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read_write)]] texture_storage_2d<rgba8unorm>;
+            [[stage(vertex)]] fn main() -> void {
             })"));
     }
 
     // Read-write storage textures cannot be declared in a fragment shader by default.
     {
-        ASSERT_DEVICE_ERROR(
-            utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
-            #version 450
-            layout(set = 0, binding = 0, rgba8) uniform image2D image0;
-            void main() {
-                vec4 pixel = imageLoad(image0, ivec2(gl_FragCoord.xy));
-                imageStore(image0, ivec2(gl_FragCoord.xy), pixel * 2);
+        ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, R"(
+            [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read_write)]] texture_storage_2d<rgba8unorm>;
+            [[stage(fragment)]] fn main() -> void {
             })"));
     }
 
     // Read-write storage textures cannot be declared in a compute shader by default.
     {
-        ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
-            #version 450
-            layout(set = 0, binding = 0, rgba8) uniform image2D image0;
-            void main() {
-                vec4 pixel = imageLoad(image0, ivec2(gl_LocalInvocationID.xy));
-                imageStore(image0, ivec2(gl_LocalInvocationID.xy), pixel * 2);
+        ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, R"(
+            [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read_write)]] texture_storage_2d<rgba8unorm>;
+            [[stage(compute)]] fn main() -> void {
             })"));
     }
 }
@@ -334,11 +317,10 @@
             std::string computeShader =
                 CreateComputeShaderWithStorageTexture(storageTextureBindingType, format);
             if (utils::TextureFormatSupportsStorageTexture(format)) {
-                utils::CreateShaderModule(device, utils::SingleShaderStage::Compute,
-                                          computeShader.c_str());
+                utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
             } else {
-                ASSERT_DEVICE_ERROR(utils::CreateShaderModule(
-                    device, utils::SingleShaderStage::Compute, computeShader.c_str()));
+                ASSERT_DEVICE_ERROR(
+                    utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
             }
         }
     }
@@ -346,33 +328,29 @@
 
 // Verify that declaring a storage texture format that is not supported in WebGPU causes validation
 // error.
-TEST_F(StorageTextureValidationTests, UnsupportedSPIRVStorageTextureFormat) {
-    struct TextureFormatInfo {
-        const char* name;
-        const char* componentTypePrefix;
+TEST_F(StorageTextureValidationTests, UnsupportedWGSLStorageTextureFormat) {
+    constexpr std::array<wgpu::TextureFormat, 16> kUnsupportedTextureFormats = {
+        wgpu::TextureFormat::R8Unorm,      wgpu::TextureFormat::R8Snorm,
+        wgpu::TextureFormat::R8Uint,       wgpu::TextureFormat::R8Sint,
+        wgpu::TextureFormat::R16Uint,      wgpu::TextureFormat::R16Sint,
+        wgpu::TextureFormat::R16Float,     wgpu::TextureFormat::RG8Unorm,
+        wgpu::TextureFormat::RG8Snorm,     wgpu::TextureFormat::RG8Uint,
+        wgpu::TextureFormat::RG8Sint,      wgpu::TextureFormat::RG16Uint,
+        wgpu::TextureFormat::RG16Sint,     wgpu::TextureFormat::RG16Float,
+        wgpu::TextureFormat::RGB10A2Unorm, wgpu::TextureFormat::RG11B10Ufloat,
     };
 
-    constexpr std::array<TextureFormatInfo, 7> kUnsupportedTextureFormats = {{{"rgba16", ""},
-                                                                              {"rg16", ""},
-                                                                              {"r16", ""},
-                                                                              {"rgba16_snorm", ""},
-                                                                              {"rg16_snorm", ""},
-                                                                              {"r16_snorm", ""},
-                                                                              {"rgb10_a2ui", "u"}}};
-
     for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
-        for (const TextureFormatInfo& formatInfo : kUnsupportedTextureFormats) {
-            std::string computeShader = CreateComputeShaderWithStorageTexture(
-                bindingType, formatInfo.name, formatInfo.componentTypePrefix);
-            ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Compute,
-                                                          computeShader.c_str()));
+        for (wgpu::TextureFormat format : kUnsupportedTextureFormats) {
+            std::string computeShader = CreateComputeShaderWithStorageTexture(bindingType, format);
+            ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
         }
     }
 }
 
-// Verify that declaring a storage texture dimension that is not supported in WebGPU in shader
-// causes validation error at the creation of PSO. WebGPU doesn't support using cube map texture
-// views and cube map array texture views as storage textures.
+// Verify that declaring a storage texture dimension that isn't supported by
+// WebGPU causes a compile failure. WebGPU doesn't support using cube map
+// texture views and cube map array texture views as storage textures.
 TEST_F(StorageTextureValidationTests, UnsupportedTextureViewDimensionInShader) {
     constexpr std::array<wgpu::TextureViewDimension, 2> kUnsupportedTextureViewDimensions = {
         wgpu::TextureViewDimension::Cube, wgpu::TextureViewDimension::CubeArray};
@@ -382,14 +360,7 @@
         for (wgpu::TextureViewDimension dimension : kUnsupportedTextureViewDimensions) {
             std::string computeShader =
                 CreateComputeShaderWithStorageTexture(bindingType, kFormat, dimension);
-            wgpu::ShaderModule csModule = utils::CreateShaderModule(
-                device, utils::SingleShaderStage::Compute, computeShader.c_str());
-
-            wgpu::ComputePipelineDescriptor computePipelineDescriptor;
-            computePipelineDescriptor.computeStage.module = csModule;
-            computePipelineDescriptor.computeStage.entryPoint = "main";
-            computePipelineDescriptor.layout = nullptr;
-            ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor));
+            ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
         }
     }
 }
@@ -431,8 +402,8 @@
         // Create the compute shader with the given binding type.
         std::string computeShader =
             CreateComputeShaderWithStorageTexture(bindingTypeInShader, kStorageTextureFormat);
-        wgpu::ShaderModule csModule = utils::CreateShaderModule(
-            device, utils::SingleShaderStage::Compute, computeShader.c_str());
+        wgpu::ShaderModule csModule =
+            utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
 
         // Set common fields of compute pipeline descriptor.
         wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor;
@@ -508,8 +479,8 @@
             // format.
             std::string computeShader =
                 CreateComputeShaderWithStorageTexture(bindingType, storageTextureFormatInShader);
-            wgpu::ShaderModule csModule = utils::CreateShaderModule(
-                device, utils::SingleShaderStage::Compute, computeShader.c_str());
+            wgpu::ShaderModule csModule =
+                utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
 
             // Set common fields of compute pipeline descriptor.
             wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor;
@@ -565,8 +536,8 @@
             // Create the compute shader with the given texture view dimension.
             std::string computeShader = CreateComputeShaderWithStorageTexture(
                 bindingType, kStorageTextureFormat, dimensionInShader);
-            wgpu::ShaderModule csModule = utils::CreateShaderModule(
-                device, utils::SingleShaderStage::Compute, computeShader.c_str());
+            wgpu::ShaderModule csModule =
+                utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
 
             // Set common fields of compute pipeline descriptor.
             wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor;
@@ -797,9 +768,8 @@
 TEST_F(StorageTextureValidationTests, MultisampledStorageTexture) {
     for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
         std::string computeShader =
-            CreateComputeShaderWithStorageTexture(bindingType, "rgba8", "", "image2DMS");
-        ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Compute,
-                                                      computeShader.c_str()));
+            CreateComputeShaderWithStorageTexture(bindingType, "", "image2DMS");
+        ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
     }
 }
 
diff --git a/src/utils/TextureFormatUtils.cpp b/src/utils/TextureFormatUtils.cpp
index 30d167b..098a42f 100644
--- a/src/utils/TextureFormatUtils.cpp
+++ b/src/utils/TextureFormatUtils.cpp
@@ -516,6 +516,42 @@
                 return "rgba32sint";
             case wgpu::TextureFormat::RGBA32Float:
                 return "rgba32float";
+
+            // The below do not currently exist in the WGSL spec, but are used
+            // for tests that expect compilation failure.
+            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::R16Uint:
+                return "r16uint";
+            case wgpu::TextureFormat::R16Sint:
+                return "r16sint";
+            case wgpu::TextureFormat::R16Float:
+                return "r16float";
+            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::RG16Uint:
+                return "rg16uint";
+            case wgpu::TextureFormat::RG16Sint:
+                return "rg16sint";
+            case wgpu::TextureFormat::RG16Float:
+                return "rg16float";
+            case wgpu::TextureFormat::RGB10A2Unorm:
+                return "rgb10a2unorm";
+            case wgpu::TextureFormat::RG11B10Ufloat:
+                return "rg11b10ufloat";
+
             default:
                 UNREACHABLE();
         }