writers: Duplicate cube height for textureDimensions()

All shader backend languages describe cubes as width / height, but give no way to query depth. WGSL however returns a vec3<i32> for cube textures when calling textureDimensions().
As cube textures must be square (width == height == depth), just replicate the height for the depth.

See https://github.com/gpuweb/gpuweb/issues/1345

Bug: tint:140
Bug: tint:437
Change-Id: I76ef18ee4bd8b53d5f9d9d3f1c10c3f7cb23e137
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/37446
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 7c36f18..d95769f 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -745,7 +745,8 @@
   auto const kNotUsed = ast::intrinsic::TextureSignature::Parameters::kNotUsed;
 
   auto* texture = params[pidx.texture];
-  auto* texture_type = texture->result_type()->UnwrapPtrIfNeeded();
+  auto* texture_type =
+      texture->result_type()->UnwrapPtrIfNeeded()->As<ast::type::Texture>();
 
   if (ident->intrinsic() == ast::Intrinsic::kTextureDimensions) {
     // Declare a variable to hold the texture dimensions
@@ -763,17 +764,29 @@
     if (pidx.level != kNotUsed) {
       pre << pidx.level << ", ";
     }
-    if (auto* vec = expr->result_type()->As<ast::type::Vector>()) {
-      for (uint32_t i = 0; i < vec->size(); i++) {
-        if (i > 0) {
-          pre << ", ";
-        }
-        pre << dims << "[" << i << "]";
-      }
-    } else {
-      pre << dims;
+    switch (texture_type->dim()) {
+      case ast::type::TextureDimension::kNone:
+        error_ = "texture dimension is kNone";
+        return false;
+      case ast::type::TextureDimension::k1d:
+      case ast::type::TextureDimension::k1dArray:
+        pre << dims << ");";
+        break;
+      case ast::type::TextureDimension::k2d:
+      case ast::type::TextureDimension::k2dArray:
+        pre << dims << "[0], " << dims << "[1]);";
+        break;
+      case ast::type::TextureDimension::k3d:
+        pre << dims << "[0], " << dims << "[1], " << dims << "[2]);";
+        break;
+      case ast::type::TextureDimension::kCube:
+      case ast::type::TextureDimension::kCubeArray:
+        // width == height == depth for cubes
+        // See https://github.com/gpuweb/gpuweb/issues/1345
+        pre << dims << "[0], " << dims << "[1]);\n";
+        pre << dims << "[2] = " << dims << "[1];";  // dims[2] = dims[1]
+        break;
     }
-    pre << ");";
 
     // The result of the textureDimensions() call is now the temporary variable.
     out << dims;
diff --git a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
index f9d6448..bd4029b 100644
--- a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
+++ b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
@@ -67,10 +67,6 @@
           "_tint_tmp",
       };
     case ValidTextureOverload::kDimensions3d:
-    case ValidTextureOverload::kDimensionsCube:
-    case ValidTextureOverload::kDimensionsCubeArray:
-    case ValidTextureOverload::kDimensionsDepthCube:
-    case ValidTextureOverload::kDimensionsDepthCubeArray:
     case ValidTextureOverload::kDimensionsStorageRO3d:
     case ValidTextureOverload::kDimensionsStorageWO3d:
       return {
@@ -79,6 +75,16 @@
           "_tint_tmp[2]);",
           "_tint_tmp",
       };
+    case ValidTextureOverload::kDimensionsCube:
+    case ValidTextureOverload::kDimensionsCubeArray:
+    case ValidTextureOverload::kDimensionsDepthCube:
+    case ValidTextureOverload::kDimensionsDepthCubeArray:
+      return {
+          "int3 _tint_tmp;\n"
+          "texture_tint_0.GetDimensions(_tint_tmp[0], _tint_tmp[1]);\n"
+          "_tint_tmp[2] = _tint_tmp[1];",
+          "_tint_tmp",
+      };
     case ValidTextureOverload::kDimensions2dLevel:
     case ValidTextureOverload::kDimensions2dArrayLevel:
     case ValidTextureOverload::kDimensionsDepth2dLevel:
@@ -89,14 +95,20 @@
           "_tint_tmp",
       };
     case ValidTextureOverload::kDimensions3dLevel:
+      return {
+          "int3 _tint_tmp;\n"
+          "texture_tint_0.GetDimensions(1, _tint_tmp[0], _tint_tmp[1], "
+          "_tint_tmp[2]);",
+          "_tint_tmp",
+      };
     case ValidTextureOverload::kDimensionsCubeLevel:
     case ValidTextureOverload::kDimensionsCubeArrayLevel:
     case ValidTextureOverload::kDimensionsDepthCubeLevel:
     case ValidTextureOverload::kDimensionsDepthCubeArrayLevel:
       return {
           "int3 _tint_tmp;\n"
-          "texture_tint_0.GetDimensions(1, _tint_tmp[0], _tint_tmp[1], "
-          "_tint_tmp[2]);",
+          "texture_tint_0.GetDimensions(1, _tint_tmp[0], _tint_tmp[1]);\n"
+          "_tint_tmp[2] = _tint_tmp[1];",
           "_tint_tmp",
       };
     case ValidTextureOverload::kSample1dF32:
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 1769301..5343a11 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -665,7 +665,37 @@
   auto& pidx = signature->params.idx;
   auto const kNotUsed = ast::intrinsic::TextureSignature::Parameters::kNotUsed;
 
+  assert(pidx.texture != kNotUsed);
+  auto* texture_type = params[pidx.texture]
+                           ->result_type()
+                           ->UnwrapAll()
+                           ->As<ast::type::Texture>();
+
   if (ident->intrinsic() == ast::Intrinsic::kTextureDimensions) {
+    std::vector<const char*> dims;
+    switch (texture_type->dim()) {
+      case ast::type::TextureDimension::kNone:
+        error_ = "texture dimension is kNone";
+        return false;
+      case ast::type::TextureDimension::k1d:
+      case ast::type::TextureDimension::k1dArray:
+        dims = {"width"};
+        break;
+      case ast::type::TextureDimension::k2d:
+      case ast::type::TextureDimension::k2dArray:
+        dims = {"width", "height"};
+        break;
+      case ast::type::TextureDimension::k3d:
+        dims = {"width", "height", "depth"};
+        break;
+      case ast::type::TextureDimension::kCube:
+      case ast::type::TextureDimension::kCubeArray:
+        // width == height == depth for cubes
+        // See https://github.com/gpuweb/gpuweb/issues/1345
+        dims = {"width", "height", "height"};
+        break;
+    }
+
     auto get_dim = [&](const char* name) {
       if (!EmitExpression(params[pidx.texture])) {
         return false;
@@ -678,32 +708,18 @@
       return true;
     };
 
-    size_t dims = 1;
-    if (auto* vec = expr->result_type()->As<ast::type::Vector>()) {
-      dims = vec->size();
-    }
-    switch (dims) {
-      case 1:
-        get_dim("width");
-        break;
-      case 2:
-        EmitType(expr->result_type(), "");
-        out_ << "(";
-        get_dim("width");
-        out_ << ", ";
-        get_dim("height");
-        out_ << ")";
-        break;
-      case 3:
-        EmitType(expr->result_type(), "");
-        out_ << "(";
-        get_dim("width");
-        out_ << ", ";
-        get_dim("height");
-        out_ << ", ";
-        get_dim("depth");
-        out_ << ")";
-        break;
+    if (dims.size() == 1) {
+      get_dim(dims[0]);
+    } else {
+      EmitType(expr->result_type(), "");
+      out_ << "(";
+      for (size_t i = 0; i < dims.size(); i++) {
+        if (i > 0) {
+          out_ << ", ";
+        }
+        get_dim(dims[i]);
+      }
+      out_ << ")";
     }
     return true;
   }
diff --git a/src/writer/msl/generator_impl_intrinsic_texture_test.cc b/src/writer/msl/generator_impl_intrinsic_texture_test.cc
index c7921d9..fcc6bbe 100644
--- a/src/writer/msl/generator_impl_intrinsic_texture_test.cc
+++ b/src/writer/msl/generator_impl_intrinsic_texture_test.cc
@@ -51,24 +51,26 @@
     case ValidTextureOverload::kDimensionsStorageWO2dArray:
       return R"(int2(texture_tint_0.get_width(), texture_tint_0.get_height()))";
     case ValidTextureOverload::kDimensions3d:
+    case ValidTextureOverload::kDimensionsStorageRO3d:
+    case ValidTextureOverload::kDimensionsStorageWO3d:
+      return R"(int3(texture_tint_0.get_width(), texture_tint_0.get_height(), texture_tint_0.get_depth()))";
     case ValidTextureOverload::kDimensionsCube:
     case ValidTextureOverload::kDimensionsCubeArray:
     case ValidTextureOverload::kDimensionsDepthCube:
     case ValidTextureOverload::kDimensionsDepthCubeArray:
-    case ValidTextureOverload::kDimensionsStorageRO3d:
-    case ValidTextureOverload::kDimensionsStorageWO3d:
-      return R"(int3(texture_tint_0.get_width(), texture_tint_0.get_height(), texture_tint_0.get_depth()))";
+      return R"(int3(texture_tint_0.get_width(), texture_tint_0.get_height(), texture_tint_0.get_height()))";
     case ValidTextureOverload::kDimensions2dLevel:
     case ValidTextureOverload::kDimensions2dArrayLevel:
     case ValidTextureOverload::kDimensionsDepth2dLevel:
     case ValidTextureOverload::kDimensionsDepth2dArrayLevel:
       return R"(int2(texture_tint_0.get_width(1), texture_tint_0.get_height(1)))";
     case ValidTextureOverload::kDimensions3dLevel:
+      return R"(int3(texture_tint_0.get_width(1), texture_tint_0.get_height(1), texture_tint_0.get_depth(1)))";
     case ValidTextureOverload::kDimensionsCubeLevel:
     case ValidTextureOverload::kDimensionsCubeArrayLevel:
     case ValidTextureOverload::kDimensionsDepthCubeLevel:
     case ValidTextureOverload::kDimensionsDepthCubeArrayLevel:
-      return R"(int3(texture_tint_0.get_width(1), texture_tint_0.get_height(1), texture_tint_0.get_depth(1)))";
+      return R"(int3(texture_tint_0.get_width(1), texture_tint_0.get_height(1), texture_tint_0.get_height(1)))";
     case ValidTextureOverload::kSample1dF32:
       return R"(texture_tint_0.sample(sampler_tint_0, 1.0f))";
     case ValidTextureOverload::kSample1dArrayF32:
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index bd6fda7..68ca025 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -272,6 +272,14 @@
   return 0;
 }
 
+/// @return the vector element type if ty is a vector, otherwise return ty.
+ast::type::Type* ElementTypeOf(ast::type::Type* ty) {
+  if (auto* v = ty->As<ast::type::Vector>()) {
+    return v->type();
+  }
+  return ty;
+}
+
 }  // namespace
 
 Builder::AccessorInfo::AccessorInfo() : source_id(0), source_type(nullptr) {}
@@ -1987,9 +1995,6 @@
                                        ast::CallExpression* call,
                                        Operand result_type,
                                        Operand result_id) {
-  auto* texture_type =
-      call->params()[0]->result_type()->UnwrapAll()->As<ast::type::Texture>();
-
   auto* sig = static_cast<const ast::intrinsic::TextureSignature*>(
       ident->intrinsic_signature());
   assert(sig != nullptr);
@@ -1997,6 +2002,10 @@
   auto const kNotUsed = ast::intrinsic::TextureSignature::Parameters::kNotUsed;
 
   assert(pidx.texture != kNotUsed);
+  auto* texture_type = call->params()[pidx.texture]
+                           ->result_type()
+                           ->UnwrapAll()
+                           ->As<ast::type::Texture>();
 
   auto op = spv::Op::OpNop;
 
@@ -2070,50 +2079,76 @@
 
   switch (ident->intrinsic()) {
     case ast::Intrinsic::kTextureDimensions: {
-      if (ast::type::IsTextureArray(texture_type->dim())) {
-        // OpImageQuerySize[Lod] will append another element to the returned
-        // vector describing the number of array elements. textureDimensions()
-        // does not include this in the returned vector, so it needs to be
-        // stripped from the resulting vector.
-        auto unstripped_result = result_op();
+      // Number of returned elements from OpImageQuerySize[Lod] may not match
+      // those of textureDimensions().
+      // This might be due to an extra vector scalar describing the number of
+      // array elements or textureDimensions() returning a vec3 for cubes
+      // when only width / height is returned by OpImageQuerySize[Lod]
+      // (see https://github.com/gpuweb/gpuweb/issues/1345).
+      // Handle these mismatches by swizzling the returned vector.
+      std::vector<uint32_t> swizzle;
+      uint32_t spirv_dims = 0;
+      switch (texture_type->dim()) {
+        case ast::type::TextureDimension::kNone:
+          error_ = "texture dimension is kNone";
+          return false;
+        case ast::type::TextureDimension::k1d:
+        case ast::type::TextureDimension::k2d:
+        case ast::type::TextureDimension::k3d:
+          break;  // No swizzle needed
+        case ast::type::TextureDimension::k1dArray:
+          swizzle = {0};   // Strip array index
+          spirv_dims = 2;  // [width, array count]
+          break;
+        case ast::type::TextureDimension::kCube:
+          swizzle = {0, 1, 1};  // Duplicate height for depth
+          spirv_dims = 2;       // [width, height]
+          break;
+        case ast::type::TextureDimension::k2dArray:
+          swizzle = {0, 1};  // Strip array index
+          spirv_dims = 3;    // [width, height, array_count]
+          break;
+        case ast::type::TextureDimension::kCubeArray:
+          swizzle = {0, 1, 1};  // Strip array index, duplicate height for depth
+          spirv_dims = 3;       // [width, height, array_count]
+          break;
+      }
 
-        ast::type::Type* unstripped_result_type;
-        if (auto* v = call->result_type()->As<ast::type::Vector>()) {
-          unstripped_result_type =
-              mod_->create<ast::type::Vector>(v->type(), v->size() + 1);
+      if (swizzle.empty()) {
+        append_result_type_and_id_to_spirv_params();
+      } else {
+        // Assign post_emission to swizzle the result of the call to
+        // OpImageQuerySize[Lod].
+        auto* element_type = ElementTypeOf(call->result_type());
+        auto spirv_result = result_op();
+        auto* spirv_result_type =
+            mod_->create<ast::type::Vector>(element_type, spirv_dims);
+        if (swizzle.size() > 1) {
           post_emission = [=] {
-            // Swizzle the unstripped vector to form a vec2 or vec3
             OperandList operands{
                 result_type,
                 result_id,
-                unstripped_result,
-                unstripped_result,
+                spirv_result,
+                spirv_result,
             };
-            for (uint32_t i = 0; i < v->size(); i++) {
-              operands.emplace_back(Operand::Int(i));
+            for (auto idx : swizzle) {
+              operands.emplace_back(Operand::Int(idx));
             }
             return push_function_inst(spv::Op::OpVectorShuffle, operands);
           };
         } else {
-          unstripped_result_type =
-              mod_->create<ast::type::Vector>(call->result_type(), 2);
           post_emission = [=] {
-            // Extract the first element of the unstripped vec2 to form a scalar
-            return push_function_inst(
-                spv::Op::OpCompositeExtract,
-                {result_type, result_id, unstripped_result, Operand::Int(0)});
+            return push_function_inst(spv::Op::OpCompositeExtract,
+                                      {result_type, result_id, spirv_result,
+                                       Operand::Int(swizzle[0])});
           };
         }
-
-        auto unstripped_result_type_id =
-            GenerateTypeIfNeeded(unstripped_result_type);
-        if (unstripped_result_type_id == 0) {
+        auto spirv_result_type_id = GenerateTypeIfNeeded(spirv_result_type);
+        if (spirv_result_type_id == 0) {
           return false;
         }
-        spirv_params.emplace_back(Operand::Int(unstripped_result_type_id));
-        spirv_params.emplace_back(unstripped_result);
-      } else {
-        append_result_type_and_id_to_spirv_params();
+        spirv_params.emplace_back(Operand::Int(spirv_result_type_id));
+        spirv_params.emplace_back(spirv_result);
       }
 
       spirv_params.emplace_back(gen_param(pidx.texture));
diff --git a/src/writer/spirv/builder_intrinsic_texture_test.cc b/src/writer/spirv/builder_intrinsic_texture_test.cc
index 760af94..a86ccb2 100644
--- a/src/writer/spirv/builder_intrinsic_texture_test.cc
+++ b/src/writer/spirv/builder_intrinsic_texture_test.cc
@@ -226,11 +226,13 @@
 %5 = OpVariable %6 UniformConstant
 %10 = OpTypeInt 32 1
 %9 = OpTypeVector %10 3
-%12 = OpConstant %10 0
+%12 = OpTypeVector %10 2
+%14 = OpConstant %10 0
 )",
           R"(
-%11 = OpLoad %3 %1
-%8 = OpImageQuerySizeLod %9 %11 %12
+%13 = OpLoad %3 %1
+%11 = OpImageQuerySizeLod %12 %13 %14
+%8 = OpVectorShuffle %9 %11 %11 0 1 1
 )",
           R"(
 OpCapability ImageQuery
@@ -247,11 +249,13 @@
 %5 = OpVariable %6 UniformConstant
 %10 = OpTypeInt 32 1
 %9 = OpTypeVector %10 3
-%12 = OpConstant %10 1
+%12 = OpTypeVector %10 2
+%14 = OpConstant %10 1
 )",
           R"(
-%11 = OpLoad %3 %1
-%8 = OpImageQuerySizeLod %9 %11 %12
+%13 = OpLoad %3 %1
+%11 = OpImageQuerySizeLod %12 %13 %14
+%8 = OpVectorShuffle %9 %11 %11 0 1 1
 )",
           R"(
 OpCapability ImageQuery
@@ -268,13 +272,12 @@
 %5 = OpVariable %6 UniformConstant
 %10 = OpTypeInt 32 1
 %9 = OpTypeVector %10 3
-%12 = OpTypeVector %10 4
-%14 = OpConstant %10 0
+%13 = OpConstant %10 0
 )",
           R"(
-%13 = OpLoad %3 %1
-%11 = OpImageQuerySizeLod %12 %13 %14
-%8 = OpVectorShuffle %9 %11 %11 0 1 2
+%12 = OpLoad %3 %1
+%11 = OpImageQuerySizeLod %9 %12 %13
+%8 = OpVectorShuffle %9 %11 %11 0 1 1
 )",
           R"(
 OpCapability SampledCubeArray
@@ -292,13 +295,12 @@
 %5 = OpVariable %6 UniformConstant
 %10 = OpTypeInt 32 1
 %9 = OpTypeVector %10 3
-%12 = OpTypeVector %10 4
-%14 = OpConstant %10 1
+%13 = OpConstant %10 1
 )",
           R"(
-%13 = OpLoad %3 %1
-%11 = OpImageQuerySizeLod %12 %13 %14
-%8 = OpVectorShuffle %9 %11 %11 0 1 2
+%12 = OpLoad %3 %1
+%11 = OpImageQuerySizeLod %9 %12 %13
+%8 = OpVectorShuffle %9 %11 %11 0 1 1
 )",
           R"(
 OpCapability SampledCubeArray
@@ -446,11 +448,13 @@
 %5 = OpVariable %6 UniformConstant
 %10 = OpTypeInt 32 1
 %9 = OpTypeVector %10 3
-%12 = OpConstant %10 0
+%12 = OpTypeVector %10 2
+%14 = OpConstant %10 0
 )",
           R"(
-%11 = OpLoad %3 %1
-%8 = OpImageQuerySizeLod %9 %11 %12
+%13 = OpLoad %3 %1
+%11 = OpImageQuerySizeLod %12 %13 %14
+%8 = OpVectorShuffle %9 %11 %11 0 1 1
 )",
           R"(
 OpCapability ImageQuery
@@ -467,11 +471,13 @@
 %5 = OpVariable %6 UniformConstant
 %10 = OpTypeInt 32 1
 %9 = OpTypeVector %10 3
-%12 = OpConstant %10 1
+%12 = OpTypeVector %10 2
+%14 = OpConstant %10 1
 )",
           R"(
-%11 = OpLoad %3 %1
-%8 = OpImageQuerySizeLod %9 %11 %12
+%13 = OpLoad %3 %1
+%11 = OpImageQuerySizeLod %12 %13 %14
+%8 = OpVectorShuffle %9 %11 %11 0 1 1
 )",
           R"(
 OpCapability ImageQuery
@@ -488,13 +494,12 @@
 %5 = OpVariable %6 UniformConstant
 %10 = OpTypeInt 32 1
 %9 = OpTypeVector %10 3
-%12 = OpTypeVector %10 4
-%14 = OpConstant %10 0
+%13 = OpConstant %10 0
 )",
           R"(
-%13 = OpLoad %3 %1
-%11 = OpImageQuerySizeLod %12 %13 %14
-%8 = OpVectorShuffle %9 %11 %11 0 1 2
+%12 = OpLoad %3 %1
+%11 = OpImageQuerySizeLod %9 %12 %13
+%8 = OpVectorShuffle %9 %11 %11 0 1 1
 )",
           R"(
 OpCapability SampledCubeArray
@@ -512,13 +517,12 @@
 %5 = OpVariable %6 UniformConstant
 %10 = OpTypeInt 32 1
 %9 = OpTypeVector %10 3
-%12 = OpTypeVector %10 4
-%14 = OpConstant %10 1
+%13 = OpConstant %10 1
 )",
           R"(
-%13 = OpLoad %3 %1
-%11 = OpImageQuerySizeLod %12 %13 %14
-%8 = OpVectorShuffle %9 %11 %11 0 1 2
+%12 = OpLoad %3 %1
+%11 = OpImageQuerySizeLod %9 %12 %13
+%8 = OpVectorShuffle %9 %11 %11 0 1 1
 )",
           R"(
 OpCapability SampledCubeArray