[spirv-writer] Simplify the texture intrinsic tests

This CL removes the complete coverage of the possible input formats
infavor of simpler testing of a few of the inputs. This should be
sufficient coverage and makes the tests easier to read.

The incorrect OpConstantNull for the samplers and textures has also been
removed.

Change-Id: I9cd7d6299f0454bb8e2019968e0422221e4ec183
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28581
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 050fd13..795e16d 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -597,7 +597,7 @@
                      Operand::Int(ConvertStorageClass(sc))};
   if (var->has_constructor()) {
     ops.push_back(Operand::Int(init_id));
-  } else {
+  } else if (!var->type()->IsTexture() && !var->type()->IsSampler()) {
     // If we don't have a constructor and we're an Output or Private variable
     // then WGSL requires an initializer.
     if (var->storage_class() == ast::StorageClass::kPrivate ||
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index e950da4..0efcdf3 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -16,7 +16,10 @@
 
 #include "gtest/gtest.h"
 #include "src/ast/call_expression.h"
+#include "src/ast/float_literal.h"
 #include "src/ast/identifier_expression.h"
+#include "src/ast/scalar_constructor_expression.h"
+#include "src/ast/sint_literal.h"
 #include "src/ast/type/bool_type.h"
 #include "src/ast/type/depth_texture_type.h"
 #include "src/ast/type/f32_type.h"
@@ -26,6 +29,7 @@
 #include "src/ast/type/sampler_type.h"
 #include "src/ast/type/u32_type.h"
 #include "src/ast/type/vector_type.h"
+#include "src/ast/type_constructor_expression.h"
 #include "src/ast/variable.h"
 #include "src/context.h"
 #include "src/type_determiner.h"
@@ -413,860 +417,743 @@
 )");
 }
 
-enum class TextureType { kF32, kI32, kU32 };
-inline std::ostream& operator<<(std::ostream& out, TextureType data) {
-  if (data == TextureType::kF32) {
-    out << "f32";
-  } else if (data == TextureType::kI32) {
-    out << "i32";
-  } else {
-    out << "u32";
-  }
-  return out;
-}
-
-struct TextureTestParams {
-  ast::type::TextureDimension dim;
-  TextureType type = TextureType::kF32;
-  ast::type::ImageFormat format = ast::type::ImageFormat::kR16Float;
-};
-inline std::ostream& operator<<(std::ostream& out, TextureTestParams data) {
-  out << data.dim << "_" << data.type;
-  return out;
-}
-
-class Builder_TextureOperation
-    : public testing::TestWithParam<TextureTestParams> {
- public:
-  Builder_TextureOperation()
-      : td_(std::make_unique<TypeDeterminer>(&ctx_, &mod_)),
-        b_(std::make_unique<Builder>(&mod_)) {}
-
-  TypeDeterminer* td() const { return td_.get(); }
-  Context* ctx() { return &ctx_; }
-  Builder* b() const { return b_.get(); }
-
-  std::unique_ptr<ast::type::Type> get_coords_type(
-      ast::type::TextureDimension dim,
-      ast::type::Type* type) {
-    if (dim == ast::type::TextureDimension::k1d) {
-      if (type->IsI32()) {
-        return std::make_unique<ast::type::I32Type>();
-      } else if (type->IsU32()) {
-        return std::make_unique<ast::type::U32Type>();
-      } else {
-        return std::make_unique<ast::type::F32Type>();
-      }
-    } else if (dim == ast::type::TextureDimension::k1dArray ||
-               dim == ast::type::TextureDimension::k2d) {
-      return std::make_unique<ast::type::VectorType>(type, 2);
-    } else if (dim == ast::type::TextureDimension::kCubeArray) {
-      return std::make_unique<ast::type::VectorType>(type, 4);
-    } else {
-      return std::make_unique<ast::type::VectorType>(type, 3);
-    }
-  }
-
-  void add_call_param(std::string name,
-                      ast::type::Type* type,
-                      ast::ExpressionList* call_params) {
-    variables_.push_back(
-        std::make_unique<ast::Variable>(name, ast::StorageClass::kNone, type));
-    td()->RegisterVariableForTesting(variables_.back().get());
-
-    call_params->push_back(std::make_unique<ast::IdentifierExpression>(name));
-    ASSERT_TRUE(b()->GenerateGlobalVariable(variables_.back().get()))
-        << b()->error();
-  }
-
-  std::unique_ptr<ast::type::Type> subtype(TextureType type) {
-    if (type == TextureType::kF32) {
-      return std::make_unique<ast::type::F32Type>();
-    }
-    if (type == TextureType::kI32) {
-      return std::make_unique<ast::type::I32Type>();
-    }
-    return std::make_unique<ast::type::U32Type>();
-  }
-
-  std::string texture_line(
-      ast::type::TextureDimension dim,
-      bool unknown_format,
-      TextureType type,
-      uint32_t depth_literal,
-      uint32_t sampled_literal,
-      ast::type::ImageFormat format = ast::type::ImageFormat::kR8Unorm) {
-    std::string res = "%6 = OpTypeImage ";
-
-    if (type == TextureType::kF32) {
-      res += "%1 ";
-    } else if (type == TextureType::kU32) {
-      res += "%2 ";
-    } else {
-      res += "%3 ";
-    }
-
-    if (dim == ast::type::TextureDimension::k1d ||
-        dim == ast::type::TextureDimension::k1dArray) {
-      res += "1D ";
-    } else if (dim == ast::type::TextureDimension::k3d) {
-      res += "3D ";
-    } else if (dim == ast::type::TextureDimension::kCube ||
-               dim == ast::type::TextureDimension::kCubeArray) {
-      res += "Cube ";
-    } else {
-      res += "2D ";
-    }
-
-    res += std::to_string(depth_literal) + " ";
-
-    if (dim == ast::type::TextureDimension::k1dArray ||
-        dim == ast::type::TextureDimension::k2dArray ||
-        dim == ast::type::TextureDimension::kCubeArray) {
-      res += "1 ";
-    } else {
-      res += "0 ";
-    }
-
-    res += "0 ";
-    res += std::to_string(sampled_literal) + " ";
-
-    if (unknown_format) {
-      res += "Unknown\n";
-    } else if (format == ast::type::ImageFormat::kR16Float) {
-      res += "R16f\n";
-    } else if (format == ast::type::ImageFormat::kR16Sint) {
-      res += "R16i\n";
-    } else {
-      res += "R8\n";
-    }
-
-    return res;
-  }
-
- private:
-  Context ctx_;
-  ast::Module mod_;
-  std::unique_ptr<TypeDeterminer> td_;
-  std::unique_ptr<Builder> b_;
-  std::vector<std::unique_ptr<ast::Variable>> variables_;
-};
-
-class Builder_TextureLoad : public Builder_TextureOperation {
- public:
-  std::string generate_type_str(ast::type::TextureDimension dim,
-                                ast::type::ImageFormat format,
-                                bool unknown_format,
-                                TextureType type,
-                                uint32_t depth_literal,
-                                uint32_t sampled_literal,
-                                uint32_t type_id,
-                                uint32_t coords_length) {
-    std::string type_str = R"(%1 = OpTypeFloat 32
-%2 = OpTypeInt 32 0
-%3 = OpTypeInt 32 1
-)";
-
-    type_str += texture_line(dim, unknown_format, type, depth_literal,
-                             sampled_literal, format);
-
-    type_str += R"(%5 = OpTypePointer Private %6
-%7 = OpConstantNull %6
-%4 = OpVariable %5 Private %7
-)";
-
-    if (coords_length > 1) {
-      type_str += "%10 = OpTypeVector %3 " + std::to_string(coords_length) +
-                  "\n" +
-                  R"(%9 = OpTypePointer Private %10
-%11 = OpConstantNull %10
-%8 = OpVariable %9 Private %11
-%13 = OpTypePointer Private %3
-%14 = OpConstantNull %3
-%12 = OpVariable %13 Private %14
-%16 = OpTypeVector %)" +
-                  std::to_string(type_id) + " 4\n";
-    } else {
-      type_str += R"(%9 = OpTypePointer Private %3
-%10 = OpConstantNull %3
-%8 = OpVariable %9 Private %10
-%11 = OpVariable %9 Private %10
-%13 = OpTypeVector %)" +
-                  std::to_string(type_id) + " 4\n";
-    }
-
-    return type_str;
-  }
-
-  std::string generate_ops_str(uint32_t coords_length, std::string op_name) {
-    if (coords_length == 1) {
-      return R"(%14 = OpLoad %6 %4
-%15 = OpLoad %3 %8
-%16 = OpLoad %3 %11
-%12 = )" + op_name +
-             R"( %13 %14 %15 Lod %16
-)";
-    }
-
-    return R"(%17 = OpLoad %6 %4
-%18 = OpLoad %10 %8
-%19 = OpLoad %3 %12
-%15 = )" + op_name +
-           R"( %16 %17 %18 Lod %19
-)";
-  }
-};
-
-TEST_P(Builder_TextureLoad, StorageReadonly) {
-  auto dim = GetParam().dim;
-  auto type = GetParam().type;
-  auto format = GetParam().format;
-
+TEST_F(BuilderTest, Call_TextureLoad_Storage_RO_1d) {
   ast::type::F32Type f32;
-  b()->GenerateTypeIfNeeded(&f32);
-  ast::type::U32Type u32;
-  b()->GenerateTypeIfNeeded(&u32);
   ast::type::I32Type i32;
-  b()->GenerateTypeIfNeeded(&i32);
+  ast::type::StorageTextureType s(ast::type::TextureDimension::k1d,
+                                  ast::type::StorageAccess::kRead,
+                                  ast::type::ImageFormat::kR16Float);
 
-  uint32_t type_id = 1;
-  if (type == TextureType::kU32) {
-    type_id = 2;
-  } else if (type == TextureType::kI32) {
-    type_id = 3;
-  }
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
 
-  auto coords_type = get_coords_type(dim, &i32);
+  ASSERT_TRUE(td.DetermineStorageTextureSubtype(&s)) << td.error();
 
-  uint32_t coords_length = 1;
-  if (coords_type->IsVector()) {
-    coords_length = coords_type->AsVector()->size();
-  }
+  b.push_function(Function{});
 
-  auto* texture_type =
-      ctx()->type_mgr().Get(std::make_unique<ast::type::StorageTextureType>(
-          dim, ast::type::StorageAccess::kRead, format));
-
-  EXPECT_TRUE(td()->Determine());
+  ast::Variable tex("texture", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
 
   ast::ExpressionList call_params;
-  b()->push_function(Function{});
-
-  add_call_param("texture", texture_type, &call_params);
-  add_call_param("coords", coords_type.get(), &call_params);
-  add_call_param("lod", &i32, &call_params);
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
 
   ast::CallExpression expr(
       std::make_unique<ast::IdentifierExpression>("textureLoad"),
       std::move(call_params));
 
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
+  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
 
-  if (coords_length > 1) {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 15u) << b()->error();
-  } else {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 12u) << b()->error();
-  }
+  EXPECT_EQ(DumpInstructions(b.types()),
+            R"(%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 1D 0 0 0 2 R16f
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%6 = OpTypeVector %4 4
+%8 = OpConstant %4 1
+%9 = OpTypeInt 32 1
+%10 = OpConstant %9 2
+)");
 
-  EXPECT_EQ(DumpInstructions(b()->types()),
-            generate_type_str(dim, format, false, type, 0, 2, type_id,
-                              coords_length));
-
-  EXPECT_EQ(DumpInstructions(b()->functions()[0].instructions()),
-            generate_ops_str(coords_length, "OpImageRead"));
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%7 = OpLoad %3 %1
+%5 = OpImageRead %6 %7 %8 Lod %10
+)");
 }
 
-TEST_P(Builder_TextureLoad, Sampled) {
-  auto dim = GetParam().dim;
-  auto type = GetParam().type;
-  auto format = GetParam().format;
-
+TEST_F(BuilderTest, Call_TextureLoad_Storage_RO_2d) {
   ast::type::F32Type f32;
-  b()->GenerateTypeIfNeeded(&f32);
-  ast::type::U32Type u32;
-  b()->GenerateTypeIfNeeded(&u32);
   ast::type::I32Type i32;
-  b()->GenerateTypeIfNeeded(&i32);
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::StorageTextureType s(ast::type::TextureDimension::k2d,
+                                  ast::type::StorageAccess::kRead,
+                                  ast::type::ImageFormat::kR16Float);
 
-  uint32_t type_id = 1;
-  if (type == TextureType::kU32) {
-    type_id = 2;
-  } else if (type == TextureType::kI32) {
-    type_id = 3;
-  }
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
 
-  auto coords_type = get_coords_type(dim, &i32);
+  ASSERT_TRUE(td.DetermineStorageTextureSubtype(&s)) << td.error();
 
-  uint32_t coords_length = 1;
-  if (coords_type->IsVector()) {
-    coords_length = coords_type->AsVector()->size();
-  }
+  b.push_function(Function{});
 
-  std::unique_ptr<ast::type::Type> s = subtype(type);
-  auto* texture_type = ctx()->type_mgr().Get(
-      std::make_unique<ast::type::SampledTextureType>(dim, s.get()));
+  ast::Variable tex("texture", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
 
-  EXPECT_TRUE(td()->Determine());
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
 
   ast::ExpressionList call_params;
-  b()->push_function(Function{});
-
-  add_call_param("texture", texture_type, &call_params);
-  add_call_param("coords", coords_type.get(), &call_params);
-  add_call_param("lod", &i32, &call_params);
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec2, std::move(vals)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
 
   ast::CallExpression expr(
       std::make_unique<ast::IdentifierExpression>("textureLoad"),
       std::move(call_params));
 
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
+  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
 
-  if (coords_length > 1) {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 15u) << b()->error();
-  } else {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 12u) << b()->error();
-  }
+  EXPECT_EQ(DumpInstructions(b.types()),
+            R"(%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 0 2 R16f
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%6 = OpTypeVector %4 4
+%8 = OpTypeVector %4 2
+%9 = OpConstant %4 1
+%10 = OpConstant %4 2
+%11 = OpConstantComposite %8 %9 %10
+%12 = OpTypeInt 32 1
+%13 = OpConstant %12 2
+)");
 
-  EXPECT_EQ(
-      DumpInstructions(b()->types()),
-      generate_type_str(dim, format, true, type, 0, 1, type_id, coords_length));
-
-  EXPECT_EQ(DumpInstructions(b()->functions()[0].instructions()),
-            generate_ops_str(coords_length, "OpImageFetch"));
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%7 = OpLoad %3 %1
+%5 = OpImageRead %6 %7 %11 Lod %13
+)");
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    BuilderTest,
-    Builder_TextureLoad,
-    testing::Values(
-        TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kF32,
-                          ast::type::ImageFormat::kR16Float},
-        TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kI32,
-                          ast::type::ImageFormat::kR16Sint},
-        TextureTestParams{ast::type::TextureDimension::k1d, TextureType::kU32,
-                          ast::type::ImageFormat::kR8Unorm},
-        TextureTestParams{ast::type::TextureDimension::k1dArray,
-                          TextureType::kF32, ast::type::ImageFormat::kR16Float},
-        TextureTestParams{ast::type::TextureDimension::k1dArray,
-                          TextureType::kI32, ast::type::ImageFormat::kR16Sint},
-        TextureTestParams{ast::type::TextureDimension::k1dArray,
-                          TextureType::kU32, ast::type::ImageFormat::kR8Unorm},
-        TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kF32,
-                          ast::type::ImageFormat::kR16Float},
-        TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kI32,
-                          ast::type::ImageFormat::kR16Sint},
-        TextureTestParams{ast::type::TextureDimension::k2d, TextureType::kU32,
-                          ast::type::ImageFormat::kR8Unorm},
-        TextureTestParams{ast::type::TextureDimension::k2dArray,
-                          TextureType::kF32, ast::type::ImageFormat::kR16Float},
-        TextureTestParams{ast::type::TextureDimension::k2dArray,
-                          TextureType::kI32, ast::type::ImageFormat::kR16Sint},
-        TextureTestParams{ast::type::TextureDimension::k2dArray,
-                          TextureType::kU32, ast::type::ImageFormat::kR8Unorm},
-        TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kF32,
-                          ast::type::ImageFormat::kR16Float},
-        TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kI32,
-                          ast::type::ImageFormat::kR16Sint},
-        TextureTestParams{ast::type::TextureDimension::k3d, TextureType::kU32,
-                          ast::type::ImageFormat::kR8Unorm}));
-
-class Builder_SampledTextureOperation : public Builder_TextureOperation {
- public:
-  std::string generate_type_str(ast::type::TextureDimension dim,
-                                bool unknown_format,
-                                TextureType type,
-                                uint32_t depth_literal,
-                                uint32_t sampled_literal,
-                                uint32_t type_id,
-                                uint32_t coords_length,
-                                bool optional_operand) {
-    std::string type_str = R"(%1 = OpTypeFloat 32
-%2 = OpTypeInt 32 0
-%3 = OpTypeInt 32 1
-)";
-
-    type_str +=
-        texture_line(dim, unknown_format, type, depth_literal, sampled_literal);
-
-    type_str += R"(%5 = OpTypePointer Private %6
-%7 = OpConstantNull %6
-%4 = OpVariable %5 Private %7
-%10 = OpTypeSampler
-%9 = OpTypePointer Private %10
-%11 = OpConstantNull %10
-%8 = OpVariable %9 Private %11
-)";
-
-    if (coords_length > 1) {
-      type_str += "%14 = OpTypeVector %3 " + std::to_string(coords_length) +
-                  R"(
-%13 = OpTypePointer Private %14
-%15 = OpConstantNull %14
-%12 = OpVariable %13 Private %15
-)";
-
-    } else {
-      type_str += R"(%13 = OpTypePointer Private %3
-%14 = OpConstantNull %3
-%12 = OpVariable %13 Private %14
-)";
-    }
-
-    if (coords_length > 1 && optional_operand) {
-      type_str += R"(%17 = OpTypePointer Private %1
-%18 = OpConstantNull %1
-%16 = OpVariable %17 Private %18
-%20 = OpTypeVector %)" +
-                  std::to_string(type_id) + R"( 4
-%25 = OpTypeSampledImage %6
-)";
-    } else if (coords_length > 1 && !optional_operand) {
-      type_str += R"(%17 = OpTypeVector %)" + std::to_string(type_id) + R"( 4
-%21 = OpTypeSampledImage %6
-)";
-    } else if (coords_length == 1 && optional_operand) {
-      type_str += R"(%16 = OpTypePointer Private %1
-%17 = OpConstantNull %1
-%15 = OpVariable %16 Private %17
-%19 = OpTypeVector %)" +
-                  std::to_string(type_id) + R"( 4
-%24 = OpTypeSampledImage %6
-)";
-    } else {
-      type_str += R"(%16 = OpTypeVector %)" + std::to_string(type_id) + R"( 4
-%20 = OpTypeSampledImage %6
-)";
-    }
-
-    return type_str;
-  }
-
-  std::string generate_ops_str(uint32_t coords_length,
-                               std::string op_name,
-                               std::string optional_operand) {
-    if (coords_length > 1 && optional_operand == "") {
-      return R"(%18 = OpLoad %6 %4
-%19 = OpLoad %10 %8
-%20 = OpLoad %14 %12
-%22 = OpSampledImage %21 %18 %19
-%16 = )" + op_name +
-             R"( %17 %22 %20
-)";
-    }
-
-    if (coords_length == 1 && optional_operand == "") {
-      return R"(%17 = OpLoad %6 %4
-%18 = OpLoad %10 %8
-%19 = OpLoad %3 %12
-%21 = OpSampledImage %20 %17 %18
-%15 = )" + op_name +
-             R"( %16 %21 %19
-)";
-    }
-
-    if (coords_length > 1 && optional_operand != "") {
-      return R"(%21 = OpLoad %6 %4
-%22 = OpLoad %10 %8
-%23 = OpLoad %14 %12
-%24 = OpLoad %1 %16
-%26 = OpSampledImage %25 %21 %22
-%19 = )" + op_name +
-             " %20 %26 %23 " + optional_operand + " %24\n";
-    }
-
-    return R"(%20 = OpLoad %6 %4
-%21 = OpLoad %10 %8
-%22 = OpLoad %3 %12
-%23 = OpLoad %1 %15
-%25 = OpSampledImage %24 %20 %21
-%18 = )" + op_name +
-           " %19 %25 %22 " + optional_operand + " %23\n";
-  }
-};
-
-TEST_P(Builder_SampledTextureOperation, TextureSample) {
-  auto dim = GetParam().dim;
-  auto type = GetParam().type;
-
+TEST_F(BuilderTest, Call_TextureLoad_Sampled_1d) {
   ast::type::F32Type f32;
-  b()->GenerateTypeIfNeeded(&f32);
-  ast::type::U32Type u32;
-  b()->GenerateTypeIfNeeded(&u32);
   ast::type::I32Type i32;
-  b()->GenerateTypeIfNeeded(&i32);
+  ast::type::SampledTextureType s(ast::type::TextureDimension::k1d, &f32);
 
-  uint32_t type_id = 1;
-  if (type == TextureType::kU32) {
-    type_id = 2;
-  } else if (type == TextureType::kI32) {
-    type_id = 3;
-  }
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
 
-  auto coords_type = get_coords_type(dim, &i32);
+  b.push_function(Function{});
 
-  uint32_t coords_length = 1;
-  if (coords_type->IsVector()) {
-    coords_length = coords_type->AsVector()->size();
-  }
-
-  auto sampler_type = std::make_unique<ast::type::SamplerType>(
-      ast::type::SamplerKind::kSampler);
-  std::unique_ptr<ast::type::Type> s = subtype(type);
-  auto* texture_type = ctx()->type_mgr().Get(
-      std::make_unique<ast::type::SampledTextureType>(dim, s.get()));
-
-  EXPECT_TRUE(td()->Determine());
+  ast::Variable tex("texture", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
 
   ast::ExpressionList call_params;
-  b()->push_function(Function{});
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
 
-  add_call_param("texture", texture_type, &call_params);
-  add_call_param("sampler", sampler_type.get(), &call_params);
-  add_call_param("coords", coords_type.get(), &call_params);
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("textureLoad"),
+      std::move(call_params));
+
+  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()),
+            R"(%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 1D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%6 = OpTypeVector %4 4
+%8 = OpConstant %4 1
+%9 = OpTypeInt 32 1
+%10 = OpConstant %9 2
+)");
+
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%7 = OpLoad %3 %1
+%5 = OpImageFetch %6 %7 %8 Lod %10
+)");
+}
+
+TEST_F(BuilderTest, Call_TextureLoad_Sampled_2d) {
+  ast::type::F32Type f32;
+  ast::type::I32Type i32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::SampledTextureType s(ast::type::TextureDimension::k2d, &f32);
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
+
+  b.push_function(Function{});
+
+  ast::Variable tex("texture", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec2, std::move(vals)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("textureLoad"),
+      std::move(call_params));
+
+  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()),
+            R"(%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%6 = OpTypeVector %4 4
+%8 = OpTypeVector %4 2
+%9 = OpConstant %4 1
+%10 = OpConstant %4 2
+%11 = OpConstantComposite %8 %9 %10
+%12 = OpTypeInt 32 1
+%13 = OpConstant %12 2
+)");
+
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%7 = OpLoad %3 %1
+%5 = OpImageFetch %6 %7 %11 Lod %13
+)");
+}
+
+TEST_F(BuilderTest, Call_TextureSample_1d) {
+  ast::type::F32Type f32;
+
+  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, &s);
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
+
+  b.push_function(Function{});
+
+  ast::Variable tex("texture", ast::StorageClass::kNone, &t);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
+
+  ast::Variable sampler("sampler", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&sampler);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&sampler)) << b.error();
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("sampler"));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
 
   ast::CallExpression expr(
       std::make_unique<ast::IdentifierExpression>("textureSample"),
       std::move(call_params));
 
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
+  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
+)");
 
-  if (coords_length > 1) {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 16u) << b()->error();
-  } else {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 15u) << b()->error();
-  }
-
-  EXPECT_EQ(
-      DumpInstructions(b()->types()),
-      generate_type_str(dim, true, type, 0, 1, type_id, coords_length, false));
-
-  EXPECT_EQ(DumpInstructions(b()->functions()[0].instructions()),
-            generate_ops_str(coords_length, "OpImageSampleImplicitLod", ""));
+  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_P(Builder_SampledTextureOperation, TextureSampleLevel) {
-  auto dim = GetParam().dim;
-  auto type = GetParam().type;
-
+TEST_F(BuilderTest, Call_TextureSample_2d) {
   ast::type::F32Type f32;
-  b()->GenerateTypeIfNeeded(&f32);
-  ast::type::U32Type u32;
-  b()->GenerateTypeIfNeeded(&u32);
-  ast::type::I32Type i32;
-  b()->GenerateTypeIfNeeded(&i32);
+  ast::type::VectorType vec2(&f32, 2);
 
-  uint32_t type_id = 1;
-  if (type == TextureType::kU32) {
-    type_id = 2;
-  } else if (type == TextureType::kI32) {
-    type_id = 3;
-  }
+  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k2d, &s);
 
-  auto coords_type = get_coords_type(dim, &i32);
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
 
-  uint32_t coords_length = 1;
-  if (coords_type->IsVector()) {
-    coords_length = coords_type->AsVector()->size();
-  }
+  b.push_function(Function{});
 
-  auto sampler_type = std::make_unique<ast::type::SamplerType>(
-      ast::type::SamplerKind::kSampler);
-  std::unique_ptr<ast::type::Type> s = subtype(type);
-  auto* texture_type = ctx()->type_mgr().Get(
-      std::make_unique<ast::type::SampledTextureType>(dim, s.get()));
+  ast::Variable tex("texture", ast::StorageClass::kNone, &t);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
 
-  EXPECT_TRUE(td()->Determine());
+  ast::Variable sampler("sampler", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&sampler);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&sampler)) << b.error();
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
 
   ast::ExpressionList call_params;
-  b()->push_function(Function{});
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("sampler"));
+  call_params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec2, std::move(vals)));
 
-  add_call_param("texture", texture_type, &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(
+      std::make_unique<ast::IdentifierExpression>("textureSample"),
+      std::move(call_params));
+
+  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(BuilderTest, Call_TextureSampleLevel_1d) {
+  ast::type::F32Type f32;
+
+  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, &f32);
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
+
+  b.push_function(Function{});
+
+  ast::Variable tex("texture", ast::StorageClass::kNone, &t);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
+
+  ast::Variable sampler("sampler", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&sampler);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&sampler)) << b.error();
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("sampler"));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
 
   ast::CallExpression expr(
       std::make_unique<ast::IdentifierExpression>("textureSampleLevel"),
       std::move(call_params));
 
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
+  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
 
-  if (coords_length > 1) {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 19u) << b()->error();
-  } else {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 18u) << b()->error();
-  }
+  EXPECT_EQ(DumpInstructions(b.types()),
+            R"(%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 1D 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()->types()),
-      generate_type_str(dim, true, type, 0, 1, type_id, coords_length, true));
-
-  EXPECT_EQ(DumpInstructions(b()->functions()[0].instructions()),
-            generate_ops_str(coords_length, "OpImageSampleExplicitLod", "Lod"));
+  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_P(Builder_SampledTextureOperation, TextureSampleBias) {
-  auto dim = GetParam().dim;
-  auto type = GetParam().type;
-
+TEST_F(BuilderTest, Call_TextureSampleLevel_2d) {
   ast::type::F32Type f32;
-  b()->GenerateTypeIfNeeded(&f32);
-  ast::type::U32Type u32;
-  b()->GenerateTypeIfNeeded(&u32);
-  ast::type::I32Type i32;
-  b()->GenerateTypeIfNeeded(&i32);
+  ast::type::VectorType vec2(&f32, 2);
 
-  uint32_t type_id = 1;
-  if (type == TextureType::kU32) {
-    type_id = 2;
-  } else if (type == TextureType::kI32) {
-    type_id = 3;
-  }
+  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k2d, &f32);
 
-  auto coords_type = get_coords_type(dim, &i32);
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
 
-  uint32_t coords_length = 1;
-  if (coords_type->IsVector()) {
-    coords_length = coords_type->AsVector()->size();
-  }
+  b.push_function(Function{});
 
-  auto sampler_type = std::make_unique<ast::type::SamplerType>(
-      ast::type::SamplerKind::kSampler);
-  std::unique_ptr<ast::type::Type> s = subtype(type);
-  auto* texture_type = ctx()->type_mgr().Get(
-      std::make_unique<ast::type::SampledTextureType>(dim, s.get()));
+  ast::Variable tex("texture", ast::StorageClass::kNone, &t);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
 
-  EXPECT_TRUE(td()->Determine());
+  ast::Variable sampler("sampler", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&sampler);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&sampler)) << b.error();
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
 
   ast::ExpressionList call_params;
-  b()->push_function(Function{});
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("sampler"));
+  call_params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec2, std::move(vals)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
 
-  add_call_param("texture", texture_type, &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(
+      std::make_unique<ast::IdentifierExpression>("textureSampleLevel"),
+      std::move(call_params));
+
+  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 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 = 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(BuilderTest, Call_TextureSampleBias_1d) {
+  ast::type::F32Type f32;
+
+  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, &f32);
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
+
+  b.push_function(Function{});
+
+  ast::Variable tex("texture", ast::StorageClass::kNone, &t);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
+
+  ast::Variable sampler("sampler", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&sampler);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&sampler)) << b.error();
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("sampler"));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
 
   ast::CallExpression expr(
       std::make_unique<ast::IdentifierExpression>("textureSampleBias"),
       std::move(call_params));
 
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
+  EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
 
-  if (coords_length > 1) {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 19u) << b()->error();
-  } else {
-    EXPECT_EQ(b()->GenerateExpression(&expr), 18u) << b()->error();
-  }
+  EXPECT_EQ(DumpInstructions(b.types()),
+            R"(%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 1D 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()->types()),
-      generate_type_str(dim, true, type, 0, 1, type_id, coords_length, true));
-
-  EXPECT_EQ(
-      DumpInstructions(b()->functions()[0].instructions()),
-      generate_ops_str(coords_length, "OpImageSampleImplicitLod", "Bias"));
+  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
+)");
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    BuilderTest,
-    Builder_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}));
-
-class Builder_DepthTextureOperation : public Builder_TextureOperation {
- public:
-  std::string generate_type_str(ast::type::TextureDimension dim,
-                                uint32_t coords_length) {
-    std::string type_str = R"(%1 = OpTypeFloat 32
-%2 = OpTypeInt 32 0
-%3 = OpTypeInt 32 1
-)";
-
-    type_str += texture_line(dim, true, TextureType::kF32, 1, 1);
-
-    type_str += R"(%5 = OpTypePointer Private %6
-%7 = OpConstantNull %6
-%4 = OpVariable %5 Private %7
-%10 = OpTypeSampler
-%9 = OpTypePointer Private %10
-%11 = OpConstantNull %10
-%8 = OpVariable %9 Private %11
-%14 = OpTypeVector %1 )" +
-                std::to_string(coords_length) + R"(
-%13 = OpTypePointer Private %14
-%15 = OpConstantNull %14
-%12 = OpVariable %13 Private %15
-%17 = OpTypePointer Private %1
-%18 = OpConstantNull %1
-%16 = OpVariable %17 Private %18
-%24 = OpTypeSampledImage %6
-%26 = OpConstant %1 0
-)";
-
-    return type_str;
-  }
-
-  std::string generate_ops_str() {
-    return R"(%20 = OpLoad %6 %4
-%21 = OpLoad %10 %8
-%22 = OpLoad %14 %12
-%23 = OpLoad %1 %16
-%25 = OpSampledImage %24 %20 %21
-%19 = OpImageSampleDrefExplicitLod %1 %25 %22 %23 Lod %26
-)";
-  }
-};
-
-TEST_P(Builder_DepthTextureOperation, TextureSampleCompare) {
-  auto dim = GetParam().dim;
-  auto type = GetParam().type;
-
+TEST_F(BuilderTest, Call_TextureSampleBias_2d) {
   ast::type::F32Type f32;
-  b()->GenerateTypeIfNeeded(&f32);
-  ast::type::U32Type u32;
-  b()->GenerateTypeIfNeeded(&u32);
-  ast::type::I32Type i32;
-  b()->GenerateTypeIfNeeded(&i32);
+  ast::type::VectorType vec2(&f32, 2);
 
-  auto coords_type = get_coords_type(dim, &f32);
+  ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, &f32);
 
-  uint32_t coords_length = coords_type->AsVector()->size();
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
 
-  auto sampler_type = std::make_unique<ast::type::SamplerType>(
-      ast::type::SamplerKind::kComparisonSampler);
-  std::unique_ptr<ast::type::Type> s = subtype(type);
-  auto* texture_type =
-      ctx()->type_mgr().Get(std::make_unique<ast::type::DepthTextureType>(dim));
+  b.push_function(Function{});
+
+  ast::Variable tex("texture", ast::StorageClass::kNone, &t);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
+
+  ast::Variable sampler("sampler", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&sampler);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&sampler)) << b.error();
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
 
   ast::ExpressionList call_params;
-  b()->push_function(Function{});
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("sampler"));
+  call_params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec2, std::move(vals)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
 
-  add_call_param("texture", texture_type, &call_params);
-  add_call_param("sampler", 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(
+      std::make_unique<ast::IdentifierExpression>("textureSampleBias"),
+      std::move(call_params));
+
+  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
+%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(BuilderTest, Call_TextureSampleCompare) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+
+  ast::type::SamplerType s(ast::type::SamplerKind::kComparisonSampler);
+  ast::type::DepthTextureType t(ast::type::TextureDimension::k2d);
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
+
+  b.push_function(Function{});
+
+  ast::Variable tex("texture", ast::StorageClass::kNone, &t);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
+
+  ast::Variable sampler("sampler", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&sampler);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&sampler)) << b.error();
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("sampler"));
+  call_params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec2, std::move(vals)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
 
   ast::CallExpression expr(
       std::make_unique<ast::IdentifierExpression>("textureSampleCompare"),
       std::move(call_params));
 
-  EXPECT_TRUE(td()->DetermineResultType(&expr));
-
-  EXPECT_EQ(b()->GenerateExpression(&expr), 19u) << b()->error();
-
-  EXPECT_EQ(DumpInstructions(b()->types()),
-            generate_type_str(dim, coords_length));
-
-  EXPECT_EQ(DumpInstructions(b()->functions()[0].instructions()),
-            generate_ops_str());
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    BuilderTest,
-    Builder_DepthTextureOperation,
-    testing::Values(TextureTestParams{ast::type::TextureDimension::k2d},
-                    TextureTestParams{ast::type::TextureDimension::k2dArray},
-                    TextureTestParams{ast::type::TextureDimension::kCube},
-                    TextureTestParams{
-                        ast::type::TextureDimension::kCubeArray}));
-
-// This tests that we do not push OpTypeSampledImage and float_0 type twice.
-TEST_F(Builder_TextureOperation, TextureSampleCompareTwice) {
-  ast::type::F32Type f32;
-  b()->GenerateTypeIfNeeded(&f32);
-
-  auto coords_type = get_coords_type(ast::type::TextureDimension::k2d, &f32);
-  auto sampler_type = std::make_unique<ast::type::SamplerType>(
-      ast::type::SamplerKind::kComparisonSampler);
-  auto* texture_type =
-      ctx()->type_mgr().Get(std::make_unique<ast::type::DepthTextureType>(
-          ast::type::TextureDimension::k2d));
-
-  b()->push_function(Function{});
-
-  ast::ExpressionList call_params_first;
-  add_call_param("texture", texture_type, &call_params_first);
-  add_call_param("sampler", sampler_type.get(), &call_params_first);
-  add_call_param("coords", coords_type.get(), &call_params_first);
-  add_call_param("depth_reference", &f32, &call_params_first);
-
-  ast::ExpressionList call_params_second;
-  add_call_param("texture", texture_type, &call_params_second);
-  add_call_param("sampler", sampler_type.get(), &call_params_second);
-  add_call_param("coords", coords_type.get(), &call_params_second);
-  add_call_param("depth_reference", &f32, &call_params_second);
-
-  ast::CallExpression expr_first(
-      std::make_unique<ast::IdentifierExpression>("textureSampleCompare"),
-      std::move(call_params_first));
-  ast::CallExpression expr_second(
-      std::make_unique<ast::IdentifierExpression>("textureSampleCompare"),
-      std::move(call_params_second));
-
-  EXPECT_TRUE(td()->DetermineResultType(&expr_first));
-  EXPECT_TRUE(td()->DetermineResultType(&expr_second));
-
-  EXPECT_EQ(b()->GenerateExpression(&expr_first), 21u) << b()->error();
-  EXPECT_EQ(b()->GenerateExpression(&expr_second), 29u) << b()->error();
-
-  EXPECT_EQ(DumpInstructions(b()->types()), R"(%1 = OpTypeFloat 32
-%4 = OpTypeImage %1 2D 1 0 0 1 Unknown
-%3 = OpTypePointer Private %4
-%5 = OpConstantNull %4
-%2 = OpVariable %3 Private %5
-%8 = OpTypeSampler
-%7 = OpTypePointer Private %8
-%9 = OpConstantNull %8
-%6 = OpVariable %7 Private %9
-%12 = OpTypeVector %1 2
-%11 = OpTypePointer Private %12
-%13 = OpConstantNull %12
-%10 = OpVariable %11 Private %13
-%15 = OpTypePointer Private %1
-%16 = OpConstantNull %1
-%14 = OpVariable %15 Private %16
-%17 = OpVariable %3 Private %5
-%18 = OpVariable %7 Private %9
-%19 = OpVariable %11 Private %13
-%20 = OpVariable %15 Private %16
-%26 = OpTypeSampledImage %4
-%28 = OpConstant %1 0
+  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 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"(%22 = OpLoad %4 %17
-%23 = OpLoad %8 %18
-%24 = OpLoad %12 %19
-%25 = OpLoad %1 %20
-%27 = OpSampledImage %26 %22 %23
-%21 = OpImageSampleDrefExplicitLod %1 %27 %24 %25 Lod %28
-%30 = OpLoad %4 %17
-%31 = OpLoad %8 %18
-%32 = OpLoad %12 %19
-%33 = OpLoad %1 %20
-%34 = OpSampledImage %26 %30 %31
-%29 = OpImageSampleDrefExplicitLod %1 %34 %32 %33 Lod %28
+  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
+)");
+}
+
+// This tests that we do not push OpTypeSampledImage and float_0 type twice.
+TEST_F(BuilderTest, Call_TextureSampleCompare_Twice) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+
+  ast::type::SamplerType s(ast::type::SamplerKind::kComparisonSampler);
+  ast::type::DepthTextureType t(ast::type::TextureDimension::k2d);
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
+
+  b.push_function(Function{});
+
+  ast::Variable tex("texture", ast::StorageClass::kNone, &t);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
+
+  ast::Variable sampler("sampler", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&sampler);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&sampler)) << b.error();
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("sampler"));
+  call_params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec2, std::move(vals)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::CallExpression expr1(
+      std::make_unique<ast::IdentifierExpression>("textureSampleCompare"),
+      std::move(call_params));
+
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("sampler"));
+  call_params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec2, std::move(vals)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::CallExpression expr2(
+      std::make_unique<ast::IdentifierExpression>("textureSampleCompare"),
+      std::move(call_params));
+
+  EXPECT_TRUE(td.DetermineResultType(&expr1)) << td.error();
+  EXPECT_TRUE(td.DetermineResultType(&expr2)) << td.error();
+
+  EXPECT_EQ(b.GenerateExpression(&expr1), 8u) << b.error();
+  EXPECT_EQ(b.GenerateExpression(&expr2), 18u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), 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
+%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
 )");
 }