spirv-reader: support OpImageDrefGather

Also, issue an error when a gather or dref-gather operation
is used with a Bias or Grad image operand.

Fixed: tint:1336
Change-Id: Ife11d2f52a1a2d1b75e26269373db5cc4b3440bf
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/74801
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index a51cc24..45b622d 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -5286,9 +5286,24 @@
 
   const auto num_args = inst.NumInOperands();
 
+  // Consumes the depth-reference argument, pushing it onto the end of
+  // the parameter list. Issues a diagnostic and returns false on error.
+  auto consume_dref = [&]() -> bool {
+    if (arg_index < num_args) {
+      params.push_back(MakeOperand(inst, arg_index).expr);
+      arg_index++;
+    } else {
+      return Fail()
+             << "image depth-compare instruction is missing a Dref operand: "
+             << inst.PrettyPrint();
+    }
+    return true;
+  };
+
   std::string builtin_name;
   bool use_level_of_detail_suffix = true;
   bool is_dref_sample = false;
+  bool is_gather_or_dref_gather = false;
   bool is_non_dref_sample = false;
   switch (opcode) {
     case SpvOpImageSampleImplicitLod:
@@ -5304,16 +5319,12 @@
     case SpvOpImageSampleProjDrefExplicitLod:
       is_dref_sample = true;
       builtin_name = "textureSampleCompare";
-      if (arg_index < num_args) {
-        params.push_back(MakeOperand(inst, arg_index).expr);
-        arg_index++;
-      } else {
-        return Fail()
-               << "image depth-compare instruction is missing a Dref operand: "
-               << inst.PrettyPrint();
+      if (!consume_dref()) {
+        return false;
       }
       break;
     case SpvOpImageGather:
+      is_gather_or_dref_gather = true;
       builtin_name = "textureGather";
       if (!texture_type->Is<DepthTexture>()) {
         // The explicit component is the *first* argument in WGSL.
@@ -5323,7 +5334,12 @@
       arg_index++;
       break;
     case SpvOpImageDrefGather:
-      return Fail() << " image dref gather is not yet supported";
+      is_gather_or_dref_gather = true;
+      builtin_name = "textureGatherCompare";
+      if (!consume_dref()) {
+        return false;
+      }
+      break;
     case SpvOpImageFetch:
     case SpvOpImageRead:
       // Read a single texel from a sampled or storage image.
@@ -5367,6 +5383,11 @@
                        "level-of-detail bias: "
                     << inst.PrettyPrint();
     }
+    if (is_gather_or_dref_gather) {
+      return Fail() << "WGSL does not support image gather with "
+                       "level-of-detail bias: "
+                    << inst.PrettyPrint();
+    }
     builtin_name += "Bias";
     params.push_back(MakeOperand(inst, arg_index).expr);
     image_operands_mask ^= SpvImageOperandsBiasMask;
@@ -5376,9 +5397,11 @@
     if (use_level_of_detail_suffix) {
       builtin_name += "Level";
     }
-    if (is_dref_sample) {
+    if (is_dref_sample || is_gather_or_dref_gather) {
       // Metal only supports Lod = 0 for comparison sampling without
       // derivatives.
+      // Vulkan SPIR-V does not allow Lod with OpImageGather or
+      // OpImageDrefGather.
       if (!IsFloatZero(inst.GetSingleWordInOperand(arg_index))) {
         return Fail() << "WGSL comparison sampling without derivatives "
                          "requires level-of-detail 0.0"
@@ -5412,6 +5435,11 @@
                        "explicit gradient: "
                     << inst.PrettyPrint();
     }
+    if (is_gather_or_dref_gather) {
+      return Fail() << "WGSL does not support image gather with "
+                       "explicit gradient: "
+                    << inst.PrettyPrint();
+    }
     builtin_name += "Grad";
     params.push_back(MakeOperand(inst, arg_index).expr);
     params.push_back(MakeOperand(inst, arg_index + 1).expr);
@@ -5475,8 +5503,8 @@
     //   compare sample      f32   DrefImplicitLod      f32
     //   compare sample      f32   DrefExplicitLod      f32
     //   texel load          vec4  ImageFetch           f32
-    //   normal gather       vec4  ImageGather          vec4 TODO(dneto)
-    //   dref gather         vec4  ImageFetch           vec4 TODO(dneto)
+    //   normal gather       vec4  ImageGather          vec4
+    //   dref gather         vec4  ImageDrefGather      vec4
     // Construct a 4-element vector with the result from the builtin in the
     // first component.
     if (texture_type->IsAnyOf<DepthTexture, DepthMultisampledTexture>()) {
diff --git a/src/reader/spirv/parser_impl_handle_test.cc b/src/reader/spirv/parser_impl_handle_test.cc
index 5d99c18..ef2eeb0 100644
--- a/src/reader/spirv/parser_impl_handle_test.cc
+++ b/src/reader/spirv/parser_impl_handle_test.cc
@@ -929,12 +929,6 @@
   EXPECT_THAT(su.to_str(), Eq(GetParam().expected_sampler_usage));
   EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
 
-  // TODO(dneto): remove this. crbug.com/tint/1336
-  if (inst.find("Gather") != std::string::npos) {
-    // WGSL does not support Gather instructions yet.
-    // So don't emit them as part of a "passing" corpus.
-    p->DeliberatelyInvalidSpirv();
-  }
   if (inst.find("ImageQueryLod") != std::string::npos) {
     // WGSL does not support querying image level of detail.
     // So don't emit them as part of a "passing" corpus.
@@ -987,12 +981,6 @@
   EXPECT_THAT(su.to_str(), Eq(GetParam().expected_sampler_usage));
   EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
 
-  // TODO(dneto): remove this. crbug.com/tint/1336
-  if (inst.find("Gather") != std::string::npos) {
-    // WGSL does not support Gather instructions yet.
-    // So don't emit them as part of a "passing" corpus.
-    p->DeliberatelyInvalidSpirv();
-  }
   if (inst.find("ImageQueryLod") != std::string::npos) {
     // WGSL does not support querying image level of detail.
     // So don't emit them as part of a "passing" corpus.
@@ -1721,15 +1709,87 @@
     ImageDrefGather,
     SpvParserHandleTest_SampledImageAccessTest,
     ::testing::ValuesIn(std::vector<ImageAccessCase>{
-        // TODO(dneto): OpImageDrefGather 2DDepth
-        // TODO(dneto): OpImageDrefGather 2DDepth ConstOffset signed
-        // TODO(dneto): OpImageDrefGather 2DDepth ConstOffset unsigned
-        // TODO(dneto): OpImageDrefGather 2DDepth Array
-        // TODO(dneto): OpImageDrefGather 2DDepth Array ConstOffset signed
-        // TODO(dneto): OpImageDrefGather 2DDepth Array ConstOffset unsigned
-        // TODO(dneto): OpImageDrefGather DepthCube
-        // TODO(dneto): OpImageDrefGather DepthCube Array
-    }));
+        // OpImageDrefGather 2DDepth
+        ImageAccessCase{
+            "%float 2D 1 0 0 1 Unknown",
+            "%result = OpImageDrefGather "
+            "%v4float %sampled_image %coords12 %depth",
+            R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
+
+[[group(2), binding(1)]] var x_20 : texture_depth_2d;)",
+            "textureGatherCompare(x_20, x_10, coords12, 0.200000003)"},
+        // OpImageDrefGather 2DDepth ConstOffset signed
+        ImageAccessCase{
+            "%float 2D 1 0 0 1 Unknown",
+            "%result = OpImageDrefGather "
+            "%v4float %sampled_image %coords12 %depth ConstOffset %offsets2d",
+            R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
+
+[[group(2), binding(1)]] var x_20 : texture_depth_2d;)",
+            "textureGatherCompare(x_20, x_10, coords12, 0.200000003, "
+            "vec2<i32>(3, 4))"},
+        // OpImageDrefGather 2DDepth ConstOffset unsigned
+        ImageAccessCase{
+            "%float 2D 1 0 0 1 Unknown",
+            "%result = OpImageDrefGather "
+            "%v4float %sampled_image %coords12 %depth ConstOffset "
+            "%u_offsets2d",
+            R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
+
+[[group(2), binding(1)]] var x_20 : texture_depth_2d;)",
+            "textureGatherCompare(x_20, x_10, coords12, 0.200000003, "
+            "vec2<i32>(vec2<u32>(3u, 4u)))"},
+        // OpImageDrefGather 2DDepth Array
+        ImageAccessCase{
+            "%float 2D 1 1 0 1 Unknown",
+            "%result = OpImageDrefGather "
+            "%v4float %sampled_image %coords123 %depth",
+            R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
+
+[[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
+            "textureGatherCompare(x_20, x_10, coords123.xy, "
+            "i32(round(coords123.z)), 0.200000003)"},
+        // OpImageDrefGather 2DDepth Array ConstOffset signed
+        ImageAccessCase{
+            "%float 2D 1 1 0 1 Unknown",
+            "%result = OpImageDrefGather "
+            "%v4float %sampled_image %coords123 %depth ConstOffset %offsets2d",
+            R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
+
+[[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
+            "textureGatherCompare(x_20, x_10, coords123.xy, "
+            "i32(round(coords123.z)), 0.200000003, vec2<i32>(3, 4))"},
+        // OpImageDrefGather 2DDepth Array ConstOffset unsigned
+        ImageAccessCase{
+            "%float 2D 1 1 0 1 Unknown",
+            "%result = OpImageDrefGather "
+            "%v4float %sampled_image %coords123 %depth ConstOffset "
+            "%u_offsets2d",
+            R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
+
+[[group(2), binding(1)]] var x_20 : texture_depth_2d_array;)",
+            "textureGatherCompare(x_20, x_10, coords123.xy, "
+            "i32(round(coords123.z)), 0.200000003, "
+            "vec2<i32>(vec2<u32>(3u, 4u)))"},
+        // OpImageDrefGather DepthCube
+        ImageAccessCase{
+            "%float Cube 1 0 0 1 Unknown",
+            "%result = OpImageDrefGather "
+            "%v4float %sampled_image %coords123 %depth",
+            R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
+
+[[group(2), binding(1)]] var x_20 : texture_depth_cube;)",
+            "textureGatherCompare(x_20, x_10, coords123, 0.200000003)"},
+        // OpImageDrefGather DepthCube Array
+        ImageAccessCase{
+            "%float Cube 1 1 0 1 Unknown",
+            "%result = OpImageDrefGather "
+            "%v4float %sampled_image %coords1234 %depth",
+            R"([[group(0), binding(0)]] var x_10 : sampler_comparison;
+
+[[group(2), binding(1)]] var x_20 : texture_depth_cube_array;)",
+            "textureGatherCompare(x_20, x_10, coords1234.xyz, "
+            "i32(round(coords1234.w)), 0.200000003)"}}));
 
 INSTANTIATE_TEST_SUITE_P(
     ImageSampleImplicitLod,
@@ -3776,6 +3836,49 @@
          "WGSL does not support querying the level of detail of an image: ",
          {}}}));
 
+INSTANTIATE_TEST_SUITE_P(
+    ImageGather_Bias_IsError,
+    SpvParserHandleTest_ImageCoordsTest,
+    ::testing::ValuesIn(std::vector<ImageCoordsCase>{
+        {"%float 2D 0 0 0 1 Unknown",
+         "%result = OpImageGather %v4float %sampled_image %vf12 %int_1 "
+         "Bias %float_null",
+         "WGSL does not support image gather with level-of-detail bias: ",
+         {}}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    ImageDrefGather_Bias_IsError,
+    SpvParserHandleTest_ImageCoordsTest,
+    ::testing::ValuesIn(std::vector<ImageCoordsCase>{
+        {"%float 2D 1 0 0 1 Unknown",
+         "%result = OpImageDrefGather %v4float %sampled_image %vf12 %depth "
+         "Bias %float_null",
+         "WGSL does not support image gather with level-of-detail bias: ",
+         {}}}));
+
+// Note: Vulkan SPIR-V ImageGather and ImageDrefGather do not allow explicit
+// Lod. The SPIR-V validator should reject those cases already.
+
+INSTANTIATE_TEST_SUITE_P(
+    ImageGather_Grad_IsError,
+    SpvParserHandleTest_ImageCoordsTest,
+    ::testing::ValuesIn(std::vector<ImageCoordsCase>{
+        {"%float 2D 0 0 0 1 Unknown",
+         "%result = OpImageGather %v4float %sampled_image %vf12 %int_1 "
+         "Grad %vf12 %vf12",
+         "WGSL does not support image gather with explicit gradient: ",
+         {}}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    ImageDrefGather_Grad_IsError,
+    SpvParserHandleTest_ImageCoordsTest,
+    ::testing::ValuesIn(std::vector<ImageCoordsCase>{
+        {"%float 2D 1 0 0 1 Unknown",
+         "%result = OpImageDrefGather %v4float %sampled_image %vf12 %depth "
+         "Grad %vf12 %vf12",
+         "WGSL does not support image gather with explicit gradient: ",
+         {}}}));
+
 TEST_F(SpvParserHandleTest,
        NeverGenerateConstDeclForHandle_UseVariableDirectly) {
   // An ad-hoc test to prove we never had the issue