Implement textureNumLayers()

SPIR-V reader TODO

Bug: tint:140
Bug: tint:437
Change-Id: Id397f5f07a2f18f365dc9c2d588e619cea8f89dc
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/37844
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/ast/intrinsic.cc b/src/ast/intrinsic.cc
index 73f3ab3..3f6f8ac 100644
--- a/src/ast/intrinsic.cc
+++ b/src/ast/intrinsic.cc
@@ -21,211 +21,220 @@
   /// The emitted name matches the spelling in the WGSL spec.
   /// including case.
   switch (i) {
+    case Intrinsic::kNone:
+      return out;
     case Intrinsic::kAbs:
       out << "abs";
-      break;
+      return out;
     case Intrinsic::kAcos:
       out << "acos";
-      break;
+      return out;
     case Intrinsic::kAll:
       out << "all";
-      break;
+      return out;
     case Intrinsic::kAny:
       out << "any";
-      break;
+      return out;
     case Intrinsic::kArrayLength:
       out << "arrayLength";
-      break;
+      return out;
     case Intrinsic::kAsin:
       out << "asin";
-      break;
+      return out;
     case Intrinsic::kAtan:
       out << "atan";
-      break;
+      return out;
     case Intrinsic::kAtan2:
       out << "atan2";
-      break;
+      return out;
     case Intrinsic::kCeil:
       out << "ceil";
-      break;
+      return out;
     case Intrinsic::kClamp:
       out << "clamp";
-      break;
+      return out;
     case Intrinsic::kCos:
       out << "cos";
-      break;
+      return out;
     case Intrinsic::kCosh:
       out << "cosh";
-      break;
+      return out;
     case Intrinsic::kCountOneBits:
       out << "countOneBits";
-      break;
+      return out;
     case Intrinsic::kCross:
       out << "cross";
-      break;
+      return out;
     case Intrinsic::kDeterminant:
       out << "determinant";
-      break;
+      return out;
     case Intrinsic::kDistance:
       out << "distance";
-      break;
+      return out;
     case Intrinsic::kDot:
       out << "dot";
-      break;
+      return out;
     case Intrinsic::kDpdx:
       out << "dpdx";
-      break;
+      return out;
     case Intrinsic::kDpdxCoarse:
       out << "dpdxCoarse";
-      break;
+      return out;
     case Intrinsic::kDpdxFine:
       out << "dpdxFine";
-      break;
+      return out;
     case Intrinsic::kDpdy:
       out << "dpdy";
-      break;
+      return out;
     case Intrinsic::kDpdyCoarse:
       out << "dpdyCoarse";
-      break;
+      return out;
     case Intrinsic::kDpdyFine:
       out << "dpdyFine";
-      break;
+      return out;
     case Intrinsic::kExp:
       out << "exp";
-      break;
+      return out;
     case Intrinsic::kExp2:
       out << "exp2";
-      break;
+      return out;
     case Intrinsic::kFaceForward:
       out << "faceForward";
-      break;
+      return out;
     case Intrinsic::kFloor:
       out << "floor";
-      break;
+      return out;
     case Intrinsic::kFma:
       out << "fma";
-      break;
+      return out;
     case Intrinsic::kFract:
       out << "fract";
-      break;
+      return out;
     case Intrinsic::kFrexp:
       out << "frexp";
-      break;
+      return out;
     case Intrinsic::kFwidth:
       out << "fwidth";
-      break;
+      return out;
     case Intrinsic::kFwidthCoarse:
       out << "fwidthCoarse";
-      break;
+      return out;
     case Intrinsic::kFwidthFine:
       out << "fwidthFine";
-      break;
+      return out;
     case Intrinsic::kInverseSqrt:
       out << "inverseSqrt";
-      break;
+      return out;
     case Intrinsic::kIsFinite:
       out << "isFinite";
-      break;
+      return out;
     case Intrinsic::kIsInf:
       out << "isInf";
-      break;
+      return out;
     case Intrinsic::kIsNan:
       out << "isNan";
-      break;
+      return out;
     case Intrinsic::kIsNormal:
       out << "isNormal";
-      break;
+      return out;
     case Intrinsic::kLdexp:
       out << "ldexp";
-      break;
+      return out;
     case Intrinsic::kLength:
       out << "length";
-      break;
+      return out;
     case Intrinsic::kLog:
       out << "log";
-      break;
+      return out;
     case Intrinsic::kLog2:
       out << "log2";
-      break;
+      return out;
     case Intrinsic::kMax:
       out << "max";
-      break;
+      return out;
     case Intrinsic::kMin:
       out << "min";
-      break;
+      return out;
     case Intrinsic::kMix:
       out << "mix";
-      break;
+      return out;
     case Intrinsic::kModf:
       out << "modf";
-      break;
+      return out;
     case Intrinsic::kNormalize:
       out << "normalize";
-      break;
+      return out;
     case Intrinsic::kPow:
       out << "pow";
-      break;
+      return out;
     case Intrinsic::kReflect:
       out << "reflect";
-      break;
+      return out;
     case Intrinsic::kReverseBits:
       out << "reverseBits";
-      break;
+      return out;
     case Intrinsic::kRound:
       out << "round";
-      break;
+      return out;
     case Intrinsic::kSelect:
       out << "select";
-      break;
+      return out;
     case Intrinsic::kSign:
       out << "sign";
-      break;
+      return out;
     case Intrinsic::kSin:
       out << "sin";
-      break;
+      return out;
     case Intrinsic::kSinh:
       out << "sinh";
-      break;
+      return out;
     case Intrinsic::kSmoothStep:
       out << "smoothStep";
-      break;
+      return out;
     case Intrinsic::kSqrt:
       out << "sqrt";
-      break;
+      return out;
     case Intrinsic::kStep:
       out << "step";
-      break;
+      return out;
     case Intrinsic::kTan:
       out << "tan";
-      break;
+      return out;
     case Intrinsic::kTanh:
       out << "tanh";
-      break;
+      return out;
+    case Intrinsic::kTextureDimensions:
+      out << "textureDimensions";
+      return out;
     case Intrinsic::kTextureLoad:
       out << "textureLoad";
-      break;
+      return out;
+    case Intrinsic::kTextureNumLayers:
+      out << "textureNumLayers";
+      return out;
     case Intrinsic::kTextureSample:
       out << "textureSample";
-      break;
+      return out;
     case Intrinsic::kTextureSampleBias:
       out << "textureSampleBias";
-      break;
+      return out;
     case Intrinsic::kTextureSampleCompare:
       out << "textureSampleCompare";
-      break;
+      return out;
     case Intrinsic::kTextureSampleGrad:
       out << "textureSampleGrad";
-      break;
+      return out;
     case Intrinsic::kTextureSampleLevel:
       out << "textureSampleLevel";
-      break;
+      return out;
+    case Intrinsic::kTextureStore:
+      out << "textureStore";
+      return out;
     case Intrinsic::kTrunc:
       out << "trunc";
-      break;
-    default:
-      out << "Unknown";
-      break;
+      return out;
   }
+  out << "Unknown";
   return out;
 }
 
@@ -260,7 +269,7 @@
 
 bool IsTextureIntrinsic(Intrinsic i) {
   return i == Intrinsic::kTextureDimensions || i == Intrinsic::kTextureLoad ||
-         i == Intrinsic::kTextureSample ||
+         i == Intrinsic::kTextureNumLayers || i == Intrinsic::kTextureSample ||
          i == Intrinsic::kTextureSampleLevel ||
          i == Intrinsic::kTextureSampleBias ||
          i == Intrinsic::kTextureSampleCompare ||
@@ -268,7 +277,8 @@
 }
 
 bool IsImageQueryIntrinsic(Intrinsic i) {
-  return i == ast::Intrinsic::kTextureDimensions;
+  return i == ast::Intrinsic::kTextureDimensions ||
+         i == Intrinsic::kTextureNumLayers;
 }
 
 }  // namespace intrinsic
diff --git a/src/ast/intrinsic.h b/src/ast/intrinsic.h
index 9de55b1..36eb5ad 100644
--- a/src/ast/intrinsic.h
+++ b/src/ast/intrinsic.h
@@ -85,6 +85,7 @@
   kTanh,
   kTextureDimensions,
   kTextureLoad,
+  kTextureNumLayers,
   kTextureSample,
   kTextureSampleBias,
   kTextureSampleCompare,
diff --git a/src/ast/intrinsic_texture_helper_test.cc b/src/ast/intrinsic_texture_helper_test.cc
index 7d06193..ac3c61a 100644
--- a/src/ast/intrinsic_texture_helper_test.cc
+++ b/src/ast/intrinsic_texture_helper_test.cc
@@ -542,6 +542,86 @@
           [](Builder* b) { return b->ExprList("texture"); },
       },
       {
+          ValidTextureOverload::kNumLayers1dArray,
+          "textureNumLayers(t : texture_1d_array<T>) -> i32",
+          TextureKind::kRegular,
+          type::SamplerKind::kSampler,
+          type::TextureDimension::k1dArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayers2dArray,
+          "textureNumLayers(t : texture_2d_array<T>) -> i32",
+          TextureKind::kRegular,
+          type::SamplerKind::kSampler,
+          type::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersCubeArray,
+          "textureNumLayers(t : texture_cube_array<T>) -> i32",
+          TextureKind::kRegular,
+          type::SamplerKind::kSampler,
+          type::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersMultisampled_2dArray,
+          "textureNumLayers(t : texture_multisampled_2d_array<T>) -> i32",
+          TextureKind::kMultisampled,
+          type::SamplerKind::kSampler,
+          type::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersDepth2dArray,
+          "textureNumLayers(t : texture_depth_2d_array) -> i32",
+          TextureKind::kDepth,
+          type::SamplerKind::kSampler,
+          type::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersDepthCubeArray,
+          "textureNumLayers(t : texture_depth_cube_array) -> i32",
+          TextureKind::kDepth,
+          type::SamplerKind::kSampler,
+          type::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersStorageWO1dArray,
+          "textureNumLayers(t : texture_storage_1d_array<F>) -> i32",
+          ast::AccessControl::kWriteOnly,
+          ast::type::ImageFormat::kRgba32Float,
+          type::TextureDimension::k1dArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersStorageWO2dArray,
+          "textureNumLayers(t : texture_storage_2d_array<F>) -> i32",
+          ast::AccessControl::kWriteOnly,
+          ast::type::ImageFormat::kRgba32Float,
+          type::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
           ValidTextureOverload::kSample1dF32,
           "textureSample(t      : texture_1d<f32>,\n"
           "              s      : sampler,\n"
diff --git a/src/ast/intrinsic_texture_helper_test.h b/src/ast/intrinsic_texture_helper_test.h
index fae5a2a..ca0d655 100644
--- a/src/ast/intrinsic_texture_helper_test.h
+++ b/src/ast/intrinsic_texture_helper_test.h
@@ -69,6 +69,14 @@
   kDimensionsStorageWO2d,
   kDimensionsStorageWO2dArray,
   kDimensionsStorageWO3d,
+  kNumLayers1dArray,
+  kNumLayers2dArray,
+  kNumLayersCubeArray,
+  kNumLayersMultisampled_2dArray,
+  kNumLayersDepth2dArray,
+  kNumLayersDepthCubeArray,
+  kNumLayersStorageWO1dArray,
+  kNumLayersStorageWO2dArray,
   kSample1dF32,
   kSample1dArrayF32,
   kSample2dF32,
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 8f3f64b..57ba101 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -566,6 +566,9 @@
           param.idx.level = param.count++;
         }
         break;
+      case ast::Intrinsic::kTextureNumLayers:
+        param.idx.texture = param.count++;
+        break;
       case ast::Intrinsic::kTextureLoad:
         param.idx.texture = param.count++;
         param.idx.coords = param.count++;
@@ -669,46 +672,54 @@
 
     // Set the function return type
     ast::type::Type* return_type = nullptr;
-    if (ident->intrinsic() == ast::Intrinsic::kTextureDimensions) {
-      auto* i32 = mod_->create<ast::type::I32>();
-      switch (texture->dim()) {
-        default:
-          set_error(expr->source(), "invalid texture dimensions");
-          break;
-        case ast::type::TextureDimension::k1d:
-        case ast::type::TextureDimension::k1dArray:
-          return_type = i32;
-          break;
-        case ast::type::TextureDimension::k2d:
-        case ast::type::TextureDimension::k2dArray:
-          return_type = mod_->create<ast::type::Vector>(i32, 2);
-          break;
-        case ast::type::TextureDimension::k3d:
-        case ast::type::TextureDimension::kCube:
-        case ast::type::TextureDimension::kCubeArray:
-          return_type = mod_->create<ast::type::Vector>(i32, 3);
-          break;
-      }
-    } else if (ident->intrinsic() == ast::Intrinsic::kTextureStore) {
-      return_type = mod_->create<ast::type::Void>();
-    } else {
-      if (texture->Is<ast::type::DepthTexture>()) {
-        return_type = mod_->create<ast::type::F32>();
-      } else {
-        ast::type::Type* type = nullptr;
-        if (auto* storage = texture->As<ast::type::StorageTexture>()) {
-          type = storage->type();
-        } else if (auto* sampled = texture->As<ast::type::SampledTexture>()) {
-          type = sampled->type();
-        } else if (auto* msampled =
-                       texture->As<ast::type::MultisampledTexture>()) {
-          type = msampled->type();
-        } else {
-          set_error(expr->source(),
-                    "unknown texture type for texture sampling");
-          return false;
+    switch (ident->intrinsic()) {
+      case ast::Intrinsic::kTextureDimensions: {
+        auto* i32 = mod_->create<ast::type::I32>();
+        switch (texture->dim()) {
+          default:
+            set_error(expr->source(), "invalid texture dimensions");
+            break;
+          case ast::type::TextureDimension::k1d:
+          case ast::type::TextureDimension::k1dArray:
+            return_type = i32;
+            break;
+          case ast::type::TextureDimension::k2d:
+          case ast::type::TextureDimension::k2dArray:
+            return_type = mod_->create<ast::type::Vector>(i32, 2);
+            break;
+          case ast::type::TextureDimension::k3d:
+          case ast::type::TextureDimension::kCube:
+          case ast::type::TextureDimension::kCubeArray:
+            return_type = mod_->create<ast::type::Vector>(i32, 3);
+            break;
         }
-        return_type = mod_->create<ast::type::Vector>(type, 4);
+        break;
+      }
+      case ast::Intrinsic::kTextureNumLayers:
+        return_type = mod_->create<ast::type::I32>();
+        break;
+      case ast::Intrinsic::kTextureStore:
+        return_type = mod_->create<ast::type::Void>();
+        break;
+      default: {
+        if (texture->Is<ast::type::DepthTexture>()) {
+          return_type = mod_->create<ast::type::F32>();
+        } else {
+          ast::type::Type* type = nullptr;
+          if (auto* storage = texture->As<ast::type::StorageTexture>()) {
+            type = storage->type();
+          } else if (auto* sampled = texture->As<ast::type::SampledTexture>()) {
+            type = sampled->type();
+          } else if (auto* msampled =
+                         texture->As<ast::type::MultisampledTexture>()) {
+            type = msampled->type();
+          } else {
+            set_error(expr->source(),
+                      "unknown texture type for texture sampling");
+            return false;
+          }
+          return_type = mod_->create<ast::type::Vector>(type, 4);
+        }
       }
     }
     expr->func()->set_result_type(return_type);
@@ -1021,6 +1032,8 @@
     ident->set_intrinsic(ast::Intrinsic::kTanh);
   } else if (name == "textureDimensions") {
     ident->set_intrinsic(ast::Intrinsic::kTextureDimensions);
+  } else if (name == "textureNumLayers") {
+    ident->set_intrinsic(ast::Intrinsic::kTextureNumLayers);
   } else if (name == "textureLoad") {
     ident->set_intrinsic(ast::Intrinsic::kTextureLoad);
   } else if (name == "textureStore") {
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 300cc99..dcd3519 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -1764,6 +1764,7 @@
         IntrinsicData{"tanh", ast::Intrinsic::kTanh},
         IntrinsicData{"textureDimensions", ast::Intrinsic::kTextureDimensions},
         IntrinsicData{"textureLoad", ast::Intrinsic::kTextureLoad},
+        IntrinsicData{"textureNumLayers", ast::Intrinsic::kTextureNumLayers},
         IntrinsicData{"textureSample", ast::Intrinsic::kTextureSample},
         IntrinsicData{"textureSampleBias", ast::Intrinsic::kTextureSampleBias},
         IntrinsicData{"textureSampleCompare",
@@ -2928,6 +2929,15 @@
     case ValidTextureOverload::kDimensionsStorageWO2dArray:
     case ValidTextureOverload::kDimensionsStorageWO3d:
       return R"(textureDimensions(texture))";
+    case ValidTextureOverload::kNumLayers1dArray:
+    case ValidTextureOverload::kNumLayers2dArray:
+    case ValidTextureOverload::kNumLayersCubeArray:
+    case ValidTextureOverload::kNumLayersMultisampled_2dArray:
+    case ValidTextureOverload::kNumLayersDepth2dArray:
+    case ValidTextureOverload::kNumLayersDepthCubeArray:
+    case ValidTextureOverload::kNumLayersStorageWO1dArray:
+    case ValidTextureOverload::kNumLayersStorageWO2dArray:
+      return R"(textureNumLayers(texture))";
     case ValidTextureOverload::kDimensions2dLevel:
     case ValidTextureOverload::kDimensions2dArrayLevel:
     case ValidTextureOverload::kDimensions3dLevel:
@@ -3181,6 +3191,8 @@
                   ty.vec3<i32>()->type_name());
         break;
     }
+  } else if (std::string(param.function) == "textureNumLayers") {
+    EXPECT_EQ(call->result_type(), ty.i32);
   } else if (std::string(param.function) == "textureStore") {
     EXPECT_EQ(call->result_type(), ty.void_);
   } else {
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index e1a5737..a750271 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -695,49 +695,99 @@
   auto* texture_type =
       texture->result_type()->UnwrapAll()->As<ast::type::Texture>();
 
-  if (ident->intrinsic() == ast::Intrinsic::kTextureDimensions) {
-    // Declare a variable to hold the texture dimensions
-    auto dims = generate_name(kTempNamePrefix);
-    EmitType(pre, expr->result_type(), "");
-    pre << " " << dims << ";" << std::endl;
+  switch (ident->intrinsic()) {
+    case ast::Intrinsic::kTextureDimensions:
+    case ast::Intrinsic::kTextureNumLayers: {
+      // Declare a variable to hold the queried texture info
+      auto dims = generate_name(kTempNamePrefix);
 
-    // Now call GetDimensions() on the texture object, populating the dims
-    // variable.
-    std::stringstream tex_out;
-    if (!EmitExpression(pre, tex_out, texture)) {
-      return false;
-    }
-    pre << tex_out.str() << ".GetDimensions(";
-    if (pidx.level != kNotUsed) {
-      pre << pidx.level << ", ";
-    }
-    switch (texture_type->dim()) {
-      case ast::type::TextureDimension::kNone:
-        error_ = "texture dimension is kNone";
+      std::stringstream texture_name;
+      if (!EmitExpression(pre, texture_name, texture)) {
         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;
-    }
+      }
 
-    // The result of the textureDimensions() call is now the temporary variable.
-    out << dims;
-    return true;
+      auto get_dimensions = [&](std::initializer_list<const char*>&& suffixes) {
+        pre << texture_name.str() << ".GetDimensions(";
+        if (pidx.level != kNotUsed) {
+          pre << pidx.level << ", ";
+        }
+        bool first = true;
+        for (auto* suffix : suffixes) {
+          if (!first) {
+            pre << ", ";
+          }
+          first = false;
+          pre << dims << suffix;
+        }
+        pre << ");";
+      };
+
+      const char* dims_swizzle = "";
+      const char* num_els_swizzle = "";
+
+      std::stringstream ss;
+      switch (texture_type->dim()) {
+        case ast::type::TextureDimension::kNone:
+          error_ = "texture dimension is kNone";
+          return false;
+        case ast::type::TextureDimension::k1d:
+          pre << "int " << dims << ";\n";
+          get_dimensions({""});
+          break;
+        case ast::type::TextureDimension::k1dArray:
+          pre << "int2 " << dims << ";\n";
+          get_dimensions({".x", ".y"});
+          dims_swizzle = ".x";
+          num_els_swizzle = ".y";
+          break;
+        case ast::type::TextureDimension::k2d:
+          pre << "int2 " << dims << ";\n";
+          get_dimensions({".x", ".y"});
+          break;
+        case ast::type::TextureDimension::k2dArray:
+          pre << "int3 " << dims << ";\n";
+          get_dimensions({".x", ".y", ".z"});
+          dims_swizzle = ".xy";
+          num_els_swizzle = ".z";
+          break;
+        case ast::type::TextureDimension::k3d:
+          pre << "int3 " << dims << ";\n";
+          get_dimensions({".x", ".y", ".z"});
+          break;
+        case ast::type::TextureDimension::kCube:
+          // width == height == depth for cubes
+          // See https://github.com/gpuweb/gpuweb/issues/1345
+          pre << "int2 " << dims << ";\n";
+          get_dimensions({".x", ".y"});
+          dims_swizzle = ".xyy";  // [width, height, height]
+          break;
+        case ast::type::TextureDimension::kCubeArray:
+          // width == height == depth for cubes
+          // See https://github.com/gpuweb/gpuweb/issues/1345
+          pre << "int3 " << dims << ";\n";
+          get_dimensions({".x", ".y", ".z"});
+          dims_swizzle = ".xyy";  // [width, height, height]
+          num_els_swizzle = ".z";
+          break;
+      }
+
+      // The result of the textureDimensions() call is now in temporary
+      // variable. This may be packed with other data, so the final expression
+      // may require a swizzle.
+      switch (ident->intrinsic()) {
+        case ast::Intrinsic::kTextureDimensions:
+          out << dims << dims_swizzle;
+          return true;
+        case ast::Intrinsic::kTextureNumLayers:
+          out << dims << num_els_swizzle;
+          return true;
+        default:
+          error_ = "Unhandled intrinsic";
+          return false;
+      }
+    }
+    default:
+      break;
   }
 
   if (!EmitExpression(pre, out, texture))
diff --git a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
index e5b5ee2..972283c 100644
--- a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
+++ b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
@@ -41,75 +41,121 @@
   using ValidTextureOverload = ast::intrinsic::test::ValidTextureOverload;
   switch (overload) {
     case ValidTextureOverload::kDimensions1d:
-    case ValidTextureOverload::kDimensions1dArray:
     case ValidTextureOverload::kDimensionsStorageRO1d:
-    case ValidTextureOverload::kDimensionsStorageRO1dArray:
     case ValidTextureOverload::kDimensionsStorageWO1d:
-    case ValidTextureOverload::kDimensionsStorageWO1dArray:
       return {
           "int _tint_tmp;\n"
           "texture_tint_0.GetDimensions(_tint_tmp);",
           "_tint_tmp",
       };
-    case ValidTextureOverload::kDimensions2d:
-    case ValidTextureOverload::kDimensions2dArray:
-    case ValidTextureOverload::kDimensionsMultisampled_2d:
-    case ValidTextureOverload::kDimensionsMultisampled_2dArray:
-    case ValidTextureOverload::kDimensionsDepth2d:
-    case ValidTextureOverload::kDimensionsDepth2dArray:
-    case ValidTextureOverload::kDimensionsStorageRO2d:
-    case ValidTextureOverload::kDimensionsStorageRO2dArray:
-    case ValidTextureOverload::kDimensionsStorageWO2d:
-    case ValidTextureOverload::kDimensionsStorageWO2dArray:
+    case ValidTextureOverload::kDimensions1dArray:
+    case ValidTextureOverload::kDimensionsStorageRO1dArray:
+    case ValidTextureOverload::kDimensionsStorageWO1dArray:
       return {
           "int2 _tint_tmp;\n"
-          "texture_tint_0.GetDimensions(_tint_tmp[0], _tint_tmp[1]);",
+          "texture_tint_0.GetDimensions(_tint_tmp.x, _tint_tmp.y);",
+          "_tint_tmp.x",
+      };
+    case ValidTextureOverload::kDimensions2d:
+    case ValidTextureOverload::kDimensionsMultisampled_2d:
+    case ValidTextureOverload::kDimensionsDepth2d:
+    case ValidTextureOverload::kDimensionsStorageRO2d:
+    case ValidTextureOverload::kDimensionsStorageWO2d:
+      return {
+          "int2 _tint_tmp;\n"
+          "texture_tint_0.GetDimensions(_tint_tmp.x, _tint_tmp.y);",
           "_tint_tmp",
       };
+    case ValidTextureOverload::kDimensions2dArray:
+    case ValidTextureOverload::kDimensionsMultisampled_2dArray:
+    case ValidTextureOverload::kDimensionsDepth2dArray:
+    case ValidTextureOverload::kDimensionsStorageRO2dArray:
+    case ValidTextureOverload::kDimensionsStorageWO2dArray:
+      return {
+          "int3 _tint_tmp;\n"
+          "texture_tint_0."
+          "GetDimensions(_tint_tmp.x, _tint_tmp.y, _tint_tmp.z);",
+          "_tint_tmp.xy",
+      };
     case ValidTextureOverload::kDimensions3d:
     case ValidTextureOverload::kDimensionsStorageRO3d:
     case ValidTextureOverload::kDimensionsStorageWO3d:
       return {
           "int3 _tint_tmp;\n"
-          "texture_tint_0.GetDimensions(_tint_tmp[0], _tint_tmp[1], "
-          "_tint_tmp[2]);",
+          "texture_tint_0."
+          "GetDimensions(_tint_tmp.x, _tint_tmp.y, _tint_tmp.z);",
           "_tint_tmp",
       };
     case ValidTextureOverload::kDimensionsCube:
-    case ValidTextureOverload::kDimensionsCubeArray:
     case ValidTextureOverload::kDimensionsDepthCube:
+      return {
+          "int2 _tint_tmp;\n"
+          "texture_tint_0.GetDimensions(_tint_tmp.x, _tint_tmp.y);",
+          "_tint_tmp.xyy",
+      };
+    case ValidTextureOverload::kDimensionsCubeArray:
     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",
+          "texture_tint_0."
+          "GetDimensions(_tint_tmp.x, _tint_tmp.y, _tint_tmp.z);",
+          "_tint_tmp.xyy",
       };
     case ValidTextureOverload::kDimensions2dLevel:
-    case ValidTextureOverload::kDimensions2dArrayLevel:
     case ValidTextureOverload::kDimensionsDepth2dLevel:
-    case ValidTextureOverload::kDimensionsDepth2dArrayLevel:
       return {
           "int2 _tint_tmp;\n"
-          "texture_tint_0.GetDimensions(1, _tint_tmp[0], _tint_tmp[1]);",
+          "texture_tint_0.GetDimensions(1, _tint_tmp.x, _tint_tmp.y);",
           "_tint_tmp",
       };
+    case ValidTextureOverload::kDimensions2dArrayLevel:
+    case ValidTextureOverload::kDimensionsDepth2dArrayLevel:
+      return {
+          "int3 _tint_tmp;\n"
+          "texture_tint_0."
+          "GetDimensions(1, _tint_tmp.x, _tint_tmp.y, _tint_tmp.z);",
+          "_tint_tmp.xy",
+      };
     case ValidTextureOverload::kDimensions3dLevel:
       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.x, _tint_tmp.y, _tint_tmp.z);",
           "_tint_tmp",
       };
     case ValidTextureOverload::kDimensionsCubeLevel:
-    case ValidTextureOverload::kDimensionsCubeArrayLevel:
     case ValidTextureOverload::kDimensionsDepthCubeLevel:
+      return {
+          "int2 _tint_tmp;\n"
+          "texture_tint_0.GetDimensions(1, _tint_tmp.x, _tint_tmp.y);",
+          "_tint_tmp.xyy",
+      };
+    case ValidTextureOverload::kDimensionsCubeArrayLevel:
     case ValidTextureOverload::kDimensionsDepthCubeArrayLevel:
       return {
           "int3 _tint_tmp;\n"
-          "texture_tint_0.GetDimensions(1, _tint_tmp[0], _tint_tmp[1]);\n"
-          "_tint_tmp[2] = _tint_tmp[1];",
-          "_tint_tmp",
+          "texture_tint_0."
+          "GetDimensions(1, _tint_tmp.x, _tint_tmp.y, _tint_tmp.z);",
+          "_tint_tmp.xyy",
+      };
+    case ValidTextureOverload::kNumLayers1dArray:
+    case ValidTextureOverload::kNumLayersStorageWO1dArray:
+      return {
+          "int2 _tint_tmp;\n"
+          "texture_tint_0.GetDimensions(_tint_tmp.x, _tint_tmp.y);",
+          "_tint_tmp.y",
+      };
+    case ValidTextureOverload::kNumLayers2dArray:
+    case ValidTextureOverload::kNumLayersMultisampled_2dArray:
+    case ValidTextureOverload::kNumLayersDepth2dArray:
+    case ValidTextureOverload::kNumLayersCubeArray:
+    case ValidTextureOverload::kNumLayersDepthCubeArray:
+    case ValidTextureOverload::kNumLayersStorageWO2dArray:
+      return {
+          "int3 _tint_tmp;\n"
+          "texture_tint_0."
+          "GetDimensions(_tint_tmp.x, _tint_tmp.y, _tint_tmp.z);",
+          "_tint_tmp.z",
       };
     case ValidTextureOverload::kSample1dF32:
       return R"(texture_tint_0.Sample(sampler_tint_0, 1.0f))";
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 7efe837..30c1887 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -616,57 +616,71 @@
                            ->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;
-    }
+  switch (ident->intrinsic()) {
+    case 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) {
+      auto get_dim = [&](const char* name) {
+        if (!EmitExpression(params[pidx.texture])) {
+          return false;
+        }
+        out_ << ".get_" << name << "(";
+        if (pidx.level != kNotUsed) {
+          out_ << pidx.level;
+        }
+        out_ << ")";
+        return true;
+      };
+
+      if (dims.size() == 1) {
+        out_ << "int(";
+        get_dim(dims[0]);
+        out_ << ")";
+      } 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;
+    }
+    case ast::Intrinsic::kTextureNumLayers: {
+      out_ << "int(";
       if (!EmitExpression(params[pidx.texture])) {
         return false;
       }
-      out_ << ".get_" << name << "(";
-      if (pidx.level != kNotUsed) {
-        out_ << pidx.level;
-      }
-      out_ << ")";
+      out_ << ".get_array_size())";
       return true;
-    };
-
-    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;
+    default:
+      break;
   }
 
   if (!EmitExpression(params[pidx.texture]))
diff --git a/src/writer/msl/generator_impl_intrinsic_texture_test.cc b/src/writer/msl/generator_impl_intrinsic_texture_test.cc
index 3955bed..67bbdc7 100644
--- a/src/writer/msl/generator_impl_intrinsic_texture_test.cc
+++ b/src/writer/msl/generator_impl_intrinsic_texture_test.cc
@@ -38,7 +38,7 @@
     case ValidTextureOverload::kDimensionsStorageRO1dArray:
     case ValidTextureOverload::kDimensionsStorageWO1d:
     case ValidTextureOverload::kDimensionsStorageWO1dArray:
-      return R"(texture_tint_0.get_width())";
+      return R"(int(texture_tint_0.get_width()))";
     case ValidTextureOverload::kDimensions2d:
     case ValidTextureOverload::kDimensions2dArray:
     case ValidTextureOverload::kDimensionsMultisampled_2d:
@@ -71,6 +71,15 @@
     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_height(1)))";
+    case ValidTextureOverload::kNumLayers1dArray:
+    case ValidTextureOverload::kNumLayers2dArray:
+    case ValidTextureOverload::kNumLayersCubeArray:
+    case ValidTextureOverload::kNumLayersMultisampled_2dArray:
+    case ValidTextureOverload::kNumLayersDepth2dArray:
+    case ValidTextureOverload::kNumLayersDepthCubeArray:
+    case ValidTextureOverload::kNumLayersStorageWO1dArray:
+    case ValidTextureOverload::kNumLayersStorageWO2dArray:
+      return R"(int(texture_tint_0.get_array_size()))";
     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 0bc1565..8887d63 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -2073,6 +2073,49 @@
     return true;
   };
 
+  // Appends a result type and id to `spirv_params`, by first swizzling the
+  // result of the op with `swizzle`.
+  auto append_result_type_and_id_to_spirv_params_swizzled =
+      [&](uint32_t spirv_result_width, std::vector<uint32_t> swizzle) {
+        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_result_width);
+          if (swizzle.size() > 1) {
+            post_emission = [=] {
+              OperandList operands{
+                  result_type,
+                  result_id,
+                  spirv_result,
+                  spirv_result,
+              };
+              for (auto idx : swizzle) {
+                operands.emplace_back(Operand::Int(idx));
+              }
+              return push_function_inst(spv::Op::OpVectorShuffle, operands);
+            };
+          } else {
+            post_emission = [=] {
+              return push_function_inst(spv::Op::OpCompositeExtract,
+                                        {result_type, result_id, spirv_result,
+                                         Operand::Int(swizzle[0])});
+            };
+          }
+          auto spirv_result_type_id = GenerateTypeIfNeeded(spirv_result_type);
+          if (spirv_result_type_id == 0) {
+            return false;
+          }
+          spirv_params.emplace_back(Operand::Int(spirv_result_type_id));
+          spirv_params.emplace_back(spirv_result);
+        }
+        return true;
+      };
+
   auto append_coords_to_spirv_params = [&]() -> bool {
     if (pidx.array_index != kNotUsed) {
       // Array index needs to be appended to the coordinates.
@@ -2147,41 +2190,9 @@
           break;
       }
 
-      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 = [=] {
-            OperandList operands{
-                result_type,
-                result_id,
-                spirv_result,
-                spirv_result,
-            };
-            for (auto idx : swizzle) {
-              operands.emplace_back(Operand::Int(idx));
-            }
-            return push_function_inst(spv::Op::OpVectorShuffle, operands);
-          };
-        } else {
-          post_emission = [=] {
-            return push_function_inst(spv::Op::OpCompositeExtract,
-                                      {result_type, result_id, spirv_result,
-                                       Operand::Int(swizzle[0])});
-          };
-        }
-        auto spirv_result_type_id = GenerateTypeIfNeeded(spirv_result_type);
-        if (spirv_result_type_id == 0) {
-          return false;
-        }
-        spirv_params.emplace_back(Operand::Int(spirv_result_type_id));
-        spirv_params.emplace_back(spirv_result);
+      if (!append_result_type_and_id_to_spirv_params_swizzled(spirv_dims,
+                                                              swizzle)) {
+        return false;
       }
 
       spirv_params.emplace_back(gen_param(pidx.texture));
@@ -2199,6 +2210,41 @@
       }
       break;
     }
+    case ast::Intrinsic::kTextureNumLayers: {
+      uint32_t spirv_dims = 0;
+      switch (texture_type->dim()) {
+        default:
+          error_ = "texture is not arrayed";
+          return false;
+        case ast::type::TextureDimension::k1dArray:
+          spirv_dims = 2;
+          break;
+        case ast::type::TextureDimension::k2dArray:
+        case ast::type::TextureDimension::kCubeArray:
+          spirv_dims = 3;
+          break;
+      }
+
+      // OpImageQuerySize[Lod] packs the array count as the last element of the
+      // returned vector. Extract this.
+      if (!append_result_type_and_id_to_spirv_params_swizzled(
+              spirv_dims, {spirv_dims - 1})) {
+        return false;
+      }
+
+      spirv_params.emplace_back(gen_param(pidx.texture));
+
+      if (texture_type->Is<ast::type::MultisampledTexture>() ||
+          texture_type->Is<ast::type::StorageTexture>()) {
+        op = spv::Op::OpImageQuerySize;
+      } else {
+        ast::SintLiteral i32_0(Source{}, mod_->create<ast::type::I32>(), 0);
+        op = spv::Op::OpImageQuerySizeLod;
+        spirv_params.emplace_back(
+            Operand::Int(GenerateLiteralIfNeeded(nullptr, &i32_0)));
+      }
+      break;
+    }
     case ast::Intrinsic::kTextureLoad: {
       op = texture_type->Is<ast::type::StorageTexture>()
                ? spv::Op::OpImageRead
diff --git a/src/writer/spirv/builder_intrinsic_texture_test.cc b/src/writer/spirv/builder_intrinsic_texture_test.cc
index c65fb96..abb588f 100644
--- a/src/writer/spirv/builder_intrinsic_texture_test.cc
+++ b/src/writer/spirv/builder_intrinsic_texture_test.cc
@@ -740,6 +740,176 @@
           R"(
 OpCapability ImageQuery
 )"};
+
+    case ValidTextureOverload::kNumLayers1dArray:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 1D 0 1 0 1 Unknown
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+%11 = OpTypeVector %9 2
+%13 = OpConstant %9 0
+)",
+              R"(
+%12 = OpLoad %3 %1
+%10 = OpImageQuerySizeLod %11 %12 %13
+%8 = OpCompositeExtract %9 %10 1
+)",
+              R"(
+OpCapability Sampled1D
+OpCapability ImageQuery
+)"};
+    case ValidTextureOverload::kNumLayers2dArray:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+%11 = OpTypeVector %9 3
+%13 = OpConstant %9 0
+)",
+              R"(
+%12 = OpLoad %3 %1
+%10 = OpImageQuerySizeLod %11 %12 %13
+%8 = OpCompositeExtract %9 %10 2
+)",
+              R"(
+OpCapability ImageQuery
+)"};
+    case ValidTextureOverload::kNumLayersCubeArray:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 0 1 0 1 Unknown
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+%11 = OpTypeVector %9 3
+%13 = OpConstant %9 0
+)",
+              R"(
+%12 = OpLoad %3 %1
+%10 = OpImageQuerySizeLod %11 %12 %13
+%8 = OpCompositeExtract %9 %10 2
+)",
+              R"(
+OpCapability SampledCubeArray
+OpCapability ImageQuery
+)"};
+    case ValidTextureOverload::kNumLayersMultisampled_2dArray:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 1 1 Unknown
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+%11 = OpTypeVector %9 3
+)",
+              R"(
+%12 = OpLoad %3 %1
+%10 = OpImageQuerySize %11 %12
+%8 = OpCompositeExtract %9 %10 2
+)",
+              R"(
+OpCapability ImageQuery
+)"};
+    case ValidTextureOverload::kNumLayersDepth2dArray:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+%11 = OpTypeVector %9 3
+%13 = OpConstant %9 0
+)",
+              R"(
+%12 = OpLoad %3 %1
+%10 = OpImageQuerySizeLod %11 %12 %13
+%8 = OpCompositeExtract %9 %10 2
+)",
+              R"(
+OpCapability ImageQuery
+)"};
+    case ValidTextureOverload::kNumLayersDepthCubeArray:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 1 1 0 1 Unknown
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+%11 = OpTypeVector %9 3
+%13 = OpConstant %9 0
+)",
+              R"(
+%12 = OpLoad %3 %1
+%10 = OpImageQuerySizeLod %11 %12 %13
+%8 = OpCompositeExtract %9 %10 2
+)",
+              R"(
+OpCapability SampledCubeArray
+OpCapability ImageQuery
+)"};
+    case ValidTextureOverload::kNumLayersStorageWO1dArray:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 1D 0 1 0 2 Rgba32f
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+%11 = OpTypeVector %9 2
+)",
+              R"(
+%12 = OpLoad %3 %1
+%10 = OpImageQuerySize %11 %12
+%8 = OpCompositeExtract %9 %10 1
+)",
+              R"(
+OpCapability Image1D
+OpCapability ImageQuery
+)"};
+    case ValidTextureOverload::kNumLayersStorageWO2dArray:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 2 Rgba32f
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+%11 = OpTypeVector %9 3
+)",
+              R"(
+%12 = OpLoad %3 %1
+%10 = OpImageQuerySize %11 %12
+%8 = OpCompositeExtract %9 %10 2
+)",
+              R"(
+OpCapability ImageQuery
+)"};
     case ValidTextureOverload::kSample1dF32:
       return {
           R"(