spirv-reader: support OpImageQuerySizeLod

It queries the dimensions and array levels of a sampled image.

Bug: tint:109
Fixed: tint:423
Change-Id: Ia9ac0ee84b0282dbde8729a1698c9b21943723d2
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/39682
Commit-Queue: David Neto <dneto@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Auto-Submit: David Neto <dneto@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 15e9988..2a0a872 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -4376,15 +4376,20 @@
 
   const auto opcode = inst.opcode();
   switch (opcode) {
-    case SpvOpImageQuerySize: {
+    case SpvOpImageQuerySize:
+    case SpvOpImageQuerySizeLod: {
       ast::ExpressionList exprs;
       // Invoke textureDimensions.
       // If the texture is arrayed, combine with the result from
       // textureNumLayers.
       auto* dims_ident = create<ast::IdentifierExpression>(
           Source{}, builder_.Symbols().Register("textureDimensions"));
-      exprs.push_back(create<ast::CallExpression>(
-          Source{}, dims_ident, ast::ExpressionList{GetImageExpression(inst)}));
+      ast::ExpressionList dims_args{GetImageExpression(inst)};
+      if (opcode == SpvOpImageQuerySizeLod) {
+        dims_args.push_back(ToI32(MakeOperand(inst, 1)).expr);
+      }
+      exprs.push_back(
+          create<ast::CallExpression>(Source{}, dims_ident, dims_args));
       if (type::IsTextureArray(texture_type->dim())) {
         auto* layers_ident = create<ast::IdentifierExpression>(
             Source{}, builder_.Symbols().Register("textureNumLayers"));
@@ -4401,7 +4406,6 @@
       return Fail() << "WGSL does not support querying the level of detail of "
                        "an image: "
                     << inst.PrettyPrint();
-    case SpvOpImageQuerySizeLod:  // TODO(dneto)
     case SpvOpImageQueryLevels:   // TODO(dneto)
     case SpvOpImageQuerySamples:  // TODO(dneto)
     default:
diff --git a/src/reader/spirv/parser_impl_handle_test.cc b/src/reader/spirv/parser_impl_handle_test.cc
index de34c44..e8206d5 100644
--- a/src/reader/spirv/parser_impl_handle_test.cc
+++ b/src/reader/spirv/parser_impl_handle_test.cc
@@ -3924,6 +3924,488 @@
       }
     })"}}));
 
+INSTANTIATE_TEST_SUITE_P(
+    ImageQuerySizeLod_NonArrayed_SignedResult_SignedLevel,
+    // ImageQuerySize requires storage image or multisampled
+    // For storage image, use another instruction to indicate whether it
+    // is readonly or writeonly.
+    SpvParserTest_SampledImageAccessTest,
+    ::testing::ValuesIn(std::vector<ImageAccessCase>{
+
+        // 1D
+        {"%float 1D 0 0 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __sampled_texture_1d__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __i32
+        {
+          TypeConstructor[not set]{
+            __i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // 2D
+        {"%float 2D 0 0 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v2int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __sampled_texture_2d__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_2__i32
+        {
+          TypeConstructor[not set]{
+            __vec_2__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // 3D
+        {"%float 3D 0 0 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __sampled_texture_3d__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_3__i32
+        {
+          TypeConstructor[not set]{
+            __vec_3__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // Cube
+        {"%float Cube 0 0 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __sampled_texture_cube__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_3__i32
+        {
+          TypeConstructor[not set]{
+            __vec_3__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // Depth 2D
+        {"%float 2D 1 0 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v2int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __depth_texture_2d
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_2__i32
+        {
+          TypeConstructor[not set]{
+            __vec_2__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // Depth Cube
+        {"%float Cube 1 0 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __depth_texture_cube
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_3__i32
+        {
+          TypeConstructor[not set]{
+            __vec_3__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+          }
+        }
+      }
+    })"}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    ImageQuerySizeLod_Arrayed_SignedResult_SignedLevel,
+    // ImageQuerySize requires storage image or multisampled
+    // For storage image, use another instruction to indicate whether it
+    // is readonly or writeonly.
+    SpvParserTest_SampledImageAccessTest,
+    ::testing::ValuesIn(std::vector<ImageAccessCase>{
+
+        // 1D arrayed
+        {"%float 1D 0 1 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v2int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __sampled_texture_1d_array__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_2__i32
+        {
+          TypeConstructor[not set]{
+            __vec_2__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+            Call[not set]{
+              Identifier[not set]{textureNumLayers}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // 2D array
+        {"%float 2D 0 1 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __sampled_texture_2d_array__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_3__i32
+        {
+          TypeConstructor[not set]{
+            __vec_3__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+            Call[not set]{
+              Identifier[not set]{textureNumLayers}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // There is no 3D array
+
+        // Cube array
+        {"%float Cube 0 1 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v4int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __sampled_texture_cube_array__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_4__i32
+        {
+          TypeConstructor[not set]{
+            __vec_4__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+            Call[not set]{
+              Identifier[not set]{textureNumLayers}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // Depth 2D array
+        {"%float 2D 1 1 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v3int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __depth_texture_2d_array
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_3__i32
+        {
+          TypeConstructor[not set]{
+            __vec_3__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+            Call[not set]{
+              Identifier[not set]{textureNumLayers}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"},
+
+        // Depth Cube Array
+        {"%float Cube 1 1 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %v4int %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __depth_texture_cube_array
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_4__i32
+        {
+          TypeConstructor[not set]{
+            __vec_4__i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+            Call[not set]{
+              Identifier[not set]{textureNumLayers}
+              (
+                Identifier[not set]{x_20}
+              )
+            }
+          }
+        }
+      }
+    })"}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    // When the level-of-detail value is given as an unsigned
+    // integer, we must convert it before using it as an argument
+    // to textureDimensions.
+    ImageQuerySizeLod_NonArrayed_SignedResult_UnsignedLevel,
+    SpvParserTest_SampledImageAccessTest,
+    ::testing::ValuesIn(std::vector<ImageAccessCase>{
+
+        {"%float 1D 0 0 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %int %im %u1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __sampled_texture_1d__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __i32
+        {
+          TypeConstructor[not set]{
+            __i32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                TypeConstructor[not set]{
+                  __i32
+                  Identifier[not set]{u1}
+                }
+              )
+            }
+          }
+        }
+      }
+    })"}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    // When SPIR-V wants the result type to be unsigned, we have to
+    // insert a type constructor or bitcast for WGSL to do the type
+    // coercion. But the algorithm already does that as a matter
+    // of course.
+    ImageQuerySizeLod_NonArrayed_UnsignedResult_SignedLevel,
+    SpvParserTest_SampledImageAccessTest,
+    ::testing::ValuesIn(std::vector<ImageAccessCase>{
+
+        {"%float 1D 0 0 0 1 Unknown",
+         "%99 = OpImageQuerySizeLod %uint %im %i1\n",
+         R"(Variable{
+    Decorations{
+      GroupDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __sampled_texture_1d__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __u32
+        {
+          TypeConstructor[not set]{
+            __u32
+            Call[not set]{
+              Identifier[not set]{textureDimensions}
+              (
+                Identifier[not set]{x_20}
+                Identifier[not set]{i1}
+              )
+            }
+          }
+        }
+      }
+    })"}}));
+
 struct ImageCoordsCase {
   // SPIR-V image type, excluding result ID and opcode
   std::string spirv_image_type_details;