Implement textureSample builtins

Handle wsgl parsing and spirv writing of:
  textureSample(), textureSampleBias(), textureSampleLevel(),
  textureSampleGrad(), textureSampleCompare()

Handle the different signature for array texture types.
Includes offset overloads.

Change-Id: I6802d97cd9a7083f12439b32725b9a4b666b8c63
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32985
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 0a47cd7..529bc60 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -755,6 +755,8 @@
     "src/ast/function_test.cc",
     "src/ast/identifier_expression_test.cc",
     "src/ast/if_statement_test.cc",
+    "src/ast/intrinsic_texture_helper_test.cc",
+    "src/ast/intrinsic_texture_helper_test.h",
     "src/ast/int_literal_test.cc",
     "src/ast/location_decoration_test.cc",
     "src/ast/loop_statement_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 56cda30..cc59d62 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -364,6 +364,8 @@
   ast/function_test.cc
   ast/identifier_expression_test.cc
   ast/if_statement_test.cc
+  ast/intrinsic_texture_helper_test.cc
+  ast/intrinsic_texture_helper_test.h
   ast/int_literal_test.cc
   ast/location_decoration_test.cc
   ast/loop_statement_test.cc
diff --git a/src/ast/identifier_expression.h b/src/ast/identifier_expression.h
index af08b69..4c6ac31 100644
--- a/src/ast/identifier_expression.h
+++ b/src/ast/identifier_expression.h
@@ -15,7 +15,9 @@
 #ifndef SRC_AST_IDENTIFIER_EXPRESSION_H_
 #define SRC_AST_IDENTIFIER_EXPRESSION_H_
 
+#include <memory>
 #include <string>
+#include <utility>
 
 #include "src/ast/expression.h"
 #include "src/ast/intrinsic.h"
@@ -45,6 +47,17 @@
   void set_intrinsic(Intrinsic i) { intrinsic_ = i; }
   /// @returns the intrinsic this identifier represents
   Intrinsic intrinsic() const { return intrinsic_; }
+
+  /// Sets the intrinsic signature
+  /// @param s the intrinsic signature to set
+  void set_intrinsic_signature(std::unique_ptr<intrinsic::Signature> s) {
+    intrinsic_sig_ = std::move(s);
+  }
+  /// @returns the intrinsic signature for this identifier.
+  const intrinsic::Signature* intrinsic_signature() const {
+    return intrinsic_sig_.get();
+  }
+
   /// @returns true if this identifier is for an intrinsic
   bool IsIntrinsic() const { return intrinsic_ != Intrinsic::kNone; }
 
@@ -63,6 +76,7 @@
   IdentifierExpression(const IdentifierExpression&) = delete;
 
   Intrinsic intrinsic_ = Intrinsic::kNone;
+  std::unique_ptr<intrinsic::Signature> intrinsic_sig_;
   std::string name_;
 };
 
diff --git a/src/ast/intrinsic.cc b/src/ast/intrinsic.cc
index 14fdeef..dc6c60a 100644
--- a/src/ast/intrinsic.cc
+++ b/src/ast/intrinsic.cc
@@ -216,6 +216,9 @@
     case Intrinsic::kTextureSampleCompare:
       out << "textureSampleCompare";
       break;
+    case Intrinsic::kTextureSampleGrad:
+      out << "textureSampleGrad";
+      break;
     case Intrinsic::kTextureSampleLevel:
       out << "textureSampleLevel";
       break;
@@ -231,6 +234,12 @@
 
 namespace intrinsic {
 
+Signature::~Signature() = default;
+TextureSignature::~TextureSignature() = default;
+
+TextureSignature::Parameters::Index::Index() = default;
+TextureSignature::Parameters::Index::Index(const Index&) = default;
+
 bool IsCoarseDerivative(ast::Intrinsic i) {
   return i == Intrinsic::kDpdxCoarse || i == Intrinsic::kDpdyCoarse ||
          i == Intrinsic::kFwidthCoarse;
@@ -256,7 +265,8 @@
   return i == Intrinsic::kTextureLoad || i == Intrinsic::kTextureSample ||
          i == Intrinsic::kTextureSampleLevel ||
          i == Intrinsic::kTextureSampleBias ||
-         i == Intrinsic::kTextureSampleCompare;
+         i == Intrinsic::kTextureSampleCompare ||
+         i == Intrinsic::kTextureSampleGrad;
 }
 
 }  // namespace intrinsic
diff --git a/src/ast/intrinsic.h b/src/ast/intrinsic.h
index 9eb1600..a122ad3 100644
--- a/src/ast/intrinsic.h
+++ b/src/ast/intrinsic.h
@@ -89,6 +89,7 @@
   kTextureSample,
   kTextureSampleBias,
   kTextureSampleCompare,
+  kTextureSampleGrad,
   kTextureSampleLevel,
   kTrunc
 };
@@ -99,6 +100,64 @@
 
 namespace intrinsic {
 
+/// Signature is the base struct for all intrinsic signature types.
+/// Signatures are used to identify the particular overload for intrinsics that
+/// have different signatures with the same function name.
+struct Signature {
+  virtual ~Signature();
+};
+
+/// TextureSignature describes the signature of a texture intrinsic function.
+struct TextureSignature : public Signature {
+  /// Parameters describes the parameters for the texture function.
+  struct Parameters {
+    /// kNotUsed is the constant that indicates the given parameter is not part
+    /// of the texture function signature.
+    static constexpr const size_t kNotUsed = ~static_cast<size_t>(0u);
+    /// Index holds each of the possible parameter indices. If a parameter index
+    /// is equal to `kNotUsed` then this parameter is not used by the function.
+    struct Index {
+      /// Constructor
+      Index();
+      /// Copy constructor
+      Index(const Index&);
+      /// `array_index` parameter index.
+      size_t array_index = kNotUsed;
+      /// `bias` parameter index.
+      size_t bias = kNotUsed;
+      /// `coords` parameter index.
+      size_t coords = kNotUsed;
+      /// `depth_ref` parameter index.
+      size_t depth_ref = kNotUsed;
+      /// `ddx` parameter index.
+      size_t ddx = kNotUsed;
+      /// `ddy` parameter index.
+      size_t ddy = kNotUsed;
+      /// `level` parameter index.
+      size_t level = kNotUsed;
+      /// `offset` parameter index.
+      size_t offset = kNotUsed;
+      /// `sampler` parameter index.
+      size_t sampler = kNotUsed;
+      /// `texture` parameter index.
+      size_t texture = kNotUsed;
+    };
+    /// The indices of all possible parameters.
+    Index idx;
+    /// Total number of parameters.
+    size_t count = 0;
+  };
+
+  /// Construct an immutable `TextureSignature`.
+  /// @param p the texture intrinsic parameter signature.
+  explicit TextureSignature(const Parameters& p) : params(p) {}
+
+  ~TextureSignature() override;
+
+  /// The texture intrinsic parameter signature.
+  const Parameters params;
+};
+
 /// Determines if the given |name| is a coarse derivative
 /// @param i the intrinsic
 /// @returns true if the given derivative is coarse.
diff --git a/src/ast/intrinsic_texture_helper_test.cc b/src/ast/intrinsic_texture_helper_test.cc
new file mode 100644
index 0000000..5e1587c
--- /dev/null
+++ b/src/ast/intrinsic_texture_helper_test.cc
@@ -0,0 +1,1119 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/ast/intrinsic_texture_helper_test.h"
+
+#include "src/ast/type_constructor_expression.h"
+
+namespace tint {
+namespace ast {
+namespace intrinsic {
+namespace test {
+
+TextureOverloadCase::TextureOverloadCase() = default;
+TextureOverloadCase::TextureOverloadCase(
+    ValidTextureOverload o,
+    const char* d,
+    TextureKind tk,
+    ast::type::SamplerKind sk,
+    ast::type::TextureDimension td,
+    TextureDataType tdt,
+    const char* f,
+    std::function<ast::ExpressionList(ast::Builder*)> a)
+    : overload(o),
+      description(d),
+      texture_kind(tk),
+      sampler_kind(sk),
+      texture_dimension(td),
+      texture_data_type(tdt),
+      function(f),
+      args(std::move(a)) {}
+TextureOverloadCase::TextureOverloadCase(const TextureOverloadCase&) = default;
+TextureOverloadCase::~TextureOverloadCase() = default;
+
+std::vector<TextureOverloadCase> TextureOverloadCase::ValidCases() {
+  return {{
+              ValidTextureOverload::kSample1dF32,
+              "textureSample(t : texture_1d<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k1d,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                return b->ExprList("texture",  // t
+                                   "sampler",  // s
+                                   1.0f);      // coords
+              },
+          },
+          {
+              ValidTextureOverload::kSample1dArrayF32,
+              "textureSample(t : texture_1d_array<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : f32,\n"
+              "              array_index : u32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k1dArray,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                return b->ExprList("texture",  // t
+                                   "sampler",  // s
+                                   1.0f,       // coords
+                                   2u);        // array_index
+              },
+          },
+          {
+              ValidTextureOverload::kSample2dF32,
+              "textureSample(t : texture_2d<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : vec2<f32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                // t
+                                   "sampler",                // s
+                                   b->vec2<f32>(1.f, 2.f));  // coords
+              },
+          },
+          {
+              ValidTextureOverload::kSample2dOffsetF32,
+              "textureSample(t : texture_2d<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : vec2<f32>\n"
+              "              offset : vec2<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   b->vec2<i32>(3, 4));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSample2dArrayF32,
+              "textureSample(t : texture_2d_array<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : vec2<f32>,\n"
+              "              array_index : u32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u);                     // array_index
+              },
+          },
+          {
+              ValidTextureOverload::kSample2dArrayOffsetF32,
+              "textureSample(t : texture_2d_array<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : vec2<f32>,\n"
+              "              array_index : u32\n"
+              "              offset : vec2<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u,                      // array_index
+                                   b->vec2<i32>(4, 5));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSample3dF32,
+              "textureSample(t : texture_3d<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : vec3<f32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k3d,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                     // t
+                                   "sampler",                     // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f));  // coords
+              },
+          },
+          {
+              ValidTextureOverload::kSample3dOffsetF32,
+              "textureSample(t : texture_3d<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : vec3<f32>\n"
+              "              offset : vec3<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k3d,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   b->vec3<i32>(4, 5, 6));       // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleCubeF32,
+              "textureSample(t : texture_cube<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : vec3<f32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCube,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                     // t
+                                   "sampler",                     // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f));  // coords
+              },
+          },
+          {
+              ValidTextureOverload::kSampleCubeArrayF32,
+              "textureSample(t : texture_cube_array<f32>,\n"
+              "              s : sampler,\n"
+              "              coords : vec3<f32>,\n"
+              "              array_index : u32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCubeArray,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4u);                          // array_index
+              },
+          },
+          {
+              ValidTextureOverload::kSampleDepth2dF32,
+              "textureSample(t : texture_depth_2d,\n"
+              "              s : sampler,\n"
+              "              coords : vec2<f32>) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                // t
+                                   "sampler",                // s
+                                   b->vec2<f32>(1.f, 2.f));  // coords
+              },
+          },
+          {
+              ValidTextureOverload::kSampleDepth2dOffsetF32,
+              "textureSample(t : texture_depth_2d,\n"
+              "              s : sampler,\n"
+              "              coords : vec2<f32>\n"
+              "              offset : vec2<i32>) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   b->vec2<i32>(3, 4));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleDepth2dArrayF32,
+              "textureSample(t : texture_depth_2d_array,\n"
+              "              s : sampler,\n"
+              "              coords : vec2<f32>,\n"
+              "              array_index : u32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u);                     // array_index
+              },
+          },
+          {
+              ValidTextureOverload::kSampleDepth2dArrayOffsetF32,
+              "textureSample(t : texture_depth_2d_array,\n"
+              "              s : sampler,\n"
+              "              coords : vec2<f32>,\n"
+              "              array_index : u32\n"
+              "              offset : vec2<i32>) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u,                      // array_index
+                                   b->vec2<i32>(4, 5));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleDepthCubeF32,
+              "textureSample(t : texture_depth_cube,\n"
+              "              s : sampler,\n"
+              "              coords : vec3<f32>) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCube,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                     // t
+                                   "sampler",                     // s
+                                   b->vec2<f32>(1.f, 2.f, 3.f));  // coords
+              },
+          },
+          {
+              ValidTextureOverload::kSampleDepthCubeArrayF32,
+              "textureSample(t : texture_depth_cube_array,\n"
+              "              s : sampler,\n"
+              "              coords : vec3<f32>,\n"
+              "              array_index : u32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCubeArray,
+              TextureDataType::kF32,
+              "textureSample",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec2<f32>(1.f, 2.f, 3.f),  // coords
+                                   4u);                          // array_index
+              },
+          },
+          {
+              ValidTextureOverload::kSampleBias2dF32,
+              "textureSampleBias(t : texture_2d<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec2<f32>,\n"
+              "                  bias : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleBias",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3.f);                    // bias
+              },
+          },
+          {
+              ValidTextureOverload::kSampleBias2dOffsetF32,
+              "textureSampleBias(t : texture_2d<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec2<f32>,\n"
+              "                  bias : f32,\n"
+              "                  offset : vec2<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleBias",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3.f,                     // bias
+                                   b->vec2<i32>(4, 5));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleBias2dArrayF32,
+              "textureSampleBias(t : texture_2d_array<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec2<f32>,\n"
+              "                  array_index : u32,\n"
+              "                  bias : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleBias",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   4u,                      // array_index
+                                   3.f);                    // bias
+              },
+          },
+          {
+              ValidTextureOverload::kSampleBias2dArrayOffsetF32,
+              "textureSampleBias(t : texture_2d_array<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec2<f32>,\n"
+              "                  array_index : u32,\n"
+              "                  bias : f32,\n"
+              "                  offset : vec2<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleBias",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u,                      // array_index
+                                   4.f,                     // bias
+                                   b->vec2<i32>(5, 6));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleBias3dF32,
+              "textureSampleBias(t : texture_3d<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec3<f32>,\n"
+              "                  bias : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k3d,
+              TextureDataType::kF32,
+              "textureSampleBias",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4.f);                         // bias
+              },
+          },
+          {
+              ValidTextureOverload::kSampleBias3dOffsetF32,
+              "textureSampleBias(t : texture_3d<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec3<f32>,\n"
+              "                  bias : f32,\n"
+              "                  offset : vec3<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k3d,
+              TextureDataType::kF32,
+              "textureSampleBias",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4.f,                          // bias
+                                   b->vec3<i32>(5, 6, 7));       // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleBiasCubeF32,
+              "textureSampleBias(t : texture_cube<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec3<f32>,\n"
+              "                  bias : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCube,
+              TextureDataType::kF32,
+              "textureSampleBias",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4.f);                         // bias
+              },
+          },
+          {
+              ValidTextureOverload::kSampleBiasCubeArrayF32,
+              "textureSampleBias(t : texture_cube_array<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec3<f32>,\n"
+              "                  array_index : u32,\n"
+              "                  bias : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCubeArray,
+              TextureDataType::kF32,
+              "textureSampleBias",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   3u,                           // array_index
+                                   4.f);                         // bias
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevel2dF32,
+              "textureSampleLevel(t : texture_2d<f32>,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec2<f32>,\n"
+              "                   level : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3.f);                    // level
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevel2dOffsetF32,
+              "textureSampleLevel(t : texture_2d<f32>,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec2<f32>,\n"
+              "                   level : f32,\n"
+              "                   offset : vec2<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3.f,                     // level
+                                   b->vec2<i32>(4, 5));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevel2dArrayF32,
+              "textureSampleLevel(t : texture_2d_array<f32>,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec2<f32>,\n"
+              "                   array_index : u32,\n"
+              "                   level : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u,                      // array_index
+                                   4.f);                    // level
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevel2dArrayOffsetF32,
+              "textureSampleLevel(t : texture_2d_array<f32>,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec2<f32>,\n"
+              "                   array_index : u32,\n"
+              "                   level : f32,\n"
+              "                   offset : vec2<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u,                      // array_index
+                                   4.f,                     // level
+                                   b->vec2<i32>(5, 6));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevel3dF32,
+              "textureSampleLevel(t : texture_3d<f32>,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec3<f32>,\n"
+              "                   level : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k3d,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4.f);                         // level
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevel3dOffsetF32,
+              "textureSampleLevel(t : texture_3d<f32>,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec3<f32>,\n"
+              "                   level : f32,\n"
+              "                   offset : vec3<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k3d,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4.f,                          // level
+                                   b->vec3<i32>(5, 6, 7));       // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevelCubeF32,
+              "textureSampleLevel(t : texture_cube<f32>,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec3<f32>,\n"
+              "                   level : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCube,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4.f);                         // level
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevelCubeArrayF32,
+              "textureSampleLevel(t : texture_cube_array<f32>,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec3<f32>,\n"
+              "                   array_index : u32,\n"
+              "                   level : f32) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCubeArray,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4u,                           // array_index
+                                   5.f);                         // level
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevelDepth2dF32,
+              "textureSampleLevel(t : texture_depth_2d,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec2<f32>,\n"
+              "                   level : u32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u);                     // level
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevelDepth2dOffsetF32,
+              "textureSampleLevel(t : texture_depth_2d,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec2<f32>,\n"
+              "                   level : u32,\n"
+              "                   offset : vec2<i32>) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u,                      // level
+                                   b->vec2<i32>(4, 5));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevelDepth2dArrayF32,
+              "textureSampleLevel(t : texture_depth_2d_array,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec2<f32>,\n"
+              "                   array_index : u32,\n"
+              "                   level : u32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u,                      // array_index
+                                   4u);                     // level
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32,
+              "textureSampleLevel(t : texture_depth_2d_array,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec2<f32>,\n"
+              "                   array_index : u32,\n"
+              "                   level : u32,\n"
+              "                   offset : vec2<i32>) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u,                      // array_index
+                                   4u,                      // level
+                                   b->vec2<i32>(5, 6));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevelDepthCubeF32,
+              "textureSampleLevel(t : texture_depth_cube,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec3<f32>,\n"
+              "                   level : u32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCube,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4u);                          // level
+              },
+          },
+          {
+              ValidTextureOverload::kSampleLevelDepthCubeArrayF32,
+              "textureSampleLevel(t : texture_depth_cube_array,\n"
+              "                   s : sampler,\n"
+              "                   coords : vec3<f32>,\n"
+              "                   array_index : u32,\n"
+              "                   level : u32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCubeArray,
+              TextureDataType::kF32,
+              "textureSampleLevel",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4u,                           // array_index
+                                   5u);                          // level
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGrad2dF32,
+              "textureSampleGrad(t : texture_2d<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec2<f32>\n"
+              "                  ddx : vec2<f32>,\n"
+              "                  ddy : vec2<f32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleGrad",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                  // t
+                                   "sampler",                  // s
+                                   b->vec2<f32>(1.0f, 2.0f),   // coords
+                                   b->vec2<f32>(3.0f, 4.0f),   // ddx
+                                   b->vec2<f32>(5.0f, 6.0f));  // ddy
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGrad2dOffsetF32,
+              "textureSampleGrad(t : texture_2d<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec2<f32>,\n"
+              "                  ddx : vec2<f32>,\n"
+              "                  ddy : vec2<f32>,\n"
+              "                  offset : vec2<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleGrad",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   b->vec2<f32>(3.f, 4.f),  // ddx
+                                   b->vec2<f32>(5.f, 6.f),  // ddy
+                                   b->vec2<i32>(7, 8));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGrad2dArrayF32,
+              "textureSampleGrad(t : texture_2d_array<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec2<f32>,\n"
+              "                  array_index : u32,\n"
+              "                  ddx : vec2<f32>,\n"
+              "                  ddy : vec2<f32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleGrad",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                // t
+                                   "sampler",                // s
+                                   b->vec2<f32>(1.f, 2.f),   // coords
+                                   3u,                       // array_index
+                                   b->vec2<f32>(4.f, 5.f),   // ddx
+                                   b->vec2<f32>(6.f, 7.f));  // ddy
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGrad2dArrayOffsetF32,
+              "textureSampleGrad(t : texture_2d_array<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec2<f32>,\n"
+              "                  array_index : u32,\n"
+              "                  ddx : vec2<f32>,\n"
+              "                  ddy : vec2<f32>,\n"
+              "                  offset : vec2<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleGrad",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3u,                      // array_index
+                                   b->vec2<f32>(4.f, 5.f),  // ddx
+                                   b->vec2<f32>(6.f, 7.f),  // ddy
+                                   b->vec2<i32>(8, 9));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGrad3dF32,
+              "textureSampleGrad(t : texture_3d<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec3<f32>,\n"
+              "                  ddx : vec3<f32>,\n"
+              "                  ddy : vec3<f32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k3d,
+              TextureDataType::kF32,
+              "textureSampleGrad",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                     // t
+                                   "sampler",                     // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),   // coords
+                                   b->vec3<f32>(4.f, 5.f, 6.f),   // ddx
+                                   b->vec3<f32>(7.f, 8.f, 9.f));  // ddy
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGrad3dOffsetF32,
+              "textureSampleGrad(t : texture_3d<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec3<f32>,\n"
+              "                  ddx : vec3<f32>,\n"
+              "                  ddy : vec3<f32>,\n"
+              "                  offset : vec3<i32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::k3d,
+              TextureDataType::kF32,
+              "textureSampleGrad",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   b->vec3<f32>(4.f, 5.f, 6.f),  // ddx
+                                   b->vec3<f32>(7.f, 8.f, 9.f),  // ddy
+                                   b->vec3<i32>(10, 11, 12));    // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGradCubeF32,
+              "textureSampleGrad(t : texture_cube<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec3<f32>,\n"
+              "                  ddx : vec3<f32>,\n"
+              "                  ddy : vec3<f32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCube,
+              TextureDataType::kF32,
+              "textureSampleGrad",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                     // t
+                                   "sampler",                     // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),   // coords
+                                   b->vec3<f32>(4.f, 5.f, 6.f),   // ddx
+                                   b->vec3<f32>(7.f, 8.f, 9.f));  // ddy
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGradCubeArrayF32,
+              "textureSampleGrad(t : texture_cube_array<f32>,\n"
+              "                  s : sampler,\n"
+              "                  coords : vec3<f32>,\n"
+              "                  array_index : u32,\n"
+              "                  ddx : vec3<f32>,\n"
+              "                  ddy : vec3<f32>) -> vec4<f32>",
+              TextureKind::kRegular,
+              ast::type::SamplerKind::kSampler,
+              ast::type::TextureDimension::kCubeArray,
+              TextureDataType::kF32,
+              "textureSampleGrad",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4u,                           // array_index
+                                   b->vec3<f32>(5.f, 6.f, 7.f),  // ddx
+                                   b->vec3<f32>(8.f, 9.f, 10.f));  // ddy
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGradDepth2dF32,
+              "textureSampleCompare(t : texture_depth_2d,\n"
+              "                     s : sampler_comparison,\n"
+              "                     coords : vec2<f32>,\n"
+              "                     depth_ref : f32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kComparisonSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleCompare",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3.f);                    // depth_ref
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGradDepth2dOffsetF32,
+              "textureSampleCompare(t : texture_depth_2d,\n"
+              "                     s : sampler_comparison,\n"
+              "                     coords : vec2<f32>,\n"
+              "                     depth_ref : f32,\n"
+              "                     offset : vec2<i32>) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kComparisonSampler,
+              ast::type::TextureDimension::k2d,
+              TextureDataType::kF32,
+              "textureSampleCompare",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   3.f,                     // depth_ref
+                                   b->vec2<i32>(4, 5));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGradDepth2dArrayF32,
+              "textureSampleCompare(t : texture_depth_2d_array,\n"
+              "                     s : sampler_comparison,\n"
+              "                     coords : vec2<f32>,\n"
+              "                     array_index : u32,\n"
+              "                     depth_ref : f32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kComparisonSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleCompare",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   4u,                      // array_index
+                                   3.f);                    // depth_ref
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGradDepth2dArrayOffsetF32,
+              "textureSampleCompare(t : texture_depth_2d_array,\n"
+              "                     s : sampler_comparison,\n"
+              "                     coords : vec2<f32>,\n"
+              "                     array_index : u32,\n"
+              "                     depth_ref : f32,\n"
+              "                     offset : vec2<i32>) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kComparisonSampler,
+              ast::type::TextureDimension::k2dArray,
+              TextureDataType::kF32,
+              "textureSampleCompare",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                using i32 = ast::Builder::i32;
+                return b->ExprList("texture",               // t
+                                   "sampler",               // s
+                                   b->vec2<f32>(1.f, 2.f),  // coords
+                                   4u,                      // array_index
+                                   3.f,                     // depth_ref
+                                   b->vec2<i32>(5, 6));     // offset
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGradDepthCubeF32,
+              "textureSampleCompare(t : texture_depth_cube,\n"
+              "                     s : sampler_comparison,\n"
+              "                     coords : vec3<f32>,\n"
+              "                     depth_ref : f32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kComparisonSampler,
+              ast::type::TextureDimension::kCube,
+              TextureDataType::kF32,
+              "textureSampleCompare",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4.f);                         // depth_ref
+              },
+          },
+          {
+              ValidTextureOverload::kSampleGradDepthCubeArrayF32,
+              "textureSampleCompare(t : texture_depth_cube_array,\n"
+              "                     s : sampler_comparison,\n"
+              "                     coords : vec3<f32>,\n"
+              "                     array_index : u32,\n"
+              "                     depth_ref : f32) -> f32",
+              TextureKind::kDepth,
+              ast::type::SamplerKind::kComparisonSampler,
+              ast::type::TextureDimension::kCubeArray,
+              TextureDataType::kF32,
+              "textureSampleCompare",
+              [](ast::Builder* b) {
+                using f32 = ast::Builder::f32;
+                return b->ExprList("texture",                    // t
+                                   "sampler",                    // s
+                                   b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                                   4u,                           // array_index
+                                   5.f);                         // depth_ref
+              },
+          }};
+}
+
+}  // namespace test
+}  // namespace intrinsic
+}  // namespace ast
+}  // namespace tint
diff --git a/src/ast/intrinsic_texture_helper_test.h b/src/ast/intrinsic_texture_helper_test.h
new file mode 100644
index 0000000..5e7da8a
--- /dev/null
+++ b/src/ast/intrinsic_texture_helper_test.h
@@ -0,0 +1,171 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_AST_INTRINSIC_TEXTURE_HELPER_TEST_H_
+#define SRC_AST_INTRINSIC_TEXTURE_HELPER_TEST_H_
+
+#include <functional>
+#include <vector>
+
+#include "src/ast/builder.h"
+#include "src/ast/type/sampler_type.h"
+#include "src/ast/type/texture_type.h"
+
+namespace tint {
+namespace ast {
+namespace intrinsic {
+namespace test {
+
+enum class TextureKind { kRegular, kDepth };
+
+inline std::ostream& operator<<(std::ostream& out, const TextureKind& kind) {
+  switch (kind) {
+    case TextureKind::kRegular:
+      out << "regular";
+      break;
+    case TextureKind::kDepth:
+      out << "depth";
+      break;
+  }
+  return out;
+}
+
+enum class TextureDataType { kF32, kU32, kI32 };
+
+inline std::ostream& operator<<(std::ostream& out, const TextureDataType& ty) {
+  switch (ty) {
+    case TextureDataType::kF32:
+      out << "f32";
+      break;
+    case TextureDataType::kU32:
+      out << "u32";
+      break;
+    case TextureDataType::kI32:
+      out << "i32";
+      break;
+  }
+  return out;
+}
+
+enum class ValidTextureOverload {
+  kSample1dF32,
+  kSample1dArrayF32,
+  kSample2dF32,
+  kSample2dOffsetF32,
+  kSample2dArrayF32,
+  kSample2dArrayOffsetF32,
+  kSample3dF32,
+  kSample3dOffsetF32,
+  kSampleCubeF32,
+  kSampleCubeArrayF32,
+  kSampleDepth2dF32,
+  kSampleDepth2dOffsetF32,
+  kSampleDepth2dArrayF32,
+  kSampleDepth2dArrayOffsetF32,
+  kSampleDepthCubeF32,
+  kSampleDepthCubeArrayF32,
+  kSampleBias2dF32,
+  kSampleBias2dOffsetF32,
+  kSampleBias2dArrayF32,
+  kSampleBias2dArrayOffsetF32,
+  kSampleBias3dF32,
+  kSampleBias3dOffsetF32,
+  kSampleBiasCubeF32,
+  kSampleBiasCubeArrayF32,
+  kSampleLevel2dF32,
+  kSampleLevel2dOffsetF32,
+  kSampleLevel2dArrayF32,
+  kSampleLevel2dArrayOffsetF32,
+  kSampleLevel3dF32,
+  kSampleLevel3dOffsetF32,
+  kSampleLevelCubeF32,
+  kSampleLevelCubeArrayF32,
+  kSampleLevelDepth2dF32,
+  kSampleLevelDepth2dOffsetF32,
+  kSampleLevelDepth2dArrayF32,
+  kSampleLevelDepth2dArrayOffsetF32,
+  kSampleLevelDepthCubeF32,
+  kSampleLevelDepthCubeArrayF32,
+  kSampleGrad2dF32,
+  kSampleGrad2dOffsetF32,
+  kSampleGrad2dArrayF32,
+  kSampleGrad2dArrayOffsetF32,
+  kSampleGrad3dF32,
+  kSampleGrad3dOffsetF32,
+  kSampleGradCubeF32,
+  kSampleGradCubeArrayF32,
+  kSampleGradDepth2dF32,
+  kSampleGradDepth2dOffsetF32,
+  kSampleGradDepth2dArrayF32,
+  kSampleGradDepth2dArrayOffsetF32,
+  kSampleGradDepthCubeF32,
+  kSampleGradDepthCubeArrayF32,
+};
+
+/// Describes a texture intrinsic overload
+struct TextureOverloadCase {
+  /// Constructor
+  TextureOverloadCase();
+  /// Constructor
+  TextureOverloadCase(ValidTextureOverload,
+                      const char*,
+                      TextureKind,
+                      ast::type::SamplerKind,
+                      ast::type::TextureDimension,
+                      TextureDataType,
+                      const char*,
+                      std::function<ast::ExpressionList(ast::Builder*)>);
+  /// Copy constructor
+  TextureOverloadCase(const TextureOverloadCase&);
+  /// Destructor
+  ~TextureOverloadCase();
+
+  /// @return a vector containing a large number of valid texture overloads
+  static std::vector<TextureOverloadCase> ValidCases();
+
+  /// The enumerator for this overload
+  ValidTextureOverload overload;
+  /// A human readable description of the overload
+  const char* description;
+  /// The texture kind for the texture parameter
+  TextureKind texture_kind;
+  /// The sampler kind for the sampler parameter
+  ast::type::SamplerKind sampler_kind;
+  /// The dimensions of the texture parameter
+  ast::type::TextureDimension texture_dimension;
+  /// The data type of the texture parameter
+  TextureDataType texture_data_type;
+  /// Name of the function. e.g. `textureSample`, `textureSampleGrad`, etc
+  const char* function;
+  /// A function that builds the AST arguments for the overload
+  std::function<ast::ExpressionList(ast::Builder*)> args;
+};
+
+inline std::ostream& operator<<(std::ostream& out,
+                                const TextureOverloadCase& data) {
+  out << "TextureOverloadCase" << static_cast<int>(data.overload) << "\n";
+  out << data.description << "\n";
+  out << "texture_kind:      " << data.texture_kind << "\n";
+  out << "sampler_kind:      " << data.sampler_kind << "\n";
+  out << "texture_dimension: " << data.texture_dimension << "\n";
+  out << "texture_data_type: " << data.texture_data_type << "\n";
+  return out;
+}
+
+}  // namespace test
+}  // namespace intrinsic
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_AST_INTRINSIC_TEXTURE_HELPER_TEST_H_
diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc
index d1d39e6..eb2a3a0 100644
--- a/src/inspector/inspector_test.cc
+++ b/src/inspector/inspector_test.cc
@@ -529,6 +529,44 @@
                                  body);
   }
 
+  /// Generates a function that references a specific sampler variable
+  /// @param func_name name of the function created
+  /// @param texture_name name of the texture to be sampled
+  /// @param sampler_name name of the sampler to use
+  /// @param coords_name name of the coords variable to use
+  /// @param array_index name of the array index variable to use
+  /// @returns a function that references all of the values specified
+  ast::Function* MakeSamplerReferenceBodyFunction(
+      const std::string& func_name,
+      const std::string& texture_name,
+      const std::string& sampler_name,
+      const std::string& coords_name,
+      const std::string& array_index,
+      ast::type::Type* base_type) {
+    std::string result_name = "sampler_result";
+
+    auto* body = create<ast::BlockStatement>();
+
+    auto* call_result = create<ast::Variable>(
+        "sampler_result", ast::StorageClass::kFunction, vec_type(base_type, 4));
+    body->append(create<ast::VariableDeclStatement>(call_result));
+
+    ast::ExpressionList call_params;
+    call_params.push_back(create<ast::IdentifierExpression>(texture_name));
+    call_params.push_back(create<ast::IdentifierExpression>(sampler_name));
+    call_params.push_back(create<ast::IdentifierExpression>(coords_name));
+    call_params.push_back(create<ast::IdentifierExpression>(array_index));
+    auto* call_expr = create<ast::CallExpression>(
+        create<ast::IdentifierExpression>("textureSample"), call_params);
+
+    body->append(create<ast::AssignmentStatement>(
+        create<ast::IdentifierExpression>("sampler_result"), call_expr));
+    body->append(create<ast::ReturnStatement>());
+
+    return create<ast::Function>(func_name, ast::VariableList(), void_type(),
+                                 body);
+  }
+
   /// Generates a function that references a specific comparison sampler
   /// variable.
   /// @param func_name name of the function created
@@ -683,6 +721,9 @@
       public testing::Test {};
 class InspectorGetSampledTextureResourceBindingsTest : public InspectorHelper,
                                                        public testing::Test {};
+class InspectorGetSampledArrayTextureResourceBindingsTest
+    : public InspectorHelper,
+      public testing::Test {};
 struct GetSampledTextureTestParams {
   ast::type::TextureDimension type_dim;
   inspector::ResourceBinding::TextureDimension inspector_dim;
@@ -691,11 +732,19 @@
 class InspectorGetSampledTextureResourceBindingsTestWithParam
     : public InspectorHelper,
       public testing::TestWithParam<GetSampledTextureTestParams> {};
-
+class InspectorGetSampledArrayTextureResourceBindingsTestWithParam
+    : public InspectorHelper,
+      public testing::TestWithParam<GetSampledTextureTestParams> {};
 class InspectorGetMultisampledTextureResourceBindingsTest
     : public InspectorHelper,
       public testing::Test {};
+class InspectorGetMultisampledArrayTextureResourceBindingsTest
+    : public InspectorHelper,
+      public testing::Test {};
 typedef GetSampledTextureTestParams GetMultisampledTextureTestParams;
+class InspectorGetMultisampledArrayTextureResourceBindingsTestWithParam
+    : public InspectorHelper,
+      public testing::TestWithParam<GetMultisampledTextureTestParams> {};
 class InspectorGetMultisampledTextureResourceBindingsTestWithParam
     : public InspectorHelper,
       public testing::TestWithParam<GetMultisampledTextureTestParams> {};
@@ -2009,18 +2058,6 @@
             inspector::ResourceBinding::TextureDimension::k1d,
             inspector::ResourceBinding::SampledKind::kUInt},
         GetSampledTextureTestParams{
-            ast::type::TextureDimension::k1dArray,
-            inspector::ResourceBinding::TextureDimension::k1dArray,
-            inspector::ResourceBinding::SampledKind::kFloat},
-        GetSampledTextureTestParams{
-            ast::type::TextureDimension::k1dArray,
-            inspector::ResourceBinding::TextureDimension::k1dArray,
-            inspector::ResourceBinding::SampledKind::kSInt},
-        GetSampledTextureTestParams{
-            ast::type::TextureDimension::k1dArray,
-            inspector::ResourceBinding::TextureDimension::k1dArray,
-            inspector::ResourceBinding::SampledKind::kUInt},
-        GetSampledTextureTestParams{
             ast::type::TextureDimension::k2d,
             inspector::ResourceBinding::TextureDimension::k2d,
             inspector::ResourceBinding::SampledKind::kFloat},
@@ -2033,18 +2070,6 @@
             inspector::ResourceBinding::TextureDimension::k2d,
             inspector::ResourceBinding::SampledKind::kUInt},
         GetSampledTextureTestParams{
-            ast::type::TextureDimension::k2dArray,
-            inspector::ResourceBinding::TextureDimension::k2dArray,
-            inspector::ResourceBinding::SampledKind::kFloat},
-        GetSampledTextureTestParams{
-            ast::type::TextureDimension::k2dArray,
-            inspector::ResourceBinding::TextureDimension::k2dArray,
-            inspector::ResourceBinding::SampledKind::kSInt},
-        GetSampledTextureTestParams{
-            ast::type::TextureDimension::k2dArray,
-            inspector::ResourceBinding::TextureDimension::k2dArray,
-            inspector::ResourceBinding::SampledKind::kUInt},
-        GetSampledTextureTestParams{
             ast::type::TextureDimension::k3d,
             inspector::ResourceBinding::TextureDimension::k3d,
             inspector::ResourceBinding::SampledKind::kFloat},
@@ -2067,6 +2092,65 @@
         GetSampledTextureTestParams{
             ast::type::TextureDimension::kCube,
             inspector::ResourceBinding::TextureDimension::kCube,
+            inspector::ResourceBinding::SampledKind::kUInt}));
+
+TEST_P(InspectorGetSampledArrayTextureResourceBindingsTestWithParam,
+       textureSample) {
+  auto sampled_texture_type = MakeSampledTextureType(
+      GetParam().type_dim, GetBaseType(GetParam().sampled_kind));
+  AddSampledTexture("foo_texture", sampled_texture_type.get(), 0, 0);
+  AddSampler("foo_sampler", 0, 1);
+  auto* coord_type =
+      GetCoordsType(GetParam().type_dim, GetParam().sampled_kind);
+  AddGlobalVariable("foo_coords", coord_type);
+  AddGlobalVariable("foo_array_index", u32_type());
+
+  auto* func = MakeSamplerReferenceBodyFunction(
+      "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_array_index",
+      GetBaseType(GetParam().sampled_kind));
+  func->add_decoration(
+      create<ast::StageDecoration>(ast::PipelineStage::kVertex, Source{}));
+  mod()->AddFunction(func);
+
+  ASSERT_TRUE(td()->Determine()) << td()->error();
+
+  auto result = inspector()->GetSampledTextureResourceBindings("ep");
+  ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
+
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ(0u, result[0].bind_group);
+  EXPECT_EQ(0u, result[0].binding);
+  EXPECT_EQ(GetParam().inspector_dim, result[0].dim);
+  EXPECT_EQ(GetParam().sampled_kind, result[0].sampled_kind);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    InspectorGetSampledArrayTextureResourceBindingsTest,
+    InspectorGetSampledArrayTextureResourceBindingsTestWithParam,
+    testing::Values(
+        GetSampledTextureTestParams{
+            ast::type::TextureDimension::k1dArray,
+            inspector::ResourceBinding::TextureDimension::k1dArray,
+            inspector::ResourceBinding::SampledKind::kFloat},
+        GetSampledTextureTestParams{
+            ast::type::TextureDimension::k1dArray,
+            inspector::ResourceBinding::TextureDimension::k1dArray,
+            inspector::ResourceBinding::SampledKind::kSInt},
+        GetSampledTextureTestParams{
+            ast::type::TextureDimension::k1dArray,
+            inspector::ResourceBinding::TextureDimension::k1dArray,
+            inspector::ResourceBinding::SampledKind::kUInt},
+        GetSampledTextureTestParams{
+            ast::type::TextureDimension::k2dArray,
+            inspector::ResourceBinding::TextureDimension::k2dArray,
+            inspector::ResourceBinding::SampledKind::kFloat},
+        GetSampledTextureTestParams{
+            ast::type::TextureDimension::k2dArray,
+            inspector::ResourceBinding::TextureDimension::k2dArray,
+            inspector::ResourceBinding::SampledKind::kSInt},
+        GetSampledTextureTestParams{
+            ast::type::TextureDimension::k2dArray,
+            inspector::ResourceBinding::TextureDimension::k2dArray,
             inspector::ResourceBinding::SampledKind::kUInt},
         GetSampledTextureTestParams{
             ast::type::TextureDimension::kCubeArray,
@@ -2081,18 +2165,6 @@
             inspector::ResourceBinding::TextureDimension::kCubeArray,
             inspector::ResourceBinding::SampledKind::kUInt}));
 
-TEST_F(InspectorGetMultisampledTextureResourceBindingsTest, Empty) {
-  auto* foo = MakeEmptyBodyFunction("foo");
-  foo->add_decoration(
-      create<ast::StageDecoration>(ast::PipelineStage::kVertex, Source{}));
-  mod()->AddFunction(foo);
-
-  auto result = inspector()->GetSampledTextureResourceBindings("foo");
-  ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
-
-  EXPECT_EQ(0u, result.size());
-}
-
 TEST_P(InspectorGetMultisampledTextureResourceBindingsTestWithParam,
        textureSample) {
   auto multisampled_texture_type = MakeMultisampledTextureType(
@@ -2139,18 +2211,6 @@
             inspector::ResourceBinding::TextureDimension::k1d,
             inspector::ResourceBinding::SampledKind::kUInt},
         GetMultisampledTextureTestParams{
-            ast::type::TextureDimension::k1dArray,
-            inspector::ResourceBinding::TextureDimension::k1dArray,
-            inspector::ResourceBinding::SampledKind::kFloat},
-        GetMultisampledTextureTestParams{
-            ast::type::TextureDimension::k1dArray,
-            inspector::ResourceBinding::TextureDimension::k1dArray,
-            inspector::ResourceBinding::SampledKind::kSInt},
-        GetMultisampledTextureTestParams{
-            ast::type::TextureDimension::k1dArray,
-            inspector::ResourceBinding::TextureDimension::k1dArray,
-            inspector::ResourceBinding::SampledKind::kUInt},
-        GetMultisampledTextureTestParams{
             ast::type::TextureDimension::k2d,
             inspector::ResourceBinding::TextureDimension::k2d,
             inspector::ResourceBinding::SampledKind::kFloat},
@@ -2161,6 +2221,65 @@
         GetMultisampledTextureTestParams{
             ast::type::TextureDimension::k2d,
             inspector::ResourceBinding::TextureDimension::k2d,
+            inspector::ResourceBinding::SampledKind::kUInt}));
+
+TEST_F(InspectorGetMultisampledArrayTextureResourceBindingsTest, Empty) {
+  auto* foo = MakeEmptyBodyFunction("foo");
+  foo->add_decoration(
+      create<ast::StageDecoration>(ast::PipelineStage::kVertex, Source{}));
+  mod()->AddFunction(foo);
+
+  auto result = inspector()->GetSampledTextureResourceBindings("foo");
+  ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
+
+  EXPECT_EQ(0u, result.size());
+}
+
+TEST_P(InspectorGetMultisampledArrayTextureResourceBindingsTestWithParam,
+       textureSample) {
+  auto multisampled_texture_type = MakeMultisampledTextureType(
+      GetParam().type_dim, GetBaseType(GetParam().sampled_kind));
+  AddMultisampledTexture("foo_texture", multisampled_texture_type.get(), 0, 0);
+  AddSampler("foo_sampler", 0, 1);
+  auto* coord_type =
+      GetCoordsType(GetParam().type_dim, GetParam().sampled_kind);
+  AddGlobalVariable("foo_coords", coord_type);
+  AddGlobalVariable("foo_array_index", u32_type());
+
+  auto* func = MakeSamplerReferenceBodyFunction(
+      "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_array_index",
+      GetBaseType(GetParam().sampled_kind));
+  func->add_decoration(
+      create<ast::StageDecoration>(ast::PipelineStage::kVertex, Source{}));
+  mod()->AddFunction(func);
+
+  ASSERT_TRUE(td()->Determine()) << td()->error();
+
+  auto result = inspector()->GetMultisampledTextureResourceBindings("ep");
+  ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
+
+  ASSERT_EQ(1u, result.size());
+  EXPECT_EQ(0u, result[0].bind_group);
+  EXPECT_EQ(0u, result[0].binding);
+  EXPECT_EQ(GetParam().inspector_dim, result[0].dim);
+  EXPECT_EQ(GetParam().sampled_kind, result[0].sampled_kind);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    InspectorGetMultisampledArrayTextureResourceBindingsTest,
+    InspectorGetMultisampledArrayTextureResourceBindingsTestWithParam,
+    testing::Values(
+        GetMultisampledTextureTestParams{
+            ast::type::TextureDimension::k1dArray,
+            inspector::ResourceBinding::TextureDimension::k1dArray,
+            inspector::ResourceBinding::SampledKind::kFloat},
+        GetMultisampledTextureTestParams{
+            ast::type::TextureDimension::k1dArray,
+            inspector::ResourceBinding::TextureDimension::k1dArray,
+            inspector::ResourceBinding::SampledKind::kSInt},
+        GetMultisampledTextureTestParams{
+            ast::type::TextureDimension::k1dArray,
+            inspector::ResourceBinding::TextureDimension::k1dArray,
             inspector::ResourceBinding::SampledKind::kUInt},
         GetMultisampledTextureTestParams{
             ast::type::TextureDimension::k2dArray,
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index a4d2382..6208fef 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -552,26 +552,7 @@
     return true;
   }
   if (ast::intrinsic::IsTextureIntrinsic(ident->intrinsic())) {
-    // TODO(dsinclair): Remove the LOD param from textureLoad on storage
-    // textures when https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
-    uint32_t num_of_params =
-        (ident->intrinsic() == ast::Intrinsic::kTextureLoad ||
-         ident->intrinsic() == ast::Intrinsic::kTextureSample)
-            ? 3
-            : 4;
-    if (expr->params().size() != num_of_params) {
-      set_error(expr->source(),
-                "incorrect number of parameters for " + ident->name() +
-                    ", got " + std::to_string(expr->params().size()) +
-                    " and expected " + std::to_string(num_of_params));
-      return false;
-    }
-
-    if (ident->intrinsic() == ast::Intrinsic::kTextureSampleCompare) {
-      expr->func()->set_result_type(
-          ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()));
-      return true;
-    }
+    ast::intrinsic::TextureSignature::Parameters param;
 
     auto* texture_param = expr->params()[0];
     if (!texture_param->result_type()->UnwrapPtrIfNeeded()->IsTexture()) {
@@ -581,6 +562,115 @@
     ast::type::TextureType* texture =
         texture_param->result_type()->UnwrapPtrIfNeeded()->AsTexture();
 
+    bool is_array = false;
+    switch (texture->dim()) {
+      case ast::type::TextureDimension::k1dArray:
+      case ast::type::TextureDimension::k2dArray:
+      case ast::type::TextureDimension::kCubeArray:
+        is_array = true;
+        break;
+      default:
+        break;
+    }
+    switch (ident->intrinsic()) {
+      case ast::Intrinsic::kTextureLoad:
+        param.idx.texture = param.count++;
+        param.idx.coords = param.count++;
+        if (is_array) {
+          param.idx.array_index = param.count++;
+        }
+
+        // TODO(dsinclair): Remove the LOD param from textureLoad on storage
+        // textures when https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
+        if (expr->params().size() > param.count) {
+          param.idx.level = param.count++;
+        }
+
+        break;
+      case ast::Intrinsic::kTextureSample:
+        param.idx.texture = param.count++;
+        param.idx.sampler = param.count++;
+        param.idx.coords = param.count++;
+        if (is_array) {
+          param.idx.array_index = param.count++;
+        }
+        if (expr->params().size() > param.count) {
+          param.idx.offset = param.count++;
+        }
+        break;
+      case ast::Intrinsic::kTextureSampleBias:
+        param.idx.texture = param.count++;
+        param.idx.sampler = param.count++;
+        param.idx.coords = param.count++;
+        if (is_array) {
+          param.idx.array_index = param.count++;
+        }
+        param.idx.bias = param.count++;
+        if (expr->params().size() > param.count) {
+          param.idx.offset = param.count++;
+        }
+        break;
+      case ast::Intrinsic::kTextureSampleLevel:
+        param.idx.texture = param.count++;
+        param.idx.sampler = param.count++;
+        param.idx.coords = param.count++;
+        if (is_array) {
+          param.idx.array_index = param.count++;
+        }
+        param.idx.level = param.count++;
+        if (expr->params().size() > param.count) {
+          param.idx.offset = param.count++;
+        }
+        break;
+      case ast::Intrinsic::kTextureSampleCompare:
+        param.idx.texture = param.count++;
+        param.idx.sampler = param.count++;
+        param.idx.coords = param.count++;
+        if (is_array) {
+          param.idx.array_index = param.count++;
+        }
+        param.idx.depth_ref = param.count++;
+        if (expr->params().size() > param.count) {
+          param.idx.offset = param.count++;
+        }
+        break;
+      case ast::Intrinsic::kTextureSampleGrad:
+        param.idx.texture = param.count++;
+        param.idx.sampler = param.count++;
+        param.idx.coords = param.count++;
+        if (is_array) {
+          param.idx.array_index = param.count++;
+        }
+        param.idx.ddx = param.count++;
+        param.idx.ddy = param.count++;
+        if (expr->params().size() > param.count) {
+          param.idx.offset = param.count++;
+        }
+        break;
+      default:
+        set_error(expr->source(),
+                  "Internal compiler error: Unreachable intrinsic " +
+                      std::to_string(static_cast<int>(ident->intrinsic())));
+        return false;
+    }
+
+    if (expr->params().size() != param.count) {
+      set_error(expr->source(),
+                "incorrect number of parameters for " + ident->name() +
+                    ", got " + std::to_string(expr->params().size()) +
+                    " and expected " + std::to_string(param.count));
+      return false;
+    }
+
+    ident->set_intrinsic_signature(
+        std::make_unique<ast::intrinsic::TextureSignature>(param));
+
+    if (texture->IsDepth()) {
+      expr->func()->set_result_type(
+          ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()));
+      return true;
+    }
+
     if (!texture->IsStorage() &&
         !(texture->IsSampled() || texture->IsMultisampled())) {
       set_error(expr->source(), "invalid texture for " + ident->name());
@@ -925,6 +1015,8 @@
     ident->set_intrinsic(ast::Intrinsic::kTextureSampleBias);
   } else if (ident->name() == "textureSampleCompare") {
     ident->set_intrinsic(ast::Intrinsic::kTextureSampleCompare);
+  } else if (ident->name() == "textureSampleGrad") {
+    ident->set_intrinsic(ast::Intrinsic::kTextureSampleGrad);
   } else if (ident->name() == "textureSampleLevel") {
     ident->set_intrinsic(ast::Intrinsic::kTextureSampleLevel);
   } else if (ident->name() == "trunc") {
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index c12c0d2..66094a2 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -14,6 +14,7 @@
 
 #include "src/type_determiner.h"
 
+#include <algorithm>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -26,6 +27,7 @@
 #include "src/ast/block_statement.h"
 #include "src/ast/bool_literal.h"
 #include "src/ast/break_statement.h"
+#include "src/ast/builder.h"
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/case_statement.h"
@@ -34,6 +36,7 @@
 #include "src/ast/float_literal.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/if_statement.h"
+#include "src/ast/intrinsic_texture_helper_test.h"
 #include "src/ast/loop_statement.h"
 #include "src/ast/member_accessor_expression.h"
 #include "src/ast/pipeline_stage.h"
@@ -79,27 +82,17 @@
   void to_str(std::ostream&, size_t) const override {}
 };
 
-class TypeDeterminerHelper {
+class TypeDeterminerHelper : public ast::BuilderWithContextAndModule {
  public:
-  TypeDeterminerHelper()
-      : td_(std::make_unique<TypeDeterminer>(&ctx_, &mod_)) {}
+  TypeDeterminerHelper() : td_(std::make_unique<TypeDeterminer>(ctx, mod)) {}
 
   TypeDeterminer* td() const { return td_.get(); }
-  ast::Module* mod() { return &mod_; }
-  Context* ctx() { return &ctx_; }
-
-  /// Creates a new `ast::Node` owned by the Module. When the Module is
-  /// destructed, the `ast::Node` will also be destructed.
-  /// @param args the arguments to pass to the type constructor
-  /// @returns the node pointer
-  template <typename T, typename... ARGS>
-  T* create(ARGS&&... args) {
-    return mod_.create<T>(std::forward<ARGS>(args)...);
-  }
 
  private:
-  Context ctx_;
-  ast::Module mod_;
+  void OnVariableBuilt(ast::Variable* var) override {
+    td_->RegisterVariableForTesting(var);
+  }
+
   std::unique_ptr<TypeDeterminer> td_;
 };
 
@@ -350,7 +343,7 @@
   ast::VariableList params;
   auto* func = create<ast::Function>("my_func", params, &f32,
                                      create<ast::BlockStatement>());
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   // Register the function
   EXPECT_TRUE(td()->Determine());
@@ -379,12 +372,12 @@
   main_body->append(create<ast::CallStatement>(call_expr));
   main_body->append(create<ast::ReturnStatement>());
   auto* func_main = create<ast::Function>("main", params0, &f32, main_body);
-  mod()->AddFunction(func_main);
+  mod->AddFunction(func_main);
 
   auto* body = create<ast::BlockStatement>();
   body->append(create<ast::ReturnStatement>());
   auto* func = create<ast::Function>("func", params0, &f32, body);
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   EXPECT_FALSE(td()->Determine()) << td()->error();
   EXPECT_EQ(td()->error(),
@@ -412,7 +405,7 @@
       create<ast::SintLiteral>(&i32, 2)));
   auto* init = var->constructor();
 
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   EXPECT_TRUE(td()->Determine());
   ASSERT_NE(init->result_type(), nullptr);
@@ -436,7 +429,7 @@
       create<ast::SintLiteral>(&i32, 2));
   auto* var =
       create<ast::Variable>("my_var", ast::StorageClass::kFunction, &ary);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -461,7 +454,7 @@
       create<ast::SintLiteral>(&i32, 2));
   auto* var =
       create<ast::Variable>("my_var", ast::StorageClass::kFunction, &aary);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -486,7 +479,7 @@
   auto* var =
       create<ast::Variable>("my_var", ast::StorageClass::kFunction, &ary);
   var->set_is_const(true);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -506,7 +499,7 @@
   auto* idx = create<ast::ScalarConstructorExpression>(
       create<ast::SintLiteral>(&i32, 2));
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &mat);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -532,7 +525,7 @@
   auto* idx2 = create<ast::ScalarConstructorExpression>(
       create<ast::SintLiteral>(&i32, 1));
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &mat);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -558,7 +551,7 @@
   auto* idx = create<ast::ScalarConstructorExpression>(
       create<ast::SintLiteral>(&i32, 2));
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &vec);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -592,7 +585,7 @@
   ast::VariableList params;
   auto* func = create<ast::Function>("my_func", params, &f32,
                                      create<ast::BlockStatement>());
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   // Register the function
   EXPECT_TRUE(td()->Determine());
@@ -611,7 +604,7 @@
   ast::VariableList params;
   auto* func = create<ast::Function>("my_func", params, &f32,
                                      create<ast::BlockStatement>());
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   // Register the function
   EXPECT_TRUE(td()->Determine());
@@ -695,7 +688,7 @@
 TEST_F(TypeDeterminerTest, Expr_Identifier_GlobalVariable) {
   ast::type::F32Type f32;
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &f32);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -711,7 +704,7 @@
   ast::type::F32Type f32;
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &f32);
   var->set_is_const(true);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -792,7 +785,7 @@
   ast::VariableList params;
   auto* func = create<ast::Function>("my_func", params, &f32,
                                      create<ast::BlockStatement>());
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   // Register the function
   EXPECT_TRUE(td()->Determine());
@@ -822,11 +815,11 @@
   auto* priv_var =
       create<ast::Variable>("priv_var", ast::StorageClass::kPrivate, &f32);
 
-  mod()->AddGlobalVariable(in_var);
-  mod()->AddGlobalVariable(out_var);
-  mod()->AddGlobalVariable(sb_var);
-  mod()->AddGlobalVariable(wg_var);
-  mod()->AddGlobalVariable(priv_var);
+  mod->AddGlobalVariable(in_var);
+  mod->AddGlobalVariable(out_var);
+  mod->AddGlobalVariable(sb_var);
+  mod->AddGlobalVariable(wg_var);
+  mod->AddGlobalVariable(priv_var);
 
   ast::VariableList params;
   auto* body = create<ast::BlockStatement>();
@@ -844,7 +837,7 @@
       create<ast::IdentifierExpression>("priv_var")));
   auto* func = create<ast::Function>("my_func", params, &f32, body);
 
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   // Register the function
   EXPECT_TRUE(td()->Determine());
@@ -872,11 +865,11 @@
   auto* priv_var =
       create<ast::Variable>("priv_var", ast::StorageClass::kPrivate, &f32);
 
-  mod()->AddGlobalVariable(in_var);
-  mod()->AddGlobalVariable(out_var);
-  mod()->AddGlobalVariable(sb_var);
-  mod()->AddGlobalVariable(wg_var);
-  mod()->AddGlobalVariable(priv_var);
+  mod->AddGlobalVariable(in_var);
+  mod->AddGlobalVariable(out_var);
+  mod->AddGlobalVariable(sb_var);
+  mod->AddGlobalVariable(wg_var);
+  mod->AddGlobalVariable(priv_var);
 
   auto* body = create<ast::BlockStatement>();
   body->append(create<ast::AssignmentStatement>(
@@ -894,7 +887,7 @@
   ast::VariableList params;
   auto* func = create<ast::Function>("my_func", params, &f32, body);
 
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   body = create<ast::BlockStatement>();
   body->append(create<ast::AssignmentStatement>(
@@ -903,7 +896,7 @@
                                   ast::ExpressionList{})));
   auto* func2 = create<ast::Function>("func", params, &f32, body);
 
-  mod()->AddFunction(func2);
+  mod->AddFunction(func2);
 
   // Register the function
   EXPECT_TRUE(td()->Determine());
@@ -933,7 +926,7 @@
   ast::VariableList params;
   auto* func = create<ast::Function>("my_func", params, &f32, body);
 
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ast::Variable v("var", ast::StorageClass::kFunction, &f32);
   td()->RegisterVariableForTesting(&v);
@@ -959,7 +952,7 @@
 
   auto* var = create<ast::Variable>("my_struct", ast::StorageClass::kNone, &st);
 
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -993,7 +986,7 @@
   auto* var =
       create<ast::Variable>("my_struct", ast::StorageClass::kNone, &alias);
 
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -1015,7 +1008,7 @@
   ast::type::VectorType vec3(&f32, 3);
 
   auto* var = create<ast::Variable>("my_vec", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -1036,7 +1029,7 @@
   ast::type::VectorType vec3(&f32, 3);
 
   auto* var = create<ast::Variable>("my_vec", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -1100,7 +1093,7 @@
   ast::type::StructType stA("A", strctA);
 
   auto* var = create<ast::Variable>("c", ast::StorageClass::kNone, &stA);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -1133,7 +1126,7 @@
   ast::type::I32Type i32;
 
   auto* var = create<ast::Variable>("val", ast::StorageClass::kNone, &i32);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1153,7 +1146,7 @@
   ast::type::VectorType vec3(&i32, 3);
 
   auto* var = create<ast::Variable>("val", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1187,7 +1180,7 @@
 
   auto* var =
       create<ast::Variable>("val", ast::StorageClass::kNone, &bool_type);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1207,7 +1200,7 @@
   ast::type::VectorType vec3(&bool_type, 3);
 
   auto* var = create<ast::Variable>("val", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1233,7 +1226,7 @@
   ast::type::I32Type i32;
 
   auto* var = create<ast::Variable>("val", ast::StorageClass::kNone, &i32);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1253,7 +1246,7 @@
   ast::type::VectorType vec3(&i32, 3);
 
   auto* var = create<ast::Variable>("val", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1280,7 +1273,7 @@
   ast::type::I32Type i32;
 
   auto* var = create<ast::Variable>("val", ast::StorageClass::kNone, &i32);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1302,8 +1295,8 @@
       create<ast::Variable>("scalar", ast::StorageClass::kNone, &f32);
   auto* vector =
       create<ast::Variable>("vector", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(scalar);
-  mod()->AddGlobalVariable(vector);
+  mod->AddGlobalVariable(scalar);
+  mod->AddGlobalVariable(vector);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1327,8 +1320,8 @@
       create<ast::Variable>("scalar", ast::StorageClass::kNone, &f32);
   auto* vector =
       create<ast::Variable>("vector", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(scalar);
-  mod()->AddGlobalVariable(vector);
+  mod->AddGlobalVariable(scalar);
+  mod->AddGlobalVariable(vector);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1350,7 +1343,7 @@
 
   auto* vector =
       create<ast::Variable>("vector", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(vector);
+  mod->AddGlobalVariable(vector);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1374,8 +1367,8 @@
       create<ast::Variable>("scalar", ast::StorageClass::kNone, &f32);
   auto* matrix =
       create<ast::Variable>("matrix", ast::StorageClass::kNone, &mat3x2);
-  mod()->AddGlobalVariable(scalar);
-  mod()->AddGlobalVariable(matrix);
+  mod->AddGlobalVariable(scalar);
+  mod->AddGlobalVariable(matrix);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1402,8 +1395,8 @@
       create<ast::Variable>("scalar", ast::StorageClass::kNone, &f32);
   auto* matrix =
       create<ast::Variable>("matrix", ast::StorageClass::kNone, &mat3x2);
-  mod()->AddGlobalVariable(scalar);
-  mod()->AddGlobalVariable(matrix);
+  mod->AddGlobalVariable(scalar);
+  mod->AddGlobalVariable(matrix);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1431,8 +1424,8 @@
       create<ast::Variable>("vector", ast::StorageClass::kNone, &vec3);
   auto* matrix =
       create<ast::Variable>("matrix", ast::StorageClass::kNone, &mat3x2);
-  mod()->AddGlobalVariable(vector);
-  mod()->AddGlobalVariable(matrix);
+  mod->AddGlobalVariable(vector);
+  mod->AddGlobalVariable(matrix);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1457,8 +1450,8 @@
       create<ast::Variable>("vector", ast::StorageClass::kNone, &vec3);
   auto* matrix =
       create<ast::Variable>("matrix", ast::StorageClass::kNone, &mat3x2);
-  mod()->AddGlobalVariable(vector);
-  mod()->AddGlobalVariable(matrix);
+  mod->AddGlobalVariable(vector);
+  mod->AddGlobalVariable(matrix);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1483,8 +1476,8 @@
       create<ast::Variable>("mat4x3", ast::StorageClass::kNone, &mat4x3);
   auto* matrix2 =
       create<ast::Variable>("mat3x4", ast::StorageClass::kNone, &mat3x4);
-  mod()->AddGlobalVariable(matrix1);
-  mod()->AddGlobalVariable(matrix2);
+  mod->AddGlobalVariable(matrix1);
+  mod->AddGlobalVariable(matrix2);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -1510,7 +1503,7 @@
   ast::type::F32Type f32;
 
   auto* var = create<ast::Variable>("ident", ast::StorageClass::kNone, &f32);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -1533,7 +1526,7 @@
   ast::type::VectorType vec4(&f32, 4);
 
   auto* var = create<ast::Variable>("ident", ast::StorageClass::kNone, &vec4);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -1575,8 +1568,8 @@
 
   auto* var1 = create<ast::Variable>("ident1", ast::StorageClass::kNone, &vec4);
   auto* var2 = create<ast::Variable>("ident2", ast::StorageClass::kNone, &vec4);
-  mod()->AddGlobalVariable(var1);
-  mod()->AddGlobalVariable(var2);
+  mod->AddGlobalVariable(var1);
+  mod->AddGlobalVariable(var2);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -1610,7 +1603,7 @@
   ast::type::VectorType vec3(&bool_type, 3);
 
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("my_var"));
@@ -1637,7 +1630,7 @@
   ast::type::VectorType vec3(&f32, 3);
 
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("my_var"));
@@ -1661,7 +1654,7 @@
   ast::type::F32Type f32;
 
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &f32);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("my_var"));
@@ -1682,7 +1675,7 @@
   ast::type::F32Type f32;
 
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &f32);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   ast::ExpressionList call_params;
   ast::CallExpression expr(create<ast::IdentifierExpression>(name),
@@ -1700,7 +1693,7 @@
   ast::type::F32Type f32;
 
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &f32);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("my_var"));
@@ -1769,7 +1762,7 @@
                       ast::type::Type* type,
                       ast::ExpressionList* call_params) {
     auto* var = create<ast::Variable>(name, ast::StorageClass::kNone, type);
-    mod()->AddGlobalVariable(var);
+    mod->AddGlobalVariable(var);
     call_params->push_back(create<ast::IdentifierExpression>(name));
   }
 
@@ -1794,7 +1787,7 @@
   auto coords_type = get_coords_type(dim, &i32);
 
   ast::type::Type* texture_type =
-      ctx()->type_mgr().Get(std::make_unique<ast::type::StorageTextureType>(
+      ctx->type_mgr().Get(std::make_unique<ast::type::StorageTextureType>(
           dim, ast::AccessControl::kReadOnly, format));
 
   ast::ExpressionList call_params;
@@ -1891,184 +1884,9 @@
   EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u);
 }
 
-TEST_P(Intrinsic_SampledTextureOperation, TextureSample) {
-  auto dim = GetParam().dim;
-  auto type = GetParam().type;
-
-  auto s = subtype(type);
-  ast::type::F32Type f32;
-  auto sampler_type = std::make_unique<ast::type::SamplerType>(
-      ast::type::SamplerKind::kSampler);
-  auto coords_type = get_coords_type(dim, &f32);
-  auto texture_type =
-      std::make_unique<ast::type::SampledTextureType>(dim, s.get());
-
-  ast::ExpressionList call_params;
-
-  add_call_param("texture", texture_type.get(), &call_params);
-  add_call_param("sampler", sampler_type.get(), &call_params);
-  add_call_param("coords", coords_type.get(), &call_params);
-
-  ast::CallExpression expr(create<ast::IdentifierExpression>("textureSample"),
-                           call_params);
-
-  EXPECT_TRUE(td()->Determine());
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
-
-  ASSERT_NE(expr.result_type(), nullptr);
-  ASSERT_TRUE(expr.result_type()->IsVector());
-  if (type == TextureType::kF32) {
-    EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsF32());
-  } else if (type == TextureType::kI32) {
-    EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsI32());
-  } else {
-    EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsU32());
-  }
-  EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u);
-}
-
-TEST_P(Intrinsic_SampledTextureOperation, TextureSampleLevel) {
-  auto dim = GetParam().dim;
-  auto type = GetParam().type;
-
-  ast::type::F32Type f32;
-  auto s = subtype(type);
-  auto sampler_type = std::make_unique<ast::type::SamplerType>(
-      ast::type::SamplerKind::kSampler);
-  auto coords_type = get_coords_type(dim, &f32);
-  auto texture_type =
-      std::make_unique<ast::type::SampledTextureType>(dim, s.get());
-
-  ast::ExpressionList call_params;
-
-  add_call_param("texture", texture_type.get(), &call_params);
-  add_call_param("sampler", sampler_type.get(), &call_params);
-  add_call_param("coords", coords_type.get(), &call_params);
-  add_call_param("lod", &f32, &call_params);
-
-  ast::CallExpression expr(
-      create<ast::IdentifierExpression>("textureSampleLevel"), call_params);
-
-  EXPECT_TRUE(td()->Determine());
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
-
-  ASSERT_NE(expr.result_type(), nullptr);
-  ASSERT_TRUE(expr.result_type()->IsVector());
-  if (type == TextureType::kF32) {
-    EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsF32());
-  } else if (type == TextureType::kI32) {
-    EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsI32());
-  } else {
-    EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsU32());
-  }
-  EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u);
-}
-
-TEST_P(Intrinsic_SampledTextureOperation, TextureSampleBias) {
-  auto dim = GetParam().dim;
-  auto type = GetParam().type;
-
-  ast::type::F32Type f32;
-  auto s = subtype(type);
-  auto sampler_type = std::make_unique<ast::type::SamplerType>(
-      ast::type::SamplerKind::kSampler);
-  auto coords_type = get_coords_type(dim, &f32);
-  auto texture_type =
-      std::make_unique<ast::type::SampledTextureType>(dim, s.get());
-
-  ast::ExpressionList call_params;
-
-  add_call_param("texture", texture_type.get(), &call_params);
-  add_call_param("sampler", sampler_type.get(), &call_params);
-  add_call_param("coords", coords_type.get(), &call_params);
-  add_call_param("bias", &f32, &call_params);
-
-  ast::CallExpression expr(
-      create<ast::IdentifierExpression>("textureSampleBias"), call_params);
-
-  EXPECT_TRUE(td()->Determine());
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
-
-  ASSERT_NE(expr.result_type(), nullptr);
-  ASSERT_TRUE(expr.result_type()->IsVector());
-  if (type == TextureType::kF32) {
-    EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsF32());
-  } else if (type == TextureType::kI32) {
-    EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsI32());
-  } else {
-    EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsU32());
-  }
-  EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u);
-}
-
 INSTANTIATE_TEST_SUITE_P(
     TypeDeterminerTest,
     Intrinsic_SampledTextureOperation,
-    testing::Values(
-        TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kF32},
-        TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kI32},
-        TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kU32},
-        TextureTestParams{ast::type::TextureDimension::k1dArray,
-                          TextureType::kF32},
-        TextureTestParams{ast::type::TextureDimension::k1dArray,
-                          TextureType::kI32},
-        TextureTestParams{ast::type::TextureDimension::k1dArray,
-                          TextureType::kU32},
-        TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kF32},
-        TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kI32},
-        TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kU32},
-        TextureTestParams{ast::type::TextureDimension::k2dArray,
-                          TextureType::kF32},
-        TextureTestParams{ast::type::TextureDimension::k2dArray,
-                          TextureType::kI32},
-        TextureTestParams{ast::type::TextureDimension::k2dArray,
-                          TextureType::kU32},
-        TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kF32},
-        TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kI32},
-        TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kU32},
-        TextureTestParams{ast::type::TextureDimension::kCube,
-                          TextureType::kF32},
-        TextureTestParams{ast::type::TextureDimension::kCube,
-                          TextureType::kI32},
-        TextureTestParams{ast::type::TextureDimension::kCube,
-                          TextureType::kU32},
-        TextureTestParams{ast::type::TextureDimension::kCubeArray,
-                          TextureType::kF32},
-        TextureTestParams{ast::type::TextureDimension::kCubeArray,
-                          TextureType::kI32},
-        TextureTestParams{ast::type::TextureDimension::kCubeArray,
-                          TextureType::kU32}));
-
-using Intrinsic_DepthTextureOperation = Intrinsic_TextureOperation;
-TEST_P(Intrinsic_DepthTextureOperation, TextureSampleCompare) {
-  auto dim = GetParam().dim;
-
-  ast::type::F32Type f32;
-  auto sampler_type = std::make_unique<ast::type::SamplerType>(
-      ast::type::SamplerKind::kComparisonSampler);
-  auto coords_type = get_coords_type(dim, &f32);
-  auto texture_type = std::make_unique<ast::type::DepthTextureType>(dim);
-
-  ast::ExpressionList call_params;
-
-  add_call_param("texture", texture_type.get(), &call_params);
-  add_call_param("sampler_comparison", sampler_type.get(), &call_params);
-  add_call_param("coords", coords_type.get(), &call_params);
-  add_call_param("depth_reference", &f32, &call_params);
-
-  ast::CallExpression expr(
-      create<ast::IdentifierExpression>("textureSampleCompare"), call_params);
-
-  EXPECT_TRUE(td()->Determine());
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
-
-  ASSERT_NE(expr.result_type(), nullptr);
-  EXPECT_TRUE(expr.result_type()->IsF32());
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
-    Intrinsic_DepthTextureOperation,
     testing::Values(TextureTestParams{ast::type::TextureDimension::k2d},
                     TextureTestParams{ast::type::TextureDimension::k2dArray},
                     TextureTestParams{ast::type::TextureDimension::kCube},
@@ -2080,7 +1898,7 @@
   ast::type::VectorType vec3(&f32, 3);
 
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("my_var"));
@@ -2105,8 +1923,8 @@
   auto* var = create<ast::Variable>("my_var", ast::StorageClass::kNone, &vec3);
   auto* bool_var =
       create<ast::Variable>("bool_var", ast::StorageClass::kNone, &bool_vec3);
-  mod()->AddGlobalVariable(var);
-  mod()->AddGlobalVariable(bool_var);
+  mod->AddGlobalVariable(var);
+  mod->AddGlobalVariable(bool_var);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("my_var"));
@@ -2130,7 +1948,7 @@
   ast::type::VectorType vec3(&f32, 3);
 
   auto* var = create<ast::Variable>("v", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("v"));
@@ -2150,7 +1968,7 @@
   ast::type::VectorType vec3(&f32, 3);
 
   auto* var = create<ast::Variable>("v", ast::StorageClass::kNone, &vec3);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("v"));
@@ -2175,8 +1993,8 @@
 
   auto* var1 = create<ast::Variable>("v3", ast::StorageClass::kNone, &vec3);
   auto* var2 = create<ast::Variable>("v2", ast::StorageClass::kNone, &vec2);
-  mod()->AddGlobalVariable(var1);
-  mod()->AddGlobalVariable(var2);
+  mod->AddGlobalVariable(var1);
+  mod->AddGlobalVariable(var2);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("v3"));
@@ -2204,7 +2022,7 @@
   ast::type::VectorType vec2(&f32, 2);
 
   auto* var2 = create<ast::Variable>("v2", ast::StorageClass::kNone, &vec2);
-  mod()->AddGlobalVariable(var2);
+  mod->AddGlobalVariable(var2);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("v2"));
@@ -2224,7 +2042,7 @@
   ast::type::VectorType vec2(&f32, 2);
 
   auto* var2 = create<ast::Variable>("v2", ast::StorageClass::kNone, &vec2);
-  mod()->AddGlobalVariable(var2);
+  mod->AddGlobalVariable(var2);
 
   ast::ExpressionList call_params;
   call_params.push_back(create<ast::IdentifierExpression>("v2"));
@@ -2249,7 +2067,7 @@
   ast::type::VectorType vec4(&f32, 4);
 
   auto* var = create<ast::Variable>("ident", ast::StorageClass::kNone, &vec4);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   EXPECT_TRUE(td()->Determine());
@@ -2276,7 +2094,7 @@
   body->append(stmt);
   auto* func = create<ast::Function>("func", ast::VariableList{}, &i32, body);
 
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
   EXPECT_EQ(var->storage_class(), ast::StorageClass::kFunction);
@@ -2293,7 +2111,7 @@
   body->append(stmt);
   auto* func = create<ast::Function>("func", ast::VariableList{}, &i32, body);
 
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
   EXPECT_EQ(var->storage_class(), ast::StorageClass::kNone);
@@ -2309,7 +2127,7 @@
   body->append(stmt);
   auto* func = create<ast::Function>("func", ast::VariableList{}, &i32, body);
 
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   EXPECT_FALSE(td()->Determine());
   EXPECT_EQ(td()->error(),
@@ -2403,6 +2221,7 @@
         IntrinsicData{"textureSampleBias", ast::Intrinsic::kTextureSampleBias},
         IntrinsicData{"textureSampleCompare",
                       ast::Intrinsic::kTextureSampleCompare},
+        IntrinsicData{"textureSampleGrad", ast::Intrinsic::kTextureSampleGrad},
         IntrinsicData{"textureSampleLevel",
                       ast::Intrinsic::kTextureSampleLevel},
         IntrinsicData{"trunc", ast::Intrinsic::kTrunc}));
@@ -4358,7 +4177,7 @@
   ast::type::MatrixType mat(&f32, 3, 3);
 
   auto* var = create<ast::Variable>("var", ast::StorageClass::kFunction, &mat);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -4383,7 +4202,7 @@
   ast::type::F32Type f32;
 
   auto* var = create<ast::Variable>("var", ast::StorageClass::kFunction, &f32);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -4419,7 +4238,7 @@
   ast::type::MatrixType mat(&f32, 3, 3);
 
   auto* var = create<ast::Variable>("var", ast::StorageClass::kFunction, &mat);
-  mod()->AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td()->Determine()) << td()->error();
@@ -4495,21 +4314,21 @@
   ep_2->add_decoration(
       create<ast::StageDecoration>(ast::PipelineStage::kVertex, Source{}));
 
-  mod()->AddFunction(func_b);
-  mod()->AddFunction(func_c);
-  mod()->AddFunction(func_a);
-  mod()->AddFunction(ep_1);
-  mod()->AddFunction(ep_2);
+  mod->AddFunction(func_b);
+  mod->AddFunction(func_c);
+  mod->AddFunction(func_a);
+  mod->AddFunction(ep_1);
+  mod->AddFunction(ep_2);
 
-  mod()->AddGlobalVariable(
+  mod->AddGlobalVariable(
       create<ast::Variable>("first", ast::StorageClass::kPrivate, &f32));
-  mod()->AddGlobalVariable(
+  mod->AddGlobalVariable(
       create<ast::Variable>("second", ast::StorageClass::kPrivate, &f32));
-  mod()->AddGlobalVariable(
+  mod->AddGlobalVariable(
       create<ast::Variable>("call_a", ast::StorageClass::kPrivate, &f32));
-  mod()->AddGlobalVariable(
+  mod->AddGlobalVariable(
       create<ast::Variable>("call_b", ast::StorageClass::kPrivate, &f32));
-  mod()->AddGlobalVariable(
+  mod->AddGlobalVariable(
       create<ast::Variable>("call_c", ast::StorageClass::kPrivate, &f32));
 
   // Register the functions and calculate the callers
@@ -4533,5 +4352,238 @@
   EXPECT_TRUE(ep_2->ancestor_entry_points().empty());
 }
 
+using TypeDeterminerTextureIntrinsicTest =
+    TypeDeterminerTestWithParam<ast::intrinsic::test::TextureOverloadCase>;
+
+INSTANTIATE_TEST_SUITE_P(
+    TypeDeterminerTest,
+    TypeDeterminerTextureIntrinsicTest,
+    testing::ValuesIn(ast::intrinsic::test::TextureOverloadCase::ValidCases()));
+
+std::string to_str(const std::string& function,
+                   const ast::intrinsic::TextureSignature* sig) {
+  struct Parameter {
+    size_t idx;
+    std::string name;
+  };
+  std::vector<Parameter> params;
+  auto maybe_add_param = [&params](size_t idx, const char* name) {
+    if (idx != ast::intrinsic::TextureSignature::Parameters::kNotUsed) {
+      params.emplace_back(Parameter{idx, name});
+    }
+  };
+  maybe_add_param(sig->params.idx.array_index, "array_index");
+  maybe_add_param(sig->params.idx.bias, "bias");
+  maybe_add_param(sig->params.idx.coords, "coords");
+  maybe_add_param(sig->params.idx.depth_ref, "depth_ref");
+  maybe_add_param(sig->params.idx.ddx, "ddx");
+  maybe_add_param(sig->params.idx.ddy, "ddy");
+  maybe_add_param(sig->params.idx.level, "level");
+  maybe_add_param(sig->params.idx.offset, "offset");
+  maybe_add_param(sig->params.idx.sampler, "sampler");
+  maybe_add_param(sig->params.idx.texture, "texture");
+  std::sort(
+      params.begin(), params.end(),
+      [](const Parameter& a, const Parameter& b) { return a.idx < b.idx; });
+
+  std::stringstream out;
+  out << function << "(";
+  bool first = true;
+  for (auto& param : params) {
+    if (!first) {
+      out << ", ";
+    }
+    out << param.name;
+    first = false;
+  }
+  out << ")";
+  return out.str();
+}
+
+const char* expected_texture_overload(
+    ast::intrinsic::test::ValidTextureOverload overload) {
+  using ValidTextureOverload = ast::intrinsic::test::ValidTextureOverload;
+  switch (overload) {
+    case ValidTextureOverload::kSample1dF32:
+      return "textureSample(texture, sampler, coords)";
+    case ValidTextureOverload::kSample1dArrayF32:
+      return "textureSample(texture, sampler, coords, array_index)";
+    case ValidTextureOverload::kSample2dF32:
+      return "textureSample(texture, sampler, coords)";
+    case ValidTextureOverload::kSample2dOffsetF32:
+      return "textureSample(texture, sampler, coords, offset)";
+    case ValidTextureOverload::kSample2dArrayF32:
+      return "textureSample(texture, sampler, coords, array_index)";
+    case ValidTextureOverload::kSample2dArrayOffsetF32:
+      return "textureSample(texture, sampler, coords, array_index, offset)";
+    case ValidTextureOverload::kSample3dF32:
+      return "textureSample(texture, sampler, coords)";
+    case ValidTextureOverload::kSample3dOffsetF32:
+      return "textureSample(texture, sampler, coords, offset)";
+    case ValidTextureOverload::kSampleCubeF32:
+      return "textureSample(texture, sampler, coords)";
+    case ValidTextureOverload::kSampleCubeArrayF32:
+      return "textureSample(texture, sampler, coords, array_index)";
+    case ValidTextureOverload::kSampleDepth2dF32:
+      return "textureSample(texture, sampler, coords)";
+    case ValidTextureOverload::kSampleDepth2dOffsetF32:
+      return "textureSample(texture, sampler, coords, offset)";
+    case ValidTextureOverload::kSampleDepth2dArrayF32:
+      return "textureSample(texture, sampler, coords, array_index)";
+    case ValidTextureOverload::kSampleDepth2dArrayOffsetF32:
+      return "textureSample(texture, sampler, coords, array_index, offset)";
+    case ValidTextureOverload::kSampleDepthCubeF32:
+      return "textureSample(texture, sampler, coords)";
+    case ValidTextureOverload::kSampleDepthCubeArrayF32:
+      return "textureSample(texture, sampler, coords, array_index)";
+    case ValidTextureOverload::kSampleBias2dF32:
+      return "textureSampleBias(texture, sampler, coords, bias)";
+    case ValidTextureOverload::kSampleBias2dOffsetF32:
+      return "textureSampleBias(texture, sampler, coords, bias, offset)";
+    case ValidTextureOverload::kSampleBias2dArrayF32:
+      return "textureSampleBias(texture, sampler, coords, array_index, "
+             "bias)";
+    case ValidTextureOverload::kSampleBias2dArrayOffsetF32:
+      return "textureSampleBias(texture, sampler, coords, array_index, "
+             "bias, offset)";
+    case ValidTextureOverload::kSampleBias3dF32:
+      return "textureSampleBias(texture, sampler, coords, bias)";
+    case ValidTextureOverload::kSampleBias3dOffsetF32:
+      return "textureSampleBias(texture, sampler, coords, bias, offset)";
+    case ValidTextureOverload::kSampleBiasCubeF32:
+      return "textureSampleBias(texture, sampler, coords, bias)";
+    case ValidTextureOverload::kSampleBiasCubeArrayF32:
+      return "textureSampleBias(texture, sampler, coords, array_index, "
+             "bias)";
+    case ValidTextureOverload::kSampleLevel2dF32:
+      return "textureSampleLevel(texture, sampler, coords, level)";
+    case ValidTextureOverload::kSampleLevel2dOffsetF32:
+      return "textureSampleLevel(texture, sampler, coords, level, offset)";
+    case ValidTextureOverload::kSampleLevel2dArrayF32:
+      return "textureSampleLevel(texture, sampler, coords, array_index, "
+             "level)";
+    case ValidTextureOverload::kSampleLevel2dArrayOffsetF32:
+      return "textureSampleLevel(texture, sampler, coords, array_index, "
+             "level, offset)";
+    case ValidTextureOverload::kSampleLevel3dF32:
+      return "textureSampleLevel(texture, sampler, coords, level)";
+    case ValidTextureOverload::kSampleLevel3dOffsetF32:
+      return "textureSampleLevel(texture, sampler, coords, level, offset)";
+    case ValidTextureOverload::kSampleLevelCubeF32:
+      return "textureSampleLevel(texture, sampler, coords, level)";
+    case ValidTextureOverload::kSampleLevelCubeArrayF32:
+      return "textureSampleLevel(texture, sampler, coords, array_index, "
+             "level)";
+    case ValidTextureOverload::kSampleLevelDepth2dF32:
+      return "textureSampleLevel(texture, sampler, coords, level)";
+    case ValidTextureOverload::kSampleLevelDepth2dOffsetF32:
+      return "textureSampleLevel(texture, sampler, coords, level, offset)";
+    case ValidTextureOverload::kSampleLevelDepth2dArrayF32:
+      return "textureSampleLevel(texture, sampler, coords, array_index, level)";
+    case ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32:
+      return "textureSampleLevel(texture, sampler, coords, array_index, level, "
+             "offset)";
+    case ValidTextureOverload::kSampleLevelDepthCubeF32:
+      return "textureSampleLevel(texture, sampler, coords, level)";
+    case ValidTextureOverload::kSampleLevelDepthCubeArrayF32:
+      return "textureSampleLevel(texture, sampler, coords, array_index, "
+             "level)";
+    case ValidTextureOverload::kSampleGrad2dF32:
+      return "textureSampleGrad(texture, sampler, coords, ddx, ddy)";
+    case ValidTextureOverload::kSampleGrad2dOffsetF32:
+      return "textureSampleGrad(texture, sampler, coords, ddx, ddy, "
+             "offset)";
+    case ValidTextureOverload::kSampleGrad2dArrayF32:
+      return "textureSampleGrad(texture, sampler, coords, array_index, ddx, "
+             "ddy)";
+    case ValidTextureOverload::kSampleGrad2dArrayOffsetF32:
+      return "textureSampleGrad(texture, sampler, coords, array_index, ddx, "
+             "ddy, offset)";
+    case ValidTextureOverload::kSampleGrad3dF32:
+      return "textureSampleGrad(texture, sampler, coords, ddx, ddy)";
+    case ValidTextureOverload::kSampleGrad3dOffsetF32:
+      return "textureSampleGrad(texture, sampler, coords, ddx, ddy, "
+             "offset)";
+    case ValidTextureOverload::kSampleGradCubeF32:
+      return "textureSampleGrad(texture, sampler, coords, ddx, ddy)";
+    case ValidTextureOverload::kSampleGradCubeArrayF32:
+      return "textureSampleGrad(texture, sampler, coords, array_index, ddx, "
+             "ddy)";
+    case ValidTextureOverload::kSampleGradDepth2dF32:
+      return "textureSampleCompare(texture, sampler, coords, depth_ref)";
+    case ValidTextureOverload::kSampleGradDepth2dOffsetF32:
+      return "textureSampleCompare(texture, sampler, coords, depth_ref, "
+             "offset)";
+    case ValidTextureOverload::kSampleGradDepth2dArrayF32:
+      return "textureSampleCompare(texture, sampler, coords, array_index, "
+             "depth_ref)";
+    case ValidTextureOverload::kSampleGradDepth2dArrayOffsetF32:
+      return "textureSampleCompare(texture, sampler, coords, array_index, "
+             "depth_ref, offset)";
+    case ValidTextureOverload::kSampleGradDepthCubeF32:
+      return "textureSampleCompare(texture, sampler, coords, depth_ref)";
+    case ValidTextureOverload::kSampleGradDepthCubeArrayF32:
+      return "textureSampleCompare(texture, sampler, coords, array_index, "
+             "depth_ref)";
+  }
+  return "<unmatched texture overload>";
+}
+
+TEST_P(TypeDeterminerTextureIntrinsicTest, Call) {
+  auto param = GetParam();
+
+  ast::type::Type* datatype = nullptr;
+  switch (param.texture_data_type) {
+    case ast::intrinsic::test::TextureDataType::kF32:
+      datatype = ty.f32;
+      break;
+    case ast::intrinsic::test::TextureDataType::kU32:
+      datatype = ty.u32;
+      break;
+    case ast::intrinsic::test::TextureDataType::kI32:
+      datatype = ty.i32;
+      break;
+  }
+
+  ast::type::SamplerType sampler_type{param.sampler_kind};
+  switch (param.texture_kind) {
+    case ast::intrinsic::test::TextureKind::kRegular:
+      Var("texture", ast::StorageClass::kNone,
+          ctx->type_mgr().Get<ast::type::SampledTextureType>(
+              param.texture_dimension, datatype));
+      break;
+
+    case ast::intrinsic::test::TextureKind::kDepth:
+      Var("texture", ast::StorageClass::kNone,
+          ctx->type_mgr().Get<ast::type::DepthTextureType>(
+              param.texture_dimension));
+      break;
+  }
+
+  Var("sampler", ast::StorageClass::kNone, &sampler_type);
+
+  auto* ident = Expr(param.function);
+  ast::CallExpression call{ident, param.args(this)};
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+
+  switch (param.texture_kind) {
+    case ast::intrinsic::test::TextureKind::kRegular:
+      ASSERT_TRUE(call.result_type()->IsVector());
+      EXPECT_EQ(call.result_type()->AsVector()->type(), datatype);
+      break;
+
+    case ast::intrinsic::test::TextureKind::kDepth:
+      EXPECT_EQ(call.result_type(), ty.f32);
+      break;
+  }
+
+  auto* sig = static_cast<const ast::intrinsic::TextureSignature*>(
+      ident->intrinsic_signature());
+
+  auto* expected = expected_texture_overload(param.overload);
+  EXPECT_EQ(to_str(param.function, sig), expected);
+}
+
 }  // namespace
 }  // namespace tint
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 518e9ce..081a3a2 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -1749,9 +1749,15 @@
     return 0;
   }
 
+  auto intrinsic = ident->intrinsic();
+
+  if (ast::intrinsic::IsTextureIntrinsic(intrinsic)) {
+    GenerateTextureIntrinsic(ident, call, Operand::Int(result_type_id), result);
+    return result_id;
+  }
+
   OperandList params = {Operand::Int(result_type_id), result};
 
-  auto intrinsic = ident->intrinsic();
   if (ast::intrinsic::IsFineDerivative(intrinsic) ||
       ast::intrinsic::IsCoarseDerivative(intrinsic)) {
     push_capability(SpvCapabilityDerivativeControl);
@@ -1825,7 +1831,7 @@
     op = spv::Op::OpBitReverse;
   } else if (intrinsic == ast::Intrinsic::kSelect) {
     op = spv::Op::OpSelect;
-  } else if (!ast::intrinsic::IsTextureIntrinsic(intrinsic)) {
+  } else {
     GenerateGLSLstd450Import();
 
     auto set_iter = import_name_to_id_.find(kGLSLstd450);
@@ -1846,7 +1852,8 @@
 
     op = spv::Op::OpExtInst;
   }
-  if (!ast::intrinsic::IsTextureIntrinsic(intrinsic) && op == spv::Op::OpNop) {
+
+  if (op == spv::Op::OpNop) {
     error_ = "unable to determine operator for: " + ident->name();
     return 0;
   }
@@ -1854,15 +1861,11 @@
   for (auto* p : call->params()) {
     auto val_id = GenerateExpression(p);
     if (val_id == 0) {
-      return 0;
+      return false;
     }
     val_id = GenerateLoadIfNeeded(p->result_type(), val_id);
 
-    params.push_back(Operand::Int(val_id));
-  }
-
-  if (ast::intrinsic::IsTextureIntrinsic(intrinsic)) {
-    return GenerateTextureIntrinsic(ident, call, result_id, params);
+    params.emplace_back(Operand::Int(val_id));
   }
 
   push_function_inst(op, params);
@@ -1870,68 +1873,171 @@
   return result_id;
 }
 
-uint32_t Builder::GenerateTextureIntrinsic(ast::IdentifierExpression* ident,
-                                           ast::CallExpression* call,
-                                           uint32_t result_id,
-                                           OperandList wgsl_params) {
+void Builder::GenerateTextureIntrinsic(ast::IdentifierExpression* ident,
+                                       ast::CallExpression* call,
+                                       spirv::Operand result_type,
+                                       spirv::Operand result_id) {
   auto* texture_type =
       call->params()[0]->result_type()->UnwrapAll()->AsTexture();
 
-  // TODO(dsinclair): Remove the LOD param from textureLoad on storage textures
-  // when https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
+  auto* sig = static_cast<const ast::intrinsic::TextureSignature*>(
+      ident->intrinsic_signature());
+  assert(sig != nullptr);
+  auto& pidx = sig->params.idx;
+  auto const kNotUsed = ast::intrinsic::TextureSignature::Parameters::kNotUsed;
+
+  assert(pidx.texture != kNotUsed);
+
+  auto op = spv::Op::OpNop;
+
+  auto gen_param = [&](size_t idx) {
+    auto* p = call->params()[idx];
+    auto val_id = GenerateExpression(p);
+    if (val_id == 0) {
+      return Operand::Int(0);
+    }
+    val_id = GenerateLoadIfNeeded(p->result_type(), val_id);
+
+    return Operand::Int(val_id);
+  };
+
+  // Populate the spirv_params with common parameters
+  OperandList spirv_params;
+  spirv_params.reserve(8);  // Enough to fit most parameter lists
+  spirv_params.emplace_back(std::move(result_type));  // result type
+  spirv_params.emplace_back(std::move(result_id));    // result id
+
+  // Extra image operands, appended to spirv_params.
+  uint32_t spirv_operand_mask = 0;
+  OperandList spirv_operands;
+  spirv_operands.reserve(4);  // Enough to fit most parameter lists
+
   if (ident->intrinsic() == ast::Intrinsic::kTextureLoad) {
-    std::vector<Operand> spirv_params = {
-        std::move(wgsl_params[0]), std::move(wgsl_params[1]),
-        std::move(wgsl_params[2]), std::move(wgsl_params[3])};
-    if (texture_type->IsMultisampled()) {
-      spirv_params.push_back(Operand::Int(SpvImageOperandsSampleMask));
+    op = texture_type->IsStorage() ? spv::Op::OpImageRead
+                                   : spv::Op::OpImageFetch;
+    spirv_params.emplace_back(gen_param(pidx.texture));
+    spirv_params.emplace_back(gen_param(pidx.coords));
+
+    // TODO(dsinclair): Remove the LOD param from textureLoad on storage
+    // textures when https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
+    if (pidx.level != kNotUsed) {
+      if (texture_type->IsMultisampled()) {
+        spirv_operand_mask |= SpvImageOperandsSampleMask;
+      } else {
+        spirv_operand_mask |= SpvImageOperandsLodMask;
+      }
+      spirv_operands.emplace_back(gen_param(pidx.level));
+    }
+  } else {
+    assert(pidx.sampler != kNotUsed);
+
+    auto sampler_param = gen_param(pidx.sampler);
+    auto texture_param = gen_param(pidx.texture);
+    auto sampled_image =
+        GenerateSampledImage(texture_type, texture_param, sampler_param);
+
+    // Populate the spirv_params with the common parameters
+    spirv_params.emplace_back(Operand::Int(sampled_image));  // sampled image
+
+    if (pidx.array_index != kNotUsed) {
+      // Array index needs to be appended to the coordinates.
+      auto* param_coords = call->params()[pidx.coords];
+      auto* param_array_index = call->params()[pidx.array_index];
+
+      uint32_t packed_coords_size;
+      ast::type::Type* packed_coords_el_ty;  // Currenly must be f32.
+      if (param_coords->result_type()->IsVector()) {
+        auto* vec = param_coords->result_type()->AsVector();
+        packed_coords_size = vec->size() + 1;
+        packed_coords_el_ty = vec->type();
+      } else {
+        packed_coords_size = 2;
+        packed_coords_el_ty = param_coords->result_type();
+      }
+
+      // Cast param_array_index to the vector element type
+      ast::TypeConstructorExpression array_index_cast(packed_coords_el_ty,
+                                                      {param_array_index});
+      array_index_cast.set_result_type(packed_coords_el_ty);
+
+      ast::type::VectorType packed_coords_ty(packed_coords_el_ty,
+                                             packed_coords_size);
+
+      ast::TypeConstructorExpression constructor{
+          &packed_coords_ty, {param_coords, &array_index_cast}};
+      auto packed_coords =
+          GenerateTypeConstructorExpression(&constructor, false);
+
+      spirv_params.emplace_back(Operand::Int(packed_coords));  // coordinates
+
     } else {
-      spirv_params.push_back(Operand::Int(SpvImageOperandsLodMask));
+      spirv_params.emplace_back(gen_param(pidx.coords));  // coordinates
     }
-    spirv_params.push_back(std::move(wgsl_params[4]));
 
-    auto op = spv::Op::OpImageFetch;
-    if (texture_type->IsStorage()) {
-      op = spv::Op::OpImageRead;
+    switch (ident->intrinsic()) {
+      case ast::Intrinsic::kTextureSample: {
+        op = spv::Op::OpImageSampleImplicitLod;
+        break;
+      }
+      case ast::Intrinsic::kTextureSampleBias: {
+        op = spv::Op::OpImageSampleImplicitLod;
+        assert(pidx.bias != kNotUsed);
+        spirv_operand_mask |= SpvImageOperandsBiasMask;
+        spirv_operands.emplace_back(gen_param(pidx.bias));
+        break;
+      }
+      case ast::Intrinsic::kTextureSampleLevel: {
+        op = spv::Op::OpImageSampleExplicitLod;
+        assert(pidx.level != kNotUsed);
+        spirv_operand_mask |= SpvImageOperandsLodMask;
+        spirv_operands.emplace_back(gen_param(pidx.level));
+        break;
+      }
+      case ast::Intrinsic::kTextureSampleGrad: {
+        op = spv::Op::OpImageSampleExplicitLod;
+        assert(pidx.ddx != kNotUsed);
+        assert(pidx.ddy != kNotUsed);
+        spirv_operand_mask |= SpvImageOperandsGradMask;
+        spirv_operands.emplace_back(gen_param(pidx.ddx));
+        spirv_operands.emplace_back(gen_param(pidx.ddy));
+        break;
+      }
+      case ast::Intrinsic::kTextureSampleCompare: {
+        op = spv::Op::OpImageSampleDrefExplicitLod;
+        assert(pidx.depth_ref != kNotUsed);
+        spirv_params.emplace_back(gen_param(pidx.depth_ref));
+
+        spirv_operand_mask |= SpvImageOperandsLodMask;
+        ast::type::F32Type f32;
+        ast::FloatLiteral float_0(&f32, 0.0);
+        spirv_operands.emplace_back(
+            Operand::Int(GenerateLiteralIfNeeded(nullptr, &float_0)));
+        break;
+      }
+      default:
+        break;  // unreachable
     }
-    push_function_inst(op, spirv_params);
-    return result_id;
   }
 
-  spv::Op op = spv::Op::OpNop;
-  OperandList spirv_params = {
-      wgsl_params[0], std::move(wgsl_params[1]),
-      Operand::Int(GenerateSampledImage(texture_type, std::move(wgsl_params[2]),
-                                        std::move(wgsl_params[3]))),
-      std::move(wgsl_params[4])};
-
-  if (ident->intrinsic() == ast::Intrinsic::kTextureSample) {
-    op = spv::Op::OpImageSampleImplicitLod;
-  } else if (ident->intrinsic() == ast::Intrinsic::kTextureSampleLevel) {
-    op = spv::Op::OpImageSampleExplicitLod;
-    spirv_params.push_back(Operand::Int(SpvImageOperandsLodMask));
-    spirv_params.push_back(std::move(wgsl_params[5]));
-  } else if (ident->intrinsic() == ast::Intrinsic::kTextureSampleBias) {
-    op = spv::Op::OpImageSampleImplicitLod;
-    spirv_params.push_back(Operand::Int(SpvImageOperandsBiasMask));
-    spirv_params.push_back(std::move(wgsl_params[5]));
-  } else if (ident->intrinsic() == ast::Intrinsic::kTextureSampleCompare) {
-    op = spv::Op::OpImageSampleDrefExplicitLod;
-    spirv_params.push_back(std::move(wgsl_params[5]));
-
-    spirv_params.push_back(Operand::Int(SpvImageOperandsLodMask));
-    ast::type::F32Type f32;
-    ast::FloatLiteral float_0(&f32, 0.0);
-    spirv_params.push_back(
-        Operand::Int(GenerateLiteralIfNeeded(nullptr, &float_0)));
+  if (pidx.offset != kNotUsed) {
+    spirv_operand_mask |= SpvImageOperandsOffsetMask;
+    spirv_operands.emplace_back(gen_param(pidx.offset));
   }
+
+  if (spirv_operand_mask != 0) {
+    // Note: Order of operands is based on SpvImageXXXOperands value -
+    // smaller-numbered SpvImageXXXOperands bits appear first.
+    spirv_params.emplace_back(Operand::Int(spirv_operand_mask));
+    spirv_params.insert(std::end(spirv_params), std::begin(spirv_operands),
+                        std::end(spirv_operands));
+  }
+
   if (op == spv::Op::OpNop) {
     error_ = "unable to determine operator for: " + ident->name();
-    return 0;
+    return;
   }
-  push_function_inst(op, spirv_params);
 
-  return result_id;
+  push_function_inst(op, spirv_params);
 }
 
 uint32_t Builder::GenerateSampledImage(ast::type::Type* texture_type,
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index dd0d20d..ac3f452 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -231,7 +231,7 @@
   bool GenerateExecutionModes(ast::Function* func, uint32_t id);
   /// Generates an expression
   /// @param expr the expression to generate
-  /// @returns the resulting ID of the exp = {};ression or 0 on error
+  /// @returns the resulting ID of the expression or 0 on error
   uint32_t GenerateExpression(ast::Expression* expr);
   /// Generates the instructions for a function
   /// @param func the function to generate
@@ -335,14 +335,13 @@
   /// Generates a texture intrinsic call
   /// @param ident the texture intrinsic
   /// @param call the call expression
-  /// @param result_id result ID of the texture instruction
-  /// @param wgsl_params SPIR-V arguments for WGSL-specific intrinsic's call
+  /// @param result_type result type operand of the texture instruction
+  /// @param result_id result identifier operand of the texture instruction
   /// parameters
-  /// @returns the expression ID on success or 0 otherwise
-  uint32_t GenerateTextureIntrinsic(ast::IdentifierExpression* ident,
-                                    ast::CallExpression* call,
-                                    uint32_t result_id,
-                                    OperandList wgsl_params);
+  void GenerateTextureIntrinsic(ast::IdentifierExpression* ident,
+                                ast::CallExpression* call,
+                                spirv::Operand result_type,
+                                spirv::Operand result_id);
   /// Generates a sampled image
   /// @param texture_type the texture type
   /// @param texture_operand the texture operand
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 3b05637..e41cc8a 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -19,6 +19,7 @@
 #include "src/ast/call_expression.h"
 #include "src/ast/float_literal.h"
 #include "src/ast/identifier_expression.h"
+#include "src/ast/intrinsic_texture_helper_test.h"
 #include "src/ast/member_accessor_expression.h"
 #include "src/ast/scalar_constructor_expression.h"
 #include "src/ast/sint_literal.h"
@@ -581,103 +582,27 @@
 )");
 }
 
-TEST_F(IntrinsicBuilderTest, Call_TextureSample_1d) {
-  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, &s);
+using IntrinsicTextureTest =
+    IntrinsicBuilderTestWithParam<ast::intrinsic::test::TextureOverloadCase>;
 
-  b.push_function(Function{});
+INSTANTIATE_TEST_SUITE_P(
+    IntrinsicBuilderTest,
+    IntrinsicTextureTest,
+    testing::ValuesIn(ast::intrinsic::test::TextureOverloadCase::ValidCases()));
 
-  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
-  ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
+struct expected_texture_overload_spirv {
+  std::string types;
+  std::string instructions;
+};
 
-  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
-  ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
-
-  auto expr = Call("textureSample", "texture", "sampler", 1.0f);
-
-  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_EQ(b.GenerateExpression(&expr), 7u) << b.error();
-  EXPECT_EQ(DumpInstructions(b.types()),
-            R"(%4 = OpTypeSampler
-%3 = OpTypeImage %4 1D 0 0 0 1 Unknown
-%2 = OpTypePointer Private %3
-%1 = OpVariable %2 Private
-%6 = OpTypePointer Private %4
-%5 = OpVariable %6 Private
-%8 = OpTypeVector %4 4
-%11 = OpTypeFloat 32
-%12 = OpConstant %11 1
-%13 = OpTypeSampledImage %3
-)");
-
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%10 = OpLoad %4 %5
-%14 = OpSampledImage %13 %9 %10
-%7 = OpImageSampleImplicitLod %8 %14 %12
-)");
-}
-
-TEST_F(IntrinsicBuilderTest, Call_TextureSample_2d) {
-  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k2d, &s);
-
-  b.push_function(Function{});
-
-  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
-  ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
-
-  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
-  ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
-
-  auto expr =
-      Call("textureSample", "texture", "sampler", vec2<f32>(1.0f, 2.0f));
-
-  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_EQ(b.GenerateExpression(&expr), 7u) << b.error();
-  EXPECT_EQ(DumpInstructions(b.types()),
-            R"(%4 = OpTypeSampler
-%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
-%2 = OpTypePointer Private %3
-%1 = OpVariable %2 Private
-%6 = OpTypePointer Private %4
-%5 = OpVariable %6 Private
-%8 = OpTypeVector %4 4
-%12 = OpTypeFloat 32
-%11 = OpTypeVector %12 2
-%13 = OpConstant %12 1
-%14 = OpConstant %12 2
-%15 = OpConstantComposite %11 %13 %14
-%16 = OpTypeSampledImage %3
-)");
-
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%10 = OpLoad %4 %5
-%17 = OpSampledImage %16 %9 %10
-%7 = OpImageSampleImplicitLod %8 %17 %15
-)");
-}
-
-TEST_F(IntrinsicBuilderTest, Call_TextureSampleLevel_1d) {
-  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, ty.f32);
-
-  b.push_function(Function{});
-
-  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
-  ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
-
-  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
-  ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
-
-  auto expr = Call("textureSampleLevel", "texture", "sampler", 1.0f, 2.0f);
-
-  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()),
-            R"(%4 = OpTypeFloat 32
+expected_texture_overload_spirv expected_texture_overload(
+    ast::intrinsic::test::ValidTextureOverload overload) {
+  using ValidTextureOverload = ast::intrinsic::test::ValidTextureOverload;
+  switch (overload) {
+    case ValidTextureOverload::kSample1dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
 %3 = OpTypeImage %4 1D 0 0 0 1 Unknown
 %2 = OpTypePointer Private %3
 %1 = OpVariable %2 Private
@@ -685,39 +610,44 @@
 %6 = OpTypePointer Private %7
 %5 = OpVariable %6 Private
 %9 = OpTypeVector %4 4
-%12 = OpConstant %4 1
-%13 = OpConstant %4 2
-%14 = OpTypeSampledImage %3
-)");
-
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%10 = OpLoad %3 %1
-%11 = OpLoad %7 %5
-%15 = OpSampledImage %14 %10 %11
-%8 = OpImageSampleExplicitLod %9 %15 %12 Lod %13
-)");
-}
-
-TEST_F(IntrinsicBuilderTest, Call_TextureSampleLevel_2d) {
-  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k2d, ty.f32);
-
-  b.push_function(Function{});
-
-  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
-  ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
-
-  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
-  ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
-
-  auto expr = Call("textureSampleLevel", "texture", "sampler",
-                   vec2<f32>(1.0f, 2.0f), 2.0f);
-
-  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()),
-            R"(%4 = OpTypeFloat 32
+%12 = OpTypeSampledImage %3
+%14 = OpConstant %4 1
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %14
+)"};
+    case ValidTextureOverload::kSample1dArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 1D 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%17 = OpTypeInt 32 0
+%18 = OpConstant %17 2
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%16 = OpConvertUToF %4 %18
+%19 = OpCompositeConstruct %14 %15 %16
+%8 = OpImageSampleImplicitLod %9 %13 %19
+)"};
+    case ValidTextureOverload::kSample2dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
 %3 = OpTypeImage %4 2D 0 0 0 1 Unknown
 %2 = OpTypePointer Private %3
 %1 = OpVariable %2 Private
@@ -725,141 +655,1537 @@
 %6 = OpTypePointer Private %7
 %5 = OpVariable %6 Private
 %9 = OpTypeVector %4 4
-%12 = OpTypeVector %4 2
-%13 = OpConstant %4 1
-%14 = OpConstant %4 2
-%15 = OpConstantComposite %12 %13 %14
-%16 = OpTypeSampledImage %3
-)");
-
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%10 = OpLoad %3 %1
-%11 = OpLoad %7 %5
-%17 = OpSampledImage %16 %10 %11
-%8 = OpImageSampleExplicitLod %9 %17 %15 Lod %14
-)");
-}
-
-TEST_F(IntrinsicBuilderTest, Call_TextureSampleBias_1d) {
-  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, ty.f32);
-
-  b.push_function(Function{});
-
-  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
-  ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
-
-  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
-  ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
-
-  auto expr = Call("textureSampleBias", "texture", "sampler", 1.0f, 2.0f);
-
-  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()),
-            R"(%4 = OpTypeFloat 32
-%3 = OpTypeImage %4 1D 0 0 0 1 Unknown
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %17
+)"};
+    case ValidTextureOverload::kSample2dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
 %2 = OpTypePointer Private %3
 %1 = OpVariable %2 Private
 %7 = OpTypeSampler
 %6 = OpTypePointer Private %7
 %5 = OpVariable %6 Private
 %9 = OpTypeVector %4 4
-%12 = OpConstant %4 1
-%13 = OpConstant %4 2
-%14 = OpTypeSampledImage %3
-)");
-
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%10 = OpLoad %3 %1
-%11 = OpLoad %7 %5
-%15 = OpSampledImage %14 %10 %11
-%8 = OpImageSampleImplicitLod %9 %15 %12 Bias %13
-)");
-}
-
-TEST_F(IntrinsicBuilderTest, Call_TextureSampleBias_2d) {
-  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, ty.f32);
-
-  b.push_function(Function{});
-
-  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
-  ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
-
-  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
-  ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
-
-  auto expr = Call("textureSampleBias", "texture", "sampler",
-                   vec2<f32>(1.0f, 2.0f), 2.0f);
-
-  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.types()),
-            R"(%4 = OpTypeFloat 32
-%3 = OpTypeImage %4 1D 0 0 0 1 Unknown
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%19 = OpTypeInt 32 1
+%18 = OpTypeVector %19 2
+%20 = OpConstant %19 3
+%21 = OpConstant %19 4
+%22 = OpConstantComposite %18 %20 %21
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %17 Offset %22
+)"};
+    case ValidTextureOverload::kSample2dArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
 %2 = OpTypePointer Private %3
 %1 = OpVariable %2 Private
 %7 = OpTypeSampler
 %6 = OpTypePointer Private %7
 %5 = OpVariable %6 Private
 %9 = OpTypeVector %4 4
-%12 = OpTypeVector %4 2
-%13 = OpConstant %4 1
-%14 = OpConstant %4 2
-%15 = OpConstantComposite %12 %13 %14
-%16 = OpTypeSampledImage %3
-)");
-
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%10 = OpLoad %3 %1
-%11 = OpLoad %7 %5
-%17 = OpSampledImage %16 %10 %11
-%8 = OpImageSampleImplicitLod %9 %17 %15 Bias %14
-)");
-}
-
-TEST_F(IntrinsicBuilderTest, Call_TextureSampleCompare) {
-  ast::type::SamplerType s(ast::type::SamplerKind::kComparisonSampler);
-  ast::type::DepthTextureType t(ast::type::TextureDimension::k2d);
-
-  b.push_function(Function{});
-
-  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
-  ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
-
-  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
-  ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
-
-  auto expr = Call("textureSampleCompare", "texture", "sampler",
-                   vec2<f32>(1.0f, 2.0f), 2.0f);
-
-  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
-  EXPECT_EQ(DumpInstructions(b.types()),
-            R"(%4 = OpTypeFloat 32
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpTypeVector %4 2
+%16 = OpConstant %4 1
+%17 = OpConstant %4 2
+%18 = OpConstantComposite %15 %16 %17
+%22 = OpTypeInt 32 0
+%23 = OpConstant %22 3
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpConvertUToF %4 %23
+%24 = OpCompositeConstruct %14 %19 %20 %21
+%8 = OpImageSampleImplicitLod %9 %13 %24
+)"};
+    case ValidTextureOverload::kSample2dArrayOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpTypeVector %4 2
+%16 = OpConstant %4 1
+%17 = OpConstant %4 2
+%18 = OpConstantComposite %15 %16 %17
+%22 = OpTypeInt 32 0
+%23 = OpConstant %22 3
+%26 = OpTypeInt 32 1
+%25 = OpTypeVector %26 2
+%27 = OpConstant %26 4
+%28 = OpConstant %26 5
+%29 = OpConstantComposite %25 %27 %28
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpConvertUToF %4 %23
+%24 = OpCompositeConstruct %14 %19 %20 %21
+%8 = OpImageSampleImplicitLod %9 %13 %24 Offset %29
+)"};
+    case ValidTextureOverload::kSample3dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 3D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %18
+)"};
+    case ValidTextureOverload::kSample3dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 3D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%20 = OpTypeInt 32 1
+%19 = OpTypeVector %20 3
+%21 = OpConstant %20 4
+%22 = OpConstant %20 5
+%23 = OpConstant %20 6
+%24 = OpConstantComposite %19 %21 %22 %23
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %18 Offset %24
+)"};
+    case ValidTextureOverload::kSampleCubeF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %18
+)"};
+    case ValidTextureOverload::kSampleCubeArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%23 = OpTypeInt 32 0
+%24 = OpConstant %23 4
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpCompositeExtract %4 %18 2
+%22 = OpConvertUToF %4 %24
+%25 = OpCompositeConstruct %9 %19 %20 %21 %22
+%8 = OpImageSampleImplicitLod %9 %13 %25
+)"};
+    case ValidTextureOverload::kSampleDepth2dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
 %3 = OpTypeImage %4 2D 1 0 0 1 Unknown
 %2 = OpTypePointer Private %3
 %1 = OpVariable %2 Private
 %7 = OpTypeSampler
 %6 = OpTypePointer Private %7
 %5 = OpVariable %6 Private
-%11 = OpTypeVector %4 2
-%12 = OpConstant %4 1
-%13 = OpConstant %4 2
-%14 = OpConstantComposite %11 %12 %13
-%15 = OpTypeSampledImage %3
-%17 = OpConstant %4 0
-)");
-
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 2
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstantComposite %13 %14 %15
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleImplicitLod %4 %12 %16
+)"};
+    case ValidTextureOverload::kSampleDepth2dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 2
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstantComposite %13 %14 %15
+%18 = OpTypeInt 32 1
+%17 = OpTypeVector %18 2
+%19 = OpConstant %18 3
+%20 = OpConstant %18 4
+%21 = OpConstantComposite %17 %19 %20
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleImplicitLod %4 %12 %16 Offset %21
+)"};
+    case ValidTextureOverload::kSampleDepth2dArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%21 = OpTypeInt 32 0
+%22 = OpConstant %21 3
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%18 = OpCompositeExtract %4 %17 0
+%19 = OpCompositeExtract %4 %17 1
+%20 = OpConvertUToF %4 %22
+%23 = OpCompositeConstruct %13 %18 %19 %20
+%8 = OpImageSampleImplicitLod %4 %12 %23
+)"};
+    case ValidTextureOverload::kSampleDepth2dArrayOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%21 = OpTypeInt 32 0
+%22 = OpConstant %21 3
+%25 = OpTypeInt 32 1
+%24 = OpTypeVector %25 2
+%26 = OpConstant %25 4
+%27 = OpConstant %25 5
+%28 = OpConstantComposite %24 %26 %27
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%18 = OpCompositeExtract %4 %17 0
+%19 = OpCompositeExtract %4 %17 1
+%20 = OpConvertUToF %4 %22
+%23 = OpCompositeConstruct %13 %18 %19 %20
+%8 = OpImageSampleImplicitLod %4 %12 %23 Offset %28
+)"};
+    case ValidTextureOverload::kSampleDepthCubeF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 1 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 2
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstant %4 3
+%17 = OpConstantComposite %13 %14 %15 %16
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleImplicitLod %4 %12 %17
+)"};
+    case ValidTextureOverload::kSampleDepthCubeArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 1 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%22 = OpTypeInt 32 0
+%23 = OpConstant %22 4
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpConvertUToF %4 %23
+%24 = OpCompositeConstruct %13 %19 %20 %21
+%8 = OpImageSampleImplicitLod %4 %12 %24
+)"};
+    case ValidTextureOverload::kSampleBias2dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%18 = OpConstant %4 3
+)",
+          R"(
 %10 = OpLoad %7 %5
-%16 = OpSampledImage %15 %9 %10
-%8 = OpImageSampleDrefExplicitLod %4 %16 %14 %13 Lod %17
-)");
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %17 Bias %18
+)"};
+    case ValidTextureOverload::kSampleBias2dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%18 = OpConstant %4 3
+%20 = OpTypeInt 32 1
+%19 = OpTypeVector %20 2
+%21 = OpConstant %20 4
+%22 = OpConstant %20 5
+%23 = OpConstantComposite %19 %21 %22
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %17 Bias|Offset %18 %23
+)"};
+    case ValidTextureOverload::kSampleBias2dArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpTypeVector %4 2
+%16 = OpConstant %4 1
+%17 = OpConstant %4 2
+%18 = OpConstantComposite %15 %16 %17
+%22 = OpTypeInt 32 0
+%23 = OpConstant %22 4
+%25 = OpConstant %4 3
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpConvertUToF %4 %23
+%24 = OpCompositeConstruct %14 %19 %20 %21
+%8 = OpImageSampleImplicitLod %9 %13 %24 Bias %25
+)"};
+    case ValidTextureOverload::kSampleBias2dArrayOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpTypeVector %4 2
+%16 = OpConstant %4 1
+%17 = OpConstant %4 2
+%18 = OpConstantComposite %15 %16 %17
+%22 = OpTypeInt 32 0
+%23 = OpConstant %22 3
+%25 = OpConstant %4 4
+%27 = OpTypeInt 32 1
+%26 = OpTypeVector %27 2
+%28 = OpConstant %27 5
+%29 = OpConstant %27 6
+%30 = OpConstantComposite %26 %28 %29
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpConvertUToF %4 %23
+%24 = OpCompositeConstruct %14 %19 %20 %21
+%8 = OpImageSampleImplicitLod %9 %13 %24 Bias|Offset %25 %30
+)"};
+    case ValidTextureOverload::kSampleBias3dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 3D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%19 = OpConstant %4 4
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %18 Bias %19
+)"};
+    case ValidTextureOverload::kSampleBias3dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 3D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%19 = OpConstant %4 4
+%21 = OpTypeInt 32 1
+%20 = OpTypeVector %21 3
+%22 = OpConstant %21 5
+%23 = OpConstant %21 6
+%24 = OpConstant %21 7
+%25 = OpConstantComposite %20 %22 %23 %24
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %18 Bias|Offset %19 %25
+)"};
+    case ValidTextureOverload::kSampleBiasCubeF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%19 = OpConstant %4 4
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleImplicitLod %9 %13 %18 Bias %19
+)"};
+    case ValidTextureOverload::kSampleBiasCubeArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%23 = OpTypeInt 32 0
+%24 = OpConstant %23 3
+%26 = OpConstant %4 4
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpCompositeExtract %4 %18 2
+%22 = OpConvertUToF %4 %24
+%25 = OpCompositeConstruct %9 %19 %20 %21 %22
+%8 = OpImageSampleImplicitLod %9 %13 %25 Bias %26
+)"};
+    case ValidTextureOverload::kSampleLevel2dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%18 = OpConstant %4 3
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %17 Lod %18
+)"};
+    case ValidTextureOverload::kSampleLevel2dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%18 = OpConstant %4 3
+%20 = OpTypeInt 32 1
+%19 = OpTypeVector %20 2
+%21 = OpConstant %20 4
+%22 = OpConstant %20 5
+%23 = OpConstantComposite %19 %21 %22
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %17 Lod|Offset %18 %23
+)"};
+    case ValidTextureOverload::kSampleLevel2dArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpTypeVector %4 2
+%16 = OpConstant %4 1
+%17 = OpConstant %4 2
+%18 = OpConstantComposite %15 %16 %17
+%22 = OpTypeInt 32 0
+%23 = OpConstant %22 3
+%25 = OpConstant %4 4
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpConvertUToF %4 %23
+%24 = OpCompositeConstruct %14 %19 %20 %21
+%8 = OpImageSampleExplicitLod %9 %13 %24 Lod %25
+)"};
+    case ValidTextureOverload::kSampleLevel2dArrayOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpTypeVector %4 2
+%16 = OpConstant %4 1
+%17 = OpConstant %4 2
+%18 = OpConstantComposite %15 %16 %17
+%22 = OpTypeInt 32 0
+%23 = OpConstant %22 3
+%25 = OpConstant %4 4
+%27 = OpTypeInt 32 1
+%26 = OpTypeVector %27 2
+%28 = OpConstant %27 5
+%29 = OpConstant %27 6
+%30 = OpConstantComposite %26 %28 %29
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpConvertUToF %4 %23
+%24 = OpCompositeConstruct %14 %19 %20 %21
+%8 = OpImageSampleExplicitLod %9 %13 %24 Lod|Offset %25 %30
+)"};
+    case ValidTextureOverload::kSampleLevel3dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 3D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%19 = OpConstant %4 4
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %18 Lod %19
+)"};
+    case ValidTextureOverload::kSampleLevel3dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 3D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%19 = OpConstant %4 4
+%21 = OpTypeInt 32 1
+%20 = OpTypeVector %21 3
+%22 = OpConstant %21 5
+%23 = OpConstant %21 6
+%24 = OpConstant %21 7
+%25 = OpConstantComposite %20 %22 %23 %24
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %18 Lod|Offset %19 %25
+)"};
+    case ValidTextureOverload::kSampleLevelCubeF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%19 = OpConstant %4 4
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %18 Lod %19
+)"};
+    case ValidTextureOverload::kSampleLevelCubeArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%23 = OpTypeInt 32 0
+%24 = OpConstant %23 4
+%26 = OpConstant %4 5
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpCompositeExtract %4 %18 2
+%22 = OpConvertUToF %4 %24
+%25 = OpCompositeConstruct %9 %19 %20 %21 %22
+%8 = OpImageSampleExplicitLod %9 %13 %25 Lod %26
+)"};
+    case ValidTextureOverload::kSampleLevelDepth2dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 2
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstantComposite %13 %14 %15
+%17 = OpTypeInt 32 0
+%18 = OpConstant %17 3
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleExplicitLod %4 %12 %16 Lod %18
+)"};
+    case ValidTextureOverload::kSampleLevelDepth2dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 2
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstantComposite %13 %14 %15
+%17 = OpTypeInt 32 0
+%18 = OpConstant %17 3
+%20 = OpTypeInt 32 1
+%19 = OpTypeVector %20 2
+%21 = OpConstant %20 4
+%22 = OpConstant %20 5
+%23 = OpConstantComposite %19 %21 %22
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleExplicitLod %4 %12 %16 Lod|Offset %18 %23
+)"};
+    case ValidTextureOverload::kSampleLevelDepth2dArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%21 = OpTypeInt 32 0
+%22 = OpConstant %21 3
+%24 = OpConstant %21 4
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%18 = OpCompositeExtract %4 %17 0
+%19 = OpCompositeExtract %4 %17 1
+%20 = OpConvertUToF %4 %22
+%23 = OpCompositeConstruct %13 %18 %19 %20
+%8 = OpImageSampleExplicitLod %4 %12 %23 Lod %24
+)"};
+    case ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%21 = OpTypeInt 32 0
+%22 = OpConstant %21 3
+%24 = OpConstant %21 4
+%26 = OpTypeInt 32 1
+%25 = OpTypeVector %26 2
+%27 = OpConstant %26 5
+%28 = OpConstant %26 6
+%29 = OpConstantComposite %25 %27 %28
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%18 = OpCompositeExtract %4 %17 0
+%19 = OpCompositeExtract %4 %17 1
+%20 = OpConvertUToF %4 %22
+%23 = OpCompositeConstruct %13 %18 %19 %20
+%8 = OpImageSampleExplicitLod %4 %12 %23 Lod|Offset %24 %29
+)"};
+    case ValidTextureOverload::kSampleLevelDepthCubeF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 1 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 3
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstant %4 3
+%17 = OpConstantComposite %13 %14 %15 %16
+%18 = OpTypeInt 32 0
+%19 = OpConstant %18 4
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleExplicitLod %4 %12 %17 Lod %19
+)"};
+    case ValidTextureOverload::kSampleLevelDepthCubeArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 1 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 4
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%23 = OpTypeInt 32 0
+%24 = OpConstant %23 4
+%26 = OpConstant %23 5
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpCompositeExtract %4 %18 2
+%22 = OpConvertUToF %4 %24
+%25 = OpCompositeConstruct %13 %19 %20 %21 %22
+%8 = OpImageSampleExplicitLod %4 %12 %25 Lod %26
+)"};
+    case ValidTextureOverload::kSampleGrad2dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%18 = OpConstant %4 3
+%19 = OpConstant %4 4
+%20 = OpConstantComposite %14 %18 %19
+%21 = OpConstant %4 5
+%22 = OpConstant %4 6
+%23 = OpConstantComposite %14 %21 %22
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %17 Grad %20 %23
+)"};
+    case ValidTextureOverload::kSampleGrad2dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%18 = OpConstant %4 3
+%19 = OpConstant %4 4
+%20 = OpConstantComposite %14 %18 %19
+%21 = OpConstant %4 5
+%22 = OpConstant %4 6
+%23 = OpConstantComposite %14 %21 %22
+%25 = OpTypeInt 32 1
+%24 = OpTypeVector %25 2
+%26 = OpConstant %25 7
+%27 = OpConstant %25 8
+%28 = OpConstantComposite %24 %26 %27
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %17 Grad|Offset %20 %23 %28
+)"};
+    case ValidTextureOverload::kSampleGrad2dArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpTypeVector %4 2
+%16 = OpConstant %4 1
+%17 = OpConstant %4 2
+%18 = OpConstantComposite %15 %16 %17
+%22 = OpTypeInt 32 0
+%23 = OpConstant %22 3
+%25 = OpConstant %4 4
+%26 = OpConstant %4 5
+%27 = OpConstantComposite %15 %25 %26
+%28 = OpConstant %4 6
+%29 = OpConstant %4 7
+%30 = OpConstantComposite %15 %28 %29
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpConvertUToF %4 %23
+%24 = OpCompositeConstruct %14 %19 %20 %21
+%8 = OpImageSampleExplicitLod %9 %13 %24 Grad %27 %30
+)"};
+    case ValidTextureOverload::kSampleGrad2dArrayOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpTypeVector %4 2
+%16 = OpConstant %4 1
+%17 = OpConstant %4 2
+%18 = OpConstantComposite %15 %16 %17
+%22 = OpTypeInt 32 0
+%23 = OpConstant %22 3
+%25 = OpConstant %4 4
+%26 = OpConstant %4 5
+%27 = OpConstantComposite %15 %25 %26
+%28 = OpConstant %4 6
+%29 = OpConstant %4 7
+%30 = OpConstantComposite %15 %28 %29
+%32 = OpTypeInt 32 1
+%31 = OpTypeVector %32 2
+%33 = OpConstant %32 8
+%34 = OpConstant %32 9
+%35 = OpConstantComposite %31 %33 %34
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpConvertUToF %4 %23
+%24 = OpCompositeConstruct %14 %19 %20 %21
+%8 = OpImageSampleExplicitLod %9 %13 %24 Grad|Offset %27 %30 %35
+)"};
+    case ValidTextureOverload::kSampleGrad3dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 3D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%19 = OpConstant %4 4
+%20 = OpConstant %4 5
+%21 = OpConstant %4 6
+%22 = OpConstantComposite %14 %19 %20 %21
+%23 = OpConstant %4 7
+%24 = OpConstant %4 8
+%25 = OpConstant %4 9
+%26 = OpConstantComposite %14 %23 %24 %25
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %18 Grad %22 %26
+)"};
+    case ValidTextureOverload::kSampleGrad3dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 3D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%19 = OpConstant %4 4
+%20 = OpConstant %4 5
+%21 = OpConstant %4 6
+%22 = OpConstantComposite %14 %19 %20 %21
+%23 = OpConstant %4 7
+%24 = OpConstant %4 8
+%25 = OpConstant %4 9
+%26 = OpConstantComposite %14 %23 %24 %25
+%28 = OpTypeInt 32 1
+%27 = OpTypeVector %28 3
+%29 = OpConstant %28 10
+%30 = OpConstant %28 11
+%31 = OpConstant %28 12
+%32 = OpConstantComposite %27 %29 %30 %31
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %18 Grad|Offset %22 %26 %32
+)"};
+    case ValidTextureOverload::kSampleGradCubeF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%19 = OpConstant %4 4
+%20 = OpConstant %4 5
+%21 = OpConstant %4 6
+%22 = OpConstantComposite %14 %19 %20 %21
+%23 = OpConstant %4 7
+%24 = OpConstant %4 8
+%25 = OpConstant %4 9
+%26 = OpConstantComposite %14 %23 %24 %25
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%8 = OpImageSampleExplicitLod %9 %13 %18 Grad %22 %26
+)"};
+    case ValidTextureOverload::kSampleGradCubeArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 0 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%9 = OpTypeVector %4 4
+%12 = OpTypeSampledImage %3
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%23 = OpTypeInt 32 0
+%24 = OpConstant %23 4
+%26 = OpConstant %4 5
+%27 = OpConstant %4 6
+%28 = OpConstant %4 7
+%29 = OpConstantComposite %14 %26 %27 %28
+%30 = OpConstant %4 8
+%31 = OpConstant %4 9
+%32 = OpConstant %4 10
+%33 = OpConstantComposite %14 %30 %31 %32
+)",
+          R"(
+%10 = OpLoad %7 %5
+%11 = OpLoad %3 %1
+%13 = OpSampledImage %12 %11 %10
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpCompositeExtract %4 %18 2
+%22 = OpConvertUToF %4 %24
+%25 = OpCompositeConstruct %9 %19 %20 %21 %22
+%8 = OpImageSampleExplicitLod %9 %13 %25 Grad %29 %33
+)"};
+    case ValidTextureOverload::kSampleGradDepth2dF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 2
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstantComposite %13 %14 %15
+%17 = OpConstant %4 3
+%18 = OpConstant %4 0
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleDrefExplicitLod %4 %12 %16 %17 Lod %18
+)"};
+    case ValidTextureOverload::kSampleGradDepth2dOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 2
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstantComposite %13 %14 %15
+%17 = OpConstant %4 3
+%18 = OpConstant %4 0
+%20 = OpTypeInt 32 1
+%19 = OpTypeVector %20 2
+%21 = OpConstant %20 4
+%22 = OpConstant %20 5
+%23 = OpConstantComposite %19 %21 %22
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleDrefExplicitLod %4 %12 %16 %17 Lod|Offset %18 %23
+)"};
+    case ValidTextureOverload::kSampleGradDepth2dArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%21 = OpTypeInt 32 0
+%22 = OpConstant %21 4
+%24 = OpConstant %4 3
+%25 = OpConstant %4 0
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%18 = OpCompositeExtract %4 %17 0
+%19 = OpCompositeExtract %4 %17 1
+%20 = OpConvertUToF %4 %22
+%23 = OpCompositeConstruct %13 %18 %19 %20
+%8 = OpImageSampleDrefExplicitLod %4 %12 %23 %24 Lod %25
+)"};
+    case ValidTextureOverload::kSampleGradDepth2dArrayOffsetF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 3
+%14 = OpTypeVector %4 2
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstantComposite %14 %15 %16
+%21 = OpTypeInt 32 0
+%22 = OpConstant %21 4
+%24 = OpConstant %4 3
+%25 = OpConstant %4 0
+%27 = OpTypeInt 32 1
+%26 = OpTypeVector %27 2
+%28 = OpConstant %27 5
+%29 = OpConstant %27 6
+%30 = OpConstantComposite %26 %28 %29
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%18 = OpCompositeExtract %4 %17 0
+%19 = OpCompositeExtract %4 %17 1
+%20 = OpConvertUToF %4 %22
+%23 = OpCompositeConstruct %13 %18 %19 %20
+%8 = OpImageSampleDrefExplicitLod %4 %12 %23 %24 Lod|Offset %25 %30
+)"};
+    case ValidTextureOverload::kSampleGradDepthCubeF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 1 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 3
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstant %4 3
+%17 = OpConstantComposite %13 %14 %15 %16
+%18 = OpConstant %4 4
+%19 = OpConstant %4 0
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleDrefExplicitLod %4 %12 %17 %18 Lod %19
+)"};
+    case ValidTextureOverload::kSampleGradDepthCubeArrayF32:
+      return {
+          R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 Cube 1 1 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%7 = OpTypeSampler
+%6 = OpTypePointer Private %7
+%5 = OpVariable %6 Private
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 4
+%14 = OpTypeVector %4 3
+%15 = OpConstant %4 1
+%16 = OpConstant %4 2
+%17 = OpConstant %4 3
+%18 = OpConstantComposite %14 %15 %16 %17
+%23 = OpTypeInt 32 0
+%24 = OpConstant %23 4
+%26 = OpConstant %4 5
+%27 = OpConstant %4 0
+)",
+          R"(
+%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%19 = OpCompositeExtract %4 %18 0
+%20 = OpCompositeExtract %4 %18 1
+%21 = OpCompositeExtract %4 %18 2
+%22 = OpConvertUToF %4 %24
+%25 = OpCompositeConstruct %13 %19 %20 %21 %22
+%8 = OpImageSampleDrefExplicitLod %4 %12 %25 %26 Lod %27
+)"};
+  }
+  return {"<unmatched texture overload>", "<unmatched texture overload>"};
+}  // NOLINT - Ignore the length of this function
+
+TEST_P(IntrinsicTextureTest, Call) {
+  auto param = GetParam();
+
+  b.push_function(Function{});
+
+  ast::type::Type* datatype = nullptr;
+  switch (param.texture_data_type) {
+    case ast::intrinsic::test::TextureDataType::kF32:
+      datatype = ty.f32;
+      break;
+    case ast::intrinsic::test::TextureDataType::kU32:
+      datatype = ty.u32;
+      break;
+    case ast::intrinsic::test::TextureDataType::kI32:
+      datatype = ty.i32;
+      break;
+  }
+
+  ast::type::SamplerType sampler_type{param.sampler_kind};
+  ast::Variable* tex = nullptr;
+  switch (param.texture_kind) {
+    case ast::intrinsic::test::TextureKind::kRegular:
+      tex = Var("texture", ast::StorageClass::kNone,
+                ctx->type_mgr().Get<ast::type::SampledTextureType>(
+                    param.texture_dimension, datatype));
+      break;
+
+    case ast::intrinsic::test::TextureKind::kDepth:
+      tex = Var("texture", ast::StorageClass::kNone,
+                ctx->type_mgr().Get<ast::type::DepthTextureType>(
+                    param.texture_dimension));
+      break;
+  }
+
+  auto* sampler = Var("sampler", ast::StorageClass::kNone, &sampler_type);
+
+  ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
+  ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
+
+  ast::CallExpression call{Expr(param.function), param.args(this)};
+
+  EXPECT_TRUE(td.DetermineResultType(&call)) << td.error();
+  EXPECT_EQ(b.GenerateExpression(&call), 8u) << b.error();
+
+  auto expected = expected_texture_overload(param.overload);
+  EXPECT_EQ(expected.types, "\n" + DumpInstructions(b.types()));
+  EXPECT_EQ(expected.instructions,
+            "\n" + DumpInstructions(b.functions()[0].instructions()));
 }
 
 // This tests that we do not push OpTypeSampledImage and float_0 type twice.
@@ -894,23 +2220,23 @@
 %7 = OpTypeSampler
 %6 = OpTypePointer Private %7
 %5 = OpVariable %6 Private
-%11 = OpTypeVector %4 2
-%12 = OpConstant %4 1
-%13 = OpConstant %4 2
-%14 = OpConstantComposite %11 %12 %13
-%15 = OpTypeSampledImage %3
+%11 = OpTypeSampledImage %3
+%13 = OpTypeVector %4 2
+%14 = OpConstant %4 1
+%15 = OpConstant %4 2
+%16 = OpConstantComposite %13 %14 %15
 %17 = OpConstant %4 0
 )");
 
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%10 = OpLoad %7 %5
-%16 = OpSampledImage %15 %9 %10
-%8 = OpImageSampleDrefExplicitLod %4 %16 %14 %13 Lod %17
-%19 = OpLoad %3 %1
-%20 = OpLoad %7 %5
-%21 = OpSampledImage %15 %19 %20
-%18 = OpImageSampleDrefExplicitLod %4 %21 %14 %13 Lod %17
+            R"(%9 = OpLoad %7 %5
+%10 = OpLoad %3 %1
+%12 = OpSampledImage %11 %10 %9
+%8 = OpImageSampleDrefExplicitLod %4 %12 %16 %15 Lod %17
+%19 = OpLoad %7 %5
+%20 = OpLoad %3 %1
+%21 = OpSampledImage %11 %20 %19
+%18 = OpImageSampleDrefExplicitLod %4 %21 %16 %15 Lod %17
 )");
 }