Add storage texture case reading from read-only then writing into write-only

This case verifies that reading from one read-only storage texture then
writing into another write-only storage texture in one dispatch are
supported in compute shader.

Bug: dawn:458
Change-Id: If1b4c13da067fa39b45a378b54c22a4162695c8d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/23040
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/tests/end2end/StorageTextureTests.cpp b/src/tests/end2end/StorageTextureTests.cpp
index 7c3d799..7b74b52 100644
--- a/src/tests/end2end/StorageTextureTests.cpp
+++ b/src/tests/end2end/StorageTextureTests.cpp
@@ -154,15 +154,16 @@
 
     std::string GetGLSLImageDeclaration(wgpu::TextureFormat format,
                                         std::string accessQualifier,
-                                        bool is2DArray) {
+                                        bool is2DArray,
+                                        uint32_t binding) {
         std::ostringstream ostream;
-        ostream << "layout(set = 0, binding = 0, " << utils::GetGLSLImageFormatQualifier(format)
-                << ") uniform " << accessQualifier << " "
-                << utils::GetColorTextureComponentTypePrefix(format) << "image2D";
+        ostream << "layout(set = 0, binding = " << binding << ", "
+                << utils::GetGLSLImageFormatQualifier(format) << ") uniform " << accessQualifier
+                << " " << utils::GetColorTextureComponentTypePrefix(format) << "image2D";
         if (is2DArray) {
             ostream << "Array";
         }
-        ostream << " storageImage;";
+        ostream << " storageImage" << binding << ";";
         return ostream.str();
     }
 
@@ -273,13 +274,13 @@
 
         const char* prefix = utils::GetColorTextureComponentTypePrefix(format);
 
-        ostream << GetGLSLImageDeclaration(format, "readonly", is2DArray) << "\n"
+        ostream << GetGLSLImageDeclaration(format, "readonly", is2DArray, 0) << "\n"
                 << GetGLSLComparisonFunction(format) << "bool doTest() {\n";
         if (is2DArray) {
-            ostream << R"(ivec3 size = imageSize(storageImage);
+            ostream << R"(ivec3 size = imageSize(storageImage0);
                           const uint layerCount = size.z;)";
         } else {
-            ostream << R"(ivec2 size = imageSize(storageImage);
+            ostream << R"(ivec2 size = imageSize(storageImage0);
                           const uint layerCount = 1;)";
         }
         ostream << R"(for (uint layer = 0; layer < layerCount; ++layer) {
@@ -288,7 +289,7 @@
                                   uint value = )"
                 << kComputeExpectedValueGLSL << ";\n"
                 << prefix << "vec4 expected = " << GetExpectedPixelValue(format) << ";\n"
-                << prefix << R"(vec4 pixel = imageLoad(storageImage, )";
+                << prefix << R"(vec4 pixel = imageLoad(storageImage0, )";
         if (is2DArray) {
             ostream << "ivec3(x, y, layer));";
         } else {
@@ -314,16 +315,16 @@
 
         ostream << R"(
             #version 450
-        )" << GetGLSLImageDeclaration(format, "writeonly", is2DArray)
+        )" << GetGLSLImageDeclaration(format, "writeonly", is2DArray, 0)
                 << R"(
             void main() {
         )";
         if (is2DArray) {
-            ostream << R"(ivec3 size = imageSize(storageImage);
+            ostream << R"(ivec3 size = imageSize(storageImage0);
                           const uint layerCount = size.z;
             )";
         } else {
-            ostream << R"(ivec2 size = imageSize(storageImage);
+            ostream << R"(ivec2 size = imageSize(storageImage0);
                           const uint layerCount = 1;
             )";
         }
@@ -340,7 +341,7 @@
             ostream << "ivec2 texcoord = ivec2(x, y);\n";
         }
 
-        ostream << R"(           imageStore(storageImage, texcoord, expected);
+        ostream << R"(           imageStore(storageImage0, texcoord, expected);
                              }
                          }
                      }
@@ -349,6 +350,44 @@
         return ostream.str();
     }
 
+    std::string CommonReadWriteTestCode(wgpu::TextureFormat format, bool is2DArray = false) {
+        std::ostringstream ostream;
+
+        ostream << R"(
+        #version 450
+        )" << GetGLSLImageDeclaration(format, "writeonly", is2DArray, 0)
+                << GetGLSLImageDeclaration(format, "readonly", is2DArray, 1) << R"(
+            void main() {
+        )";
+        if (is2DArray) {
+            ostream << R"(ivec3 size = imageSize(storageImage0);
+                          const uint layerCount = size.z;
+            )";
+        } else {
+            ostream << R"(ivec2 size = imageSize(storageImage0);
+                          const uint layerCount = 1;
+            )";
+        }
+
+        ostream << R"(for (uint layer = 0; layer < layerCount; ++layer) {
+                          for (uint y = 0; y < size.y; ++y) {
+                              for (uint x = 0; x < size.x; ++x) {)"
+                   "\n";
+        if (is2DArray) {
+            ostream << "ivec3 texcoord = ivec3(x, y, layer);\n";
+        } else {
+            ostream << "ivec2 texcoord = ivec2(x, y);\n";
+        }
+
+        ostream
+            << R"(           imageStore(storageImage0, texcoord, imageLoad(storageImage1, texcoord));
+                             }
+                         }
+                     }
+                 })";
+        return ostream.str();
+    }
+
     static std::vector<uint8_t> GetExpectedData(wgpu::TextureFormat format,
                                                 uint32_t arrayLayerCount = 1) {
         const uint32_t texelSizeInBytes = utils::GetTexelBlockSizeInBytes(format);
@@ -570,6 +609,25 @@
         queue.Submit(1, &commandBuffer);
     }
 
+    void ReadWriteIntoStorageTextureInComputePass(wgpu::Texture readonlyStorageTexture,
+                                                  wgpu::Texture writeonlyStorageTexture,
+                                                  const char* computeShader) {
+        // Create a compute pipeline that writes the expected pixel values into the storage texture.
+        wgpu::ComputePipeline pipeline = CreateComputePipeline(computeShader);
+        wgpu::BindGroup bindGroup = utils::MakeBindGroup(
+            device, pipeline.GetBindGroupLayout(0),
+            {{0, writeonlyStorageTexture.CreateView()}, {1, readonlyStorageTexture.CreateView()}});
+
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        wgpu::ComputePassEncoder computePassEncoder = encoder.BeginComputePass();
+        computePassEncoder.SetBindGroup(0, bindGroup);
+        computePassEncoder.SetPipeline(pipeline);
+        computePassEncoder.Dispatch(1);
+        computePassEncoder.EndPass();
+        wgpu::CommandBuffer commandBuffer = encoder.Finish();
+        queue.Submit(1, &commandBuffer);
+    }
+
     void CheckOutputStorageTexture(wgpu::Texture writeonlyStorageTexture,
                                    wgpu::TextureFormat format,
                                    uint32_t arrayLayerCount = 1) {
@@ -818,6 +876,45 @@
     }
 }
 
+// Test that reading from one read-only storage texture then writing into another write-only storage
+// texture in one dispatch are supported in compute shader.
+TEST_P(StorageTextureTests, ReadWriteDifferentStorageTextureInOneDispatchInComputeShader) {
+    // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
+    // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
+    // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
+    // bug in spvc parser is fixed.
+    DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed());
+
+    for (wgpu::TextureFormat format : utils::kAllTextureFormats) {
+        if (!utils::TextureFormatSupportsStorageTexture(format)) {
+            continue;
+        }
+
+        // TODO(jiawei.shao@intel.com): investigate why this test fails with RGBA8Snorm on Linux
+        // Intel OpenGL driver.
+        if (format == wgpu::TextureFormat::RGBA8Snorm && IsIntel() && IsOpenGL() && IsLinux()) {
+            continue;
+        }
+
+        // Prepare the read-only storage texture.
+        const std::vector<uint8_t> kInitialTextureData = GetExpectedData(format);
+        wgpu::Texture readonlyStorageTexture =
+            CreateTextureWithTestData(kInitialTextureData, format);
+
+        // Prepare the write-only storage texture.
+        wgpu::Texture writeonlyStorageTexture =
+            CreateTexture(format, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc);
+
+        // Write the expected pixel values into the write-only storage texture.
+        const std::string computeShader = CommonReadWriteTestCode(format);
+        ReadWriteIntoStorageTextureInComputePass(readonlyStorageTexture, writeonlyStorageTexture,
+                                                 computeShader.c_str());
+
+        // Verify the pixel data in the write-only storage texture is expected.
+        CheckOutputStorageTexture(writeonlyStorageTexture, format);
+    }
+}
+
 // Test that write-only storage textures are supported in fragment shader.
 TEST_P(StorageTextureTests, WriteonlyStorageTextureInFragmentShader) {
     // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a