Implement textureGather, textureGatherCompare

All writers implemented, along with resolving and validation.

TODO: SPIR-V Reader.

Bug: tint:1330
Change-Id: I8ba2f6023749474f80efb8a5422ac187e6c73a69
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/71820
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton
2021-12-08 19:54:08 +00:00
committed by Tint LUCI CQ
parent 2f7730a16e
commit 3703522d41
235 changed files with 13068 additions and 3580 deletions

View File

@@ -127,7 +127,7 @@ std::ostream& operator<<(std::ostream& out, const TextureOverloadCase& data) {
out << "<unused>";
}
out << "\n";
out << "access: " << data.access << "\n";
out << "access: " << data.access << "\n";
out << "image_format: " << data.image_format << "\n";
out << "texture_dimension: " << data.texture_dimension << "\n";
out << "texture_data_type: " << data.texture_data_type << "\n";
@@ -461,6 +461,349 @@ std::vector<TextureOverloadCase> TextureOverloadCase::ValidCases() {
"textureDimensions",
[](ProgramBuilder* b) { return b->ExprList("texture"); },
},
{
ValidTextureOverload::kGather2dF32,
"textureGather(component : i32,\n"
" t : texture_2d<T>,\n"
" s : sampler,\n"
" coords : vec2<f32>) -> vec4<T>",
TextureKind::kRegular,
ast::SamplerKind::kSampler,
ast::TextureDimension::k2d,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList(0, // component
"texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f)); // coords
},
},
{
ValidTextureOverload::kGather2dOffsetF32,
"textureGather(component : i32,\n"
" t : texture_2d<T>,\n"
" s : sampler,\n"
" coords : vec2<f32>,\n"
" offset : vec2<i32>) -> vec4<T>",
TextureKind::kRegular,
ast::SamplerKind::kSampler,
ast::TextureDimension::k2d,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList(0, // component
"texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f), // coords
b->vec2<i32>(3, 4)); // offset
},
},
{
ValidTextureOverload::kGather2dArrayF32,
"textureGather(component : i32,\n"
" t : texture_2d_array<T>,\n"
" s : sampler,\n"
" coords : vec2<f32>,\n"
" array_index : i32) -> vec4<T>",
TextureKind::kRegular,
ast::SamplerKind::kSampler,
ast::TextureDimension::k2dArray,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList(0, // component
"texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f), // coords
3); // array index
},
},
{
ValidTextureOverload::kGather2dArrayOffsetF32,
"textureGather(component : i32,\n"
" t : texture_2d_array<T>,\n"
" s : sampler,\n"
" coords : vec2<f32>,\n"
" array_index : i32,\n"
" offset : vec2<i32>) -> vec4<T>",
TextureKind::kRegular,
ast::SamplerKind::kSampler,
ast::TextureDimension::k2dArray,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList(0, // component
"texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f), // coords
3, // array_index
b->vec2<i32>(4, 5)); // offset
},
},
{
ValidTextureOverload::kGatherCubeF32,
"textureGather(component : i32,\n"
" t : texture_cube<T>,\n"
" s : sampler,\n"
" coords : vec3<f32>) -> vec4<T>",
TextureKind::kRegular,
ast::SamplerKind::kSampler,
ast::TextureDimension::kCube,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList(0, // component
"texture", // t
"sampler", // s
b->vec3<f32>(1.f, 2.f, 3.f)); // coords
},
},
{
ValidTextureOverload::kGatherCubeArrayF32,
"textureGather(component : i32,\n"
" t : texture_cube_array<T>,\n"
" s : sampler,\n"
" coords : vec3<f32>,\n"
" array_index : i32) -> vec4<T>",
TextureKind::kRegular,
ast::SamplerKind::kSampler,
ast::TextureDimension::kCubeArray,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList(0, // component
"texture", // t
"sampler", // s
b->vec3<f32>(1.f, 2.f, 3.f), // coords
4); // array_index
},
},
{
ValidTextureOverload::kGatherDepth2dF32,
"textureGather(t : texture_depth_2d,\n"
" s : sampler,\n"
" coords : vec2<f32>) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kSampler,
ast::TextureDimension::k2d,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f)); // coords
},
},
{
ValidTextureOverload::kGatherDepth2dOffsetF32,
"textureGather(t : texture_depth_2d,\n"
" s : sampler,\n"
" coords : vec2<f32>,\n"
" offset : vec2<i32>) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kSampler,
ast::TextureDimension::k2d,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f), // coords
b->vec2<i32>(3, 4)); // offset
},
},
{
ValidTextureOverload::kGatherDepth2dArrayF32,
"textureGather(t : texture_depth_2d_array,\n"
" s : sampler,\n"
" coords : vec2<f32>,\n"
" array_index : i32) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kSampler,
ast::TextureDimension::k2dArray,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f), // coords
3); // array_index
},
},
{
ValidTextureOverload::kGatherDepth2dArrayOffsetF32,
"textureGather(t : texture_depth_2d_array,\n"
" s : sampler,\n"
" coords : vec2<f32>,\n"
" array_index : i32,\n"
" offset : vec2<i32>) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kSampler,
ast::TextureDimension::k2dArray,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f), // coords
3, // array_index
b->vec2<i32>(4, 5)); // offset
},
},
{
ValidTextureOverload::kGatherDepthCubeF32,
"textureGather(t : texture_depth_cube,\n"
" s : sampler,\n"
" coords : vec3<f32>) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kSampler,
ast::TextureDimension::kCube,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec3<f32>(1.f, 2.f, 3.f)); // coords
},
},
{
ValidTextureOverload::kGatherDepthCubeArrayF32,
"textureGather(t : texture_depth_cube_array,\n"
" s : sampler,\n"
" coords : vec3<f32>,\n"
" array_index : i32) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kSampler,
ast::TextureDimension::kCubeArray,
TextureDataType::kF32,
"textureGather",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec3<f32>(1.f, 2.f, 3.f), // coords
4); // array_index
},
},
{
ValidTextureOverload::kGatherCompareDepth2dF32,
"textureGatherCompare(t : texture_depth_2d,\n"
" s : sampler_comparison,\n"
" coords : vec2<f32>,\n"
" depth_ref : f32) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kComparisonSampler,
ast::TextureDimension::k2d,
TextureDataType::kF32,
"textureGatherCompare",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f), // coords
3.f); // depth_ref
},
},
{
ValidTextureOverload::kGatherCompareDepth2dOffsetF32,
"textureGatherCompare(t : texture_depth_2d,\n"
" s : sampler_comparison,\n"
" coords : vec2<f32>,\n"
" depth_ref : f32,\n"
" offset : vec2<i32>) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kComparisonSampler,
ast::TextureDimension::k2d,
TextureDataType::kF32,
"textureGatherCompare",
[](ProgramBuilder* b) {
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::kGatherCompareDepth2dArrayF32,
"textureGatherCompare(t : texture_depth_2d_array,\n"
" s : sampler_comparison,\n"
" coords : vec2<f32>,\n"
" array_index : i32,\n"
" depth_ref : f32) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kComparisonSampler,
ast::TextureDimension::k2dArray,
TextureDataType::kF32,
"textureGatherCompare",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f), // coords
3, // array_index
4.f); // depth_ref
},
},
{
ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32,
"textureGatherCompare(t : texture_depth_2d_array,\n"
" s : sampler_comparison,\n"
" coords : vec2<f32>,\n"
" array_index : i32,\n"
" depth_ref : f32,\n"
" offset : vec2<i32>) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kComparisonSampler,
ast::TextureDimension::k2dArray,
TextureDataType::kF32,
"textureGatherCompare",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec2<f32>(1.f, 2.f), // coords
3, // array_index
4.f, // depth_ref
b->vec2<i32>(5, 6)); // offset
},
},
{
ValidTextureOverload::kGatherCompareDepthCubeF32,
"textureGatherCompare(t : texture_depth_cube,\n"
" s : sampler_comparison,\n"
" coords : vec3<f32>,\n"
" depth_ref : f32) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kComparisonSampler,
ast::TextureDimension::kCube,
TextureDataType::kF32,
"textureGatherCompare",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec3<f32>(1.f, 2.f, 3.f), // coords
4.f); // depth_ref
},
},
{
ValidTextureOverload::kGatherCompareDepthCubeArrayF32,
"textureGatherCompare(t : texture_depth_cube_array,\n"
" s : sampler_comparison,\n"
" coords : vec3<f32>,\n"
" array_index : i32,\n"
" depth_ref : f32) -> vec4<f32>",
TextureKind::kDepth,
ast::SamplerKind::kComparisonSampler,
ast::TextureDimension::kCubeArray,
TextureDataType::kF32,
"textureGatherCompare",
[](ProgramBuilder* b) {
return b->ExprList("texture", // t
"sampler", // s
b->vec3<f32>(1.f, 2.f, 3.f), // coords
4, // array_index
5.f); // depth_ref
},
},
{
ValidTextureOverload::kNumLayers2dArray,
"textureNumLayers(t : texture_2d_array<f32>) -> i32",

View File

@@ -65,6 +65,24 @@ enum class ValidTextureOverload {
kDimensionsStorageWO2d,
kDimensionsStorageWO2dArray,
kDimensionsStorageWO3d,
kGather2dF32,
kGather2dOffsetF32,
kGather2dArrayF32,
kGather2dArrayOffsetF32,
kGatherCubeF32,
kGatherCubeArrayF32,
kGatherDepth2dF32,
kGatherDepth2dOffsetF32,
kGatherDepth2dArrayF32,
kGatherDepth2dArrayOffsetF32,
kGatherDepthCubeF32,
kGatherDepthCubeArrayF32,
kGatherCompareDepth2dF32,
kGatherCompareDepth2dOffsetF32,
kGatherCompareDepth2dArrayF32,
kGatherCompareDepth2dArrayOffsetF32,
kGatherCompareDepthCubeF32,
kGatherCompareDepthCubeArrayF32,
kNumLayers2dArray,
kNumLayersCubeArray,
kNumLayersDepth2dArray,

File diff suppressed because it is too large Load Diff

View File

@@ -423,6 +423,24 @@ fn textureDimensions<F: texel_format, A: write_only>(texture: texture_storage_2d
fn textureDimensions<F: texel_format, A: write_only>(texture: texture_storage_2d_array<F, A>) -> vec2<i32>
fn textureDimensions<F: texel_format, A: write_only>(texture: texture_storage_3d<F, A>) -> vec3<i32>
fn textureDimensions(texture: texture_external) -> vec2<i32>
fn textureGather<T: fiu32>(component: i32, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>) -> vec4<T>
fn textureGather<T: fiu32>(component: i32, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<T>
fn textureGather<T: fiu32>(component: i32, texture: texture_2d_array<T>, sampler: sampler, coords: vec2<f32>, array_index: i32) -> vec4<T>
fn textureGather<T: fiu32>(component: i32, texture: texture_2d_array<T>, sampler: sampler, coords: vec2<f32>, array_index: i32, offset: vec2<i32>) -> vec4<T>
fn textureGather<T: fiu32>(component: i32, texture: texture_cube<T>, sampler: sampler, coords: vec3<f32>) -> vec4<T>
fn textureGather<T: fiu32>(component: i32, texture: texture_cube_array<T>, sampler: sampler, coords: vec3<f32>, array_index: i32) -> vec4<T>
fn textureGather(texture: texture_depth_2d, sampler: sampler, coords: vec2<f32>) -> vec4<f32>
fn textureGather(texture: texture_depth_2d, sampler: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<f32>
fn textureGather(texture: texture_depth_2d_array, sampler: sampler, coords: vec2<f32>, array_index: i32) -> vec4<f32>
fn textureGather(texture: texture_depth_2d_array, sampler: sampler, coords: vec2<f32>, array_index: i32, offset: vec2<i32>) -> vec4<f32>
fn textureGather(texture: texture_depth_cube, sampler: sampler, coords: vec3<f32>) -> vec4<f32>
fn textureGather(texture: texture_depth_cube_array, sampler: sampler, coords: vec3<f32>, array_index: i32) -> vec4<f32>
fn textureGatherCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> vec4<f32>
fn textureGatherCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> vec4<f32>
fn textureGatherCompare(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: i32, depth_ref: f32) -> vec4<f32>
fn textureGatherCompare(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: i32, depth_ref: f32, offset: vec2<i32>) -> vec4<f32>
fn textureGatherCompare(texture: texture_depth_cube, sampler: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> vec4<f32>
fn textureGatherCompare(texture: texture_depth_cube_array, sampler: sampler_comparison, coords: vec3<f32>, array_index: i32, depth_ref: f32) -> vec4<f32>
fn textureNumLayers<T: fiu32>(texture: texture_2d_array<T>) -> i32
fn textureNumLayers<T: fiu32>(texture: texture_cube_array<T>) -> i32
fn textureNumLayers(texture: texture_depth_2d_array) -> i32

View File

@@ -1744,6 +1744,42 @@ const char* expected_texture_overload(
case ValidTextureOverload::kDimensionsStorageWO2dArray:
case ValidTextureOverload::kDimensionsStorageWO3d:
return R"(textureDimensions(texture))";
case ValidTextureOverload::kGather2dF32:
return R"(textureGather(component, texture, sampler, coords))";
case ValidTextureOverload::kGather2dOffsetF32:
return R"(textureGather(component, texture, sampler, coords, offset))";
case ValidTextureOverload::kGather2dArrayF32:
return R"(textureGather(component, texture, sampler, coords, array_index))";
case ValidTextureOverload::kGather2dArrayOffsetF32:
return R"(textureGather(component, texture, sampler, coords, array_index, offset))";
case ValidTextureOverload::kGatherCubeF32:
return R"(textureGather(component, texture, sampler, coords))";
case ValidTextureOverload::kGatherCubeArrayF32:
return R"(textureGather(component, texture, sampler, coords, array_index))";
case ValidTextureOverload::kGatherDepth2dF32:
return R"(textureGather(texture, sampler, coords))";
case ValidTextureOverload::kGatherDepth2dOffsetF32:
return R"(textureGather(texture, sampler, coords, offset))";
case ValidTextureOverload::kGatherDepth2dArrayF32:
return R"(textureGather(texture, sampler, coords, array_index))";
case ValidTextureOverload::kGatherDepth2dArrayOffsetF32:
return R"(textureGather(texture, sampler, coords, array_index, offset))";
case ValidTextureOverload::kGatherDepthCubeF32:
return R"(textureGather(texture, sampler, coords))";
case ValidTextureOverload::kGatherDepthCubeArrayF32:
return R"(textureGather(texture, sampler, coords, array_index))";
case ValidTextureOverload::kGatherCompareDepth2dF32:
return R"(textureGatherCompare(texture, sampler, coords, depth_ref))";
case ValidTextureOverload::kGatherCompareDepth2dOffsetF32:
return R"(textureGatherCompare(texture, sampler, coords, depth_ref, offset))";
case ValidTextureOverload::kGatherCompareDepth2dArrayF32:
return R"(textureGatherCompare(texture, sampler, coords, array_index, depth_ref))";
case ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32:
return R"(textureGatherCompare(texture, sampler, coords, array_index, depth_ref, offset))";
case ValidTextureOverload::kGatherCompareDepthCubeF32:
return R"(textureGatherCompare(texture, sampler, coords, depth_ref))";
case ValidTextureOverload::kGatherCompareDepthCubeArrayF32:
return R"(textureGatherCompare(texture, sampler, coords, array_index, depth_ref))";
case ValidTextureOverload::kNumLayers2dArray:
case ValidTextureOverload::kNumLayersCubeArray:
case ValidTextureOverload::kNumLayersDepth2dArray:
@@ -1965,6 +2001,26 @@ TEST_P(ResolverIntrinsicTest_Texture, Call) {
EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
} else if (std::string(param.function) == "textureStore") {
EXPECT_TRUE(TypeOf(call)->Is<sem::Void>());
} else if (std::string(param.function) == "textureGather") {
auto* vec = As<sem::Vector>(TypeOf(call));
ASSERT_NE(vec, nullptr);
EXPECT_EQ(vec->Width(), 4u);
switch (param.texture_data_type) {
case ast::intrinsic::test::TextureDataType::kF32:
EXPECT_TRUE(vec->type()->Is<sem::F32>());
break;
case ast::intrinsic::test::TextureDataType::kU32:
EXPECT_TRUE(vec->type()->Is<sem::U32>());
break;
case ast::intrinsic::test::TextureDataType::kI32:
EXPECT_TRUE(vec->type()->Is<sem::I32>());
break;
}
} else if (std::string(param.function) == "textureGatherCompare") {
auto* vec = As<sem::Vector>(TypeOf(call));
ASSERT_NE(vec, nullptr);
EXPECT_EQ(vec->Width(), 4u);
EXPECT_TRUE(vec->type()->Is<sem::F32>());
} else {
switch (param.texture_kind) {
case ast::intrinsic::test::TextureKind::kRegular:

View File

@@ -121,7 +121,7 @@ TEST_F(ResolverIntrinsicValidationTest, IntrinsicRedeclaredAsStruct) {
R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a struct)");
}
namespace TextureSamplerOffset {
namespace texture_constexpr_args {
using TextureOverloadCase = ast::intrinsic::test::TextureOverloadCase;
using ValidTextureOverload = ast::intrinsic::test::ValidTextureOverload;
@@ -131,194 +131,272 @@ using u32 = ProgramBuilder::u32;
using i32 = ProgramBuilder::i32;
using f32 = ProgramBuilder::f32;
static std::vector<TextureOverloadCase> ValidCases() {
static std::vector<TextureOverloadCase> TextureCases(
std::unordered_set<ValidTextureOverload> overloads) {
std::vector<TextureOverloadCase> cases;
for (auto c : TextureOverloadCase::ValidCases()) {
if (std::string(c.function).find("textureSample") == 0) {
if (std::string(c.description).find("offset ") != std::string::npos) {
cases.push_back(c);
}
if (overloads.count(c.overload)) {
cases.push_back(c);
}
}
return cases;
}
struct OffsetCase {
bool is_valid;
int32_t x;
int32_t y;
int32_t z;
int32_t illegal_value = 0;
enum class Position {
kFirst,
kLast,
};
static std::vector<OffsetCase> OffsetCases() {
return {
{true, 0, 1, 2}, //
{true, 7, -8, 7}, //
{false, 10, 10, 20, 10}, //
{false, -9, 0, 0, -9}, //
{false, 0, 8, 0, 8}, //
struct Parameter {
const char* const name;
const Position position;
int min;
int max;
};
class Constexpr {
public:
enum class Kind {
kScalar,
kVec2,
kVec3,
kVec3_Scalar_Vec2,
kVec3_Vec2_Scalar,
kEmptyVec2,
kEmptyVec3,
};
}
using IntrinsicTextureSamplerValidationTest =
ResolverTestWithParam<std::tuple<TextureOverloadCase, // texture info
OffsetCase // offset info
>>;
TEST_P(IntrinsicTextureSamplerValidationTest, ConstExpr) {
auto& p = GetParam();
auto param = std::get<0>(p);
auto offset = std::get<1>(p);
param.BuildTextureVariable(this);
param.BuildSamplerVariable(this);
Constexpr(int32_t invalid_idx,
Kind k,
int32_t x = 0,
int32_t y = 0,
int32_t z = 0)
: invalid_index(invalid_idx), kind(k), values{x, y, z} {}
auto args = param.args(this);
// Make Resolver visit the Node about to be removed
WrapInFunction(args.back());
args.pop_back();
if (NumCoordinateAxes(param.texture_dimension) == 2) {
args.push_back(
Construct(Source{{12, 34}}, ty.vec2<i32>(), offset.x, offset.y));
} else if (NumCoordinateAxes(param.texture_dimension) == 3) {
args.push_back(Construct(Source{{12, 34}}, ty.vec3<i32>(), offset.x,
offset.y, offset.z));
const ast::Expression* operator()(Source src, ProgramBuilder& b) {
switch (kind) {
case Kind::kScalar:
return b.Expr(src, values[0]);
case Kind::kVec2:
return b.Construct(src, b.ty.vec2<i32>(), values[0], values[1]);
case Kind::kVec3:
return b.Construct(src, b.ty.vec3<i32>(), values[0], values[1],
values[2]);
case Kind::kVec3_Scalar_Vec2:
return b.Construct(src, b.ty.vec3<i32>(), values[0],
b.vec2<i32>(values[1], values[2]));
case Kind::kVec3_Vec2_Scalar:
return b.Construct(src, b.ty.vec3<i32>(),
b.vec2<i32>(values[0], values[1]), values[2]);
case Kind::kEmptyVec2:
return b.Construct(src, b.ty.vec2<i32>());
case Kind::kEmptyVec3:
return b.Construct(src, b.ty.vec3<i32>());
}
return nullptr;
}
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
static const constexpr int32_t kValid = -1;
const int32_t invalid_index; // Expected error value, or kValid
const Kind kind;
const std::array<int32_t, 3> values;
};
if (offset.is_valid) {
static std::ostream& operator<<(std::ostream& out, Parameter param) {
return out << param.name;
}
static std::ostream& operator<<(std::ostream& out, Constexpr expr) {
switch (expr.kind) {
case Constexpr::Kind::kScalar:
return out << expr.values[0];
case Constexpr::Kind::kVec2:
return out << "vec2(" << expr.values[0] << ", " << expr.values[1] << ")";
case Constexpr::Kind::kVec3:
return out << "vec3(" << expr.values[0] << ", " << expr.values[1] << ", "
<< expr.values[2] << ")";
case Constexpr::Kind::kVec3_Scalar_Vec2:
return out << "vec3(" << expr.values[0] << ", vec2(" << expr.values[1]
<< ", " << expr.values[2] << "))";
case Constexpr::Kind::kVec3_Vec2_Scalar:
return out << "vec3(vec2(" << expr.values[0] << ", " << expr.values[1]
<< "), " << expr.values[2] << ")";
case Constexpr::Kind::kEmptyVec2:
return out << "vec2()";
case Constexpr::Kind::kEmptyVec3:
return out << "vec3()";
}
return out;
}
using IntrinsicTextureConstExprArgValidationTest = ResolverTestWithParam<
std::tuple<TextureOverloadCase, Parameter, Constexpr>>;
TEST_P(IntrinsicTextureConstExprArgValidationTest, Immediate) {
auto& p = GetParam();
auto overload = std::get<0>(p);
auto param = std::get<1>(p);
auto expr = std::get<2>(p);
overload.BuildTextureVariable(this);
overload.BuildSamplerVariable(this);
auto args = overload.args(this);
auto*& arg_to_replace =
(param.position == Position::kFirst) ? args.front() : args.back();
// BuildTextureVariable() uses a Literal for scalars, and a CallExpression for
// a vector constructor.
bool is_vector = arg_to_replace->Is<ast::CallExpression>();
// Make the expression to be replaced, reachable. This keeps the resolver
// happy.
WrapInFunction(arg_to_replace);
arg_to_replace = expr(Source{{12, 34}}, *this);
// Call the intrinsic with the constexpr argument replaced
Func("func", {}, ty.void_(), {CallStmt(Call(overload.function, args))},
{Stage(ast::PipelineStage::kFragment)});
if (expr.invalid_index == Constexpr::kValid) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve());
std::stringstream err;
err << "12:34 error: each offset component of '" << param.function
<< "' must be at least -8 and at most 7. found: '"
<< std::to_string(offset.illegal_value) << "'";
if (is_vector) {
err << "12:34 error: each component of the " << param.name
<< " argument must be at least " << param.min << " and at most "
<< param.max << ". " << param.name << " component "
<< expr.invalid_index << " is "
<< std::to_string(expr.values[expr.invalid_index]);
} else {
err << "12:34 error: the " << param.name << " argument must be at least "
<< param.min << " and at most " << param.max << ". " << param.name
<< " is " << std::to_string(expr.values[expr.invalid_index]);
}
EXPECT_EQ(r()->error(), err.str());
}
}
TEST_P(IntrinsicTextureSamplerValidationTest, ConstExprOfConstExpr) {
TEST_P(IntrinsicTextureConstExprArgValidationTest, GlobalConst) {
auto& p = GetParam();
auto param = std::get<0>(p);
auto offset = std::get<1>(p);
param.BuildTextureVariable(this);
param.BuildSamplerVariable(this);
auto overload = std::get<0>(p);
auto param = std::get<1>(p);
auto expr = std::get<2>(p);
auto args = param.args(this);
// Make Resolver visit the Node about to be removed
WrapInFunction(args.back());
args.pop_back();
if (NumCoordinateAxes(param.texture_dimension) == 2) {
args.push_back(Construct(Source{{12, 34}}, ty.vec2<i32>(),
Construct(ty.i32(), offset.x), offset.y));
} else if (NumCoordinateAxes(param.texture_dimension) == 3) {
args.push_back(Construct(Source{{12, 34}}, ty.vec3<i32>(), offset.x,
Construct(ty.vec2<i32>(), offset.y, offset.z)));
}
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
if (offset.is_valid) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve());
std::stringstream err;
err << "12:34 error: each offset component of '" << param.function
<< "' must be at least -8 and at most 7. found: '"
<< std::to_string(offset.illegal_value) << "'";
EXPECT_EQ(r()->error(), err.str());
}
}
// Build the global texture and sampler variables
overload.BuildTextureVariable(this);
overload.BuildSamplerVariable(this);
TEST_P(IntrinsicTextureSamplerValidationTest, EmptyVectorConstructor) {
auto& p = GetParam();
auto param = std::get<0>(p);
param.BuildTextureVariable(this);
param.BuildSamplerVariable(this);
// Build the module-scope let 'G' with the offset value
GlobalConst("G", nullptr, expr({}, *this));
auto args = param.args(this);
// Make Resolver visit the Node about to be removed
WrapInFunction(args.back());
args.pop_back();
if (NumCoordinateAxes(param.texture_dimension) == 2) {
args.push_back(Construct(Source{{12, 34}}, ty.vec2<i32>()));
} else if (NumCoordinateAxes(param.texture_dimension) == 3) {
args.push_back(Construct(Source{{12, 34}}, ty.vec3<i32>()));
}
auto args = overload.args(this);
auto*& arg_to_replace =
(param.position == Position::kFirst) ? args.front() : args.back();
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
// Make the expression to be replaced, reachable. This keeps the resolver
// happy.
WrapInFunction(arg_to_replace);
TEST_P(IntrinsicTextureSamplerValidationTest, GlobalConst) {
auto& p = GetParam();
auto param = std::get<0>(p);
auto offset = std::get<1>(p);
param.BuildTextureVariable(this);
param.BuildSamplerVariable(this);
arg_to_replace = Expr(Source{{12, 34}}, "G");
auto args = param.args(this);
// Make Resolver visit the Node about to be removed
WrapInFunction(args.back());
args.pop_back();
GlobalConst("offset_2d", ty.vec2<i32>(), vec2<i32>(offset.x, offset.y));
GlobalConst("offset_3d", ty.vec3<i32>(),
vec3<i32>(offset.x, offset.y, offset.z));
if (NumCoordinateAxes(param.texture_dimension) == 2) {
args.push_back(Expr(Source{{12, 34}}, "offset_2d"));
} else if (NumCoordinateAxes(param.texture_dimension) == 3) {
args.push_back(Expr(Source{{12, 34}}, "offset_3d"));
}
// Call the intrinsic with the constexpr argument replaced
Func("func", {}, ty.void_(), {CallStmt(Call(overload.function, args))},
{Stage(ast::PipelineStage::kFragment)});
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_FALSE(r()->Resolve());
std::stringstream err;
err << "12:34 error: '" << param.function
<< "' offset parameter must be a const_expression";
err << "12:34 error: the " << param.name
<< " argument must be a const_expression";
EXPECT_EQ(r()->error(), err.str());
}
TEST_P(IntrinsicTextureSamplerValidationTest, ScalarConst) {
auto& p = GetParam();
auto param = std::get<0>(p);
auto offset = std::get<1>(p);
param.BuildTextureVariable(this);
param.BuildSamplerVariable(this);
auto* x = Const("x", ty.i32(), Construct(ty.i32(), offset.x));
INSTANTIATE_TEST_SUITE_P(
Offset2D,
IntrinsicTextureConstExprArgValidationTest,
testing::Combine(
testing::ValuesIn(TextureCases({
ValidTextureOverload::kSample2dOffsetF32,
ValidTextureOverload::kSample2dArrayOffsetF32,
ValidTextureOverload::kSampleDepth2dOffsetF32,
ValidTextureOverload::kSampleDepth2dArrayOffsetF32,
ValidTextureOverload::kSampleBias2dOffsetF32,
ValidTextureOverload::kSampleBias2dArrayOffsetF32,
ValidTextureOverload::kSampleLevel2dOffsetF32,
ValidTextureOverload::kSampleLevel2dArrayOffsetF32,
ValidTextureOverload::kSampleLevelDepth2dOffsetF32,
ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32,
ValidTextureOverload::kSampleGrad2dOffsetF32,
ValidTextureOverload::kSampleGrad2dArrayOffsetF32,
ValidTextureOverload::kSampleCompareDepth2dOffsetF32,
ValidTextureOverload::kSampleCompareDepth2dArrayOffsetF32,
ValidTextureOverload::kSampleCompareLevelDepth2dOffsetF32,
ValidTextureOverload::kSampleCompareLevelDepth2dArrayOffsetF32,
})),
testing::Values(Parameter{"offset", Position::kLast, -8, 7}),
testing::Values(
Constexpr{Constexpr::kValid, Constexpr::Kind::kEmptyVec2},
Constexpr{Constexpr::kValid, Constexpr::Kind::kVec2, -1, 1},
Constexpr{Constexpr::kValid, Constexpr::Kind::kVec2, 7, -8},
Constexpr{0, Constexpr::Kind::kVec2, 8, 0},
Constexpr{1, Constexpr::Kind::kVec2, 0, 8},
Constexpr{0, Constexpr::Kind::kVec2, -9, 0},
Constexpr{1, Constexpr::Kind::kVec2, 0, -9},
Constexpr{0, Constexpr::Kind::kVec2, 8, 8},
Constexpr{0, Constexpr::Kind::kVec2, -9, -9})));
auto args = param.args(this);
// Make Resolver visit the Node about to be removed
WrapInFunction(args.back());
args.pop_back();
if (NumCoordinateAxes(param.texture_dimension) == 2) {
args.push_back(Construct(Source{{12, 34}}, ty.vec2<i32>(), x, offset.y));
} else if (NumCoordinateAxes(param.texture_dimension) == 3) {
args.push_back(
Construct(Source{{12, 34}}, ty.vec3<i32>(), x, offset.y, offset.z));
}
INSTANTIATE_TEST_SUITE_P(
Offset3D,
IntrinsicTextureConstExprArgValidationTest,
testing::Combine(
testing::ValuesIn(TextureCases({
ValidTextureOverload::kSample3dOffsetF32,
ValidTextureOverload::kSampleBias3dOffsetF32,
ValidTextureOverload::kSampleLevel3dOffsetF32,
ValidTextureOverload::kSampleGrad3dOffsetF32,
})),
testing::Values(Parameter{"offset", Position::kLast, -8, 7}),
testing::Values(
Constexpr{Constexpr::kValid, Constexpr::Kind::kEmptyVec3},
Constexpr{Constexpr::kValid, Constexpr::Kind::kVec3, 0, 0, 0},
Constexpr{Constexpr::kValid, Constexpr::Kind::kVec3, 7, -8, 7},
Constexpr{0, Constexpr::Kind::kVec3, 10, 0, 0},
Constexpr{1, Constexpr::Kind::kVec3, 0, 10, 0},
Constexpr{2, Constexpr::Kind::kVec3, 0, 0, 10},
Constexpr{0, Constexpr::Kind::kVec3, 10, 11, 12},
Constexpr{0, Constexpr::Kind::kVec3_Scalar_Vec2, 10, 0, 0},
Constexpr{1, Constexpr::Kind::kVec3_Scalar_Vec2, 0, 10, 0},
Constexpr{2, Constexpr::Kind::kVec3_Scalar_Vec2, 0, 0, 10},
Constexpr{0, Constexpr::Kind::kVec3_Scalar_Vec2, 10, 11, 12},
Constexpr{0, Constexpr::Kind::kVec3_Vec2_Scalar, 10, 0, 0},
Constexpr{1, Constexpr::Kind::kVec3_Vec2_Scalar, 0, 10, 0},
Constexpr{2, Constexpr::Kind::kVec3_Vec2_Scalar, 0, 0, 10},
Constexpr{0, Constexpr::Kind::kVec3_Vec2_Scalar, 10, 11, 12})));
auto* call = Call(param.function, args);
Func("func", {}, ty.void_(), {Decl(x), CallStmt(call)},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
EXPECT_FALSE(r()->Resolve());
std::stringstream err;
err << "12:34 error: '" << param.function
<< "' offset parameter must be a const_expression";
EXPECT_EQ(r()->error(), err.str());
}
INSTANTIATE_TEST_SUITE_P(
Component,
IntrinsicTextureConstExprArgValidationTest,
testing::Combine(
testing::ValuesIn(
TextureCases({ValidTextureOverload::kGather2dF32,
ValidTextureOverload::kGather2dOffsetF32,
ValidTextureOverload::kGather2dArrayF32,
ValidTextureOverload::kGather2dArrayOffsetF32,
ValidTextureOverload::kGatherCubeF32,
ValidTextureOverload::kGatherCubeArrayF32})),
testing::Values(Parameter{"component", Position::kFirst, 0, 3}),
testing::Values(
Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 0},
Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 1},
Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 2},
Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 3},
Constexpr{0, Constexpr::Kind::kScalar, 4},
Constexpr{0, Constexpr::Kind::kScalar, 123},
Constexpr{0, Constexpr::Kind::kScalar, -1})));
INSTANTIATE_TEST_SUITE_P(IntrinsicTextureSamplerValidationTest,
IntrinsicTextureSamplerValidationTest,
testing::Combine(testing::ValuesIn(ValidCases()),
testing::ValuesIn(OffsetCases())));
} // namespace TextureSamplerOffset
} // namespace texture_constexpr_args
} // namespace
} // namespace resolver

View File

@@ -45,7 +45,6 @@ class BitcastExpression;
class CallExpression;
class CallStatement;
class CaseStatement;
class ConstructorExpression;
class ForLoopStatement;
class Function;
class IdentifierExpression;

View File

@@ -1505,22 +1505,30 @@ bool Resolver::ValidateTextureIntrinsicFunction(const sem::Call* call) {
if (!intrinsic) {
return false;
}
std::string func_name = intrinsic->str();
auto& signature = intrinsic->Signature();
auto index = signature.IndexOf(sem::ParameterUsage::kOffset);
if (index > -1) {
auto check_arg_is_constexpr = [&](sem::ParameterUsage usage, int min,
int max) {
auto index = signature.IndexOf(usage);
if (index < 0) {
return true;
}
std::string name = sem::str(usage);
auto* arg = call->Arguments()[index];
if (auto values = arg->ConstantValue()) {
// Assert that the constant values are of the expected type.
if (!values.Type()->Is<sem::Vector>() ||
if (!values.Type()->IsAnyOf<sem::I32, sem::Vector>() ||
!values.ElementType()->Is<sem::I32>()) {
TINT_ICE(Resolver, diagnostics_)
<< "failed to resolve '" + func_name + "' offset parameter type";
<< "failed to resolve '" + func_name + "' " << name
<< " parameter type";
return false;
}
// Currently const_expr is restricted to literals and type constructors.
// Check that that's all we have for the offset parameter.
// Check that that's all we have for the parameter.
bool is_const_expr = true;
ast::TraverseExpressions(
arg->Declaration(), diagnostics_, [&](const ast::Expression* e) {
@@ -1531,25 +1539,37 @@ bool Resolver::ValidateTextureIntrinsicFunction(const sem::Call* call) {
return ast::TraverseAction::Stop;
});
if (is_const_expr) {
for (auto offset : values.Elements()) {
auto offset_value = offset.i32;
if (offset_value < -8 || offset_value > 7) {
AddError("each offset component of '" + func_name +
"' must be at least -8 and at most 7. "
"found: '" +
std::to_string(offset_value) + "'",
arg->Declaration()->source);
auto vector = intrinsic->Parameters()[index]->Type()->Is<sem::Vector>();
for (size_t i = 0; i < values.Elements().size(); i++) {
auto value = values.Elements()[i].i32;
if (value < min || value > max) {
if (vector) {
AddError("each component of the " + name +
" argument must be at least " + std::to_string(min) +
" and at most " + std::to_string(max) + ". " + name +
" component " + std::to_string(i) + " is " +
std::to_string(value),
arg->Declaration()->source);
} else {
AddError("the " + name + " argument must be at least " +
std::to_string(min) + " and at most " +
std::to_string(max) + ". " + name + " is " +
std::to_string(value),
arg->Declaration()->source);
}
return false;
}
}
return true;
}
}
AddError("'" + func_name + "' offset parameter must be a const_expression",
AddError("the " + name + " argument must be a const_expression",
arg->Declaration()->source);
return false;
}
return true;
};
return check_arg_is_constexpr(sem::ParameterUsage::kOffset, -8, 7) &&
check_arg_is_constexpr(sem::ParameterUsage::kComponent, 0, 3);
}
bool Resolver::ValidateFunctionCall(const sem::Call* call) {

View File

@@ -50,6 +50,8 @@ bool IsFloatClassificationIntrinsic(IntrinsicType i) {
bool IsTextureIntrinsic(IntrinsicType i) {
return IsImageQueryIntrinsic(i) || i == IntrinsicType::kTextureLoad ||
i == IntrinsicType::kTextureGather ||
i == IntrinsicType::kTextureGatherCompare ||
i == IntrinsicType::kTextureSample ||
i == IntrinsicType::kTextureSampleLevel ||
i == IntrinsicType::kTextureSampleBias ||

View File

@@ -261,6 +261,12 @@ IntrinsicType ParseIntrinsicType(const std::string& name) {
if (name == "textureDimensions") {
return IntrinsicType::kTextureDimensions;
}
if (name == "textureGather") {
return IntrinsicType::kTextureGather;
}
if (name == "textureGatherCompare") {
return IntrinsicType::kTextureGatherCompare;
}
if (name == "textureNumLayers") {
return IntrinsicType::kTextureNumLayers;
}
@@ -488,6 +494,10 @@ const char* str(IntrinsicType i) {
return "workgroupBarrier";
case IntrinsicType::kTextureDimensions:
return "textureDimensions";
case IntrinsicType::kTextureGather:
return "textureGather";
case IntrinsicType::kTextureGatherCompare:
return "textureGatherCompare";
case IntrinsicType::kTextureNumLayers:
return "textureNumLayers";
case IntrinsicType::kTextureNumLevels:

View File

@@ -111,6 +111,8 @@ enum class IntrinsicType {
kUnpack4x8unorm,
kWorkgroupBarrier,
kTextureDimensions,
kTextureGather,
kTextureGatherCompare,
kTextureNumLayers,
kTextureNumLevels,
kTextureNumSamples,

View File

@@ -35,6 +35,8 @@ const char* str(ParameterUsage usage) {
return "array_index";
case ParameterUsage::kBias:
return "bias";
case ParameterUsage::kComponent:
return "component";
case ParameterUsage::kCoords:
return "coords";
case ParameterUsage::kDdx:

View File

@@ -34,6 +34,7 @@ enum class ParameterUsage {
kNone = -1,
kArrayIndex,
kBias,
kComponent,
kCoords,
kDdx,
kDdy,

View File

@@ -480,7 +480,7 @@ bool GeneratorImpl::EmitIntrinsicCall(std::ostream& out,
const sem::Intrinsic* intrinsic) {
auto* expr = call->Declaration();
if (intrinsic->IsTexture()) {
return EmitTextureCall(out, expr, intrinsic);
return EmitTextureCall(out, call, intrinsic);
}
if (intrinsic->Type() == sem::IntrinsicType::kSelect) {
return EmitSelectCall(out, expr);
@@ -1098,11 +1098,12 @@ bool GeneratorImpl::EmitBarrierCall(std::ostream& out,
}
bool GeneratorImpl::EmitTextureCall(std::ostream& out,
const ast::CallExpression* expr,
const sem::Call* call,
const sem::Intrinsic* intrinsic) {
using Usage = sem::ParameterUsage;
auto& signature = intrinsic->Signature();
auto* expr = call->Declaration();
auto arguments = expr->args;
// Returns the argument with the given usage
@@ -1184,6 +1185,12 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
case sem::IntrinsicType::kTextureSampleLevel:
out << "textureLod(";
break;
case sem::IntrinsicType::kTextureGather:
case sem::IntrinsicType::kTextureGatherCompare:
out << (intrinsic->Signature().IndexOf(sem::ParameterUsage::kOffset) < 0
? "textureGather("
: "textureGatherOffset(");
break;
case sem::IntrinsicType::kTextureSampleGrad:
out << "textureGrad(";
break;
@@ -1232,9 +1239,9 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
}
}
for (auto usage :
{Usage::kDepthRef, Usage::kBias, Usage::kLevel, Usage::kDdx, Usage::kDdy,
Usage::kSampleIndex, Usage::kOffset, Usage::kValue}) {
for (auto usage : {Usage::kDepthRef, Usage::kBias, Usage::kLevel, Usage::kDdx,
Usage::kDdy, Usage::kSampleIndex, Usage::kOffset,
Usage::kComponent, Usage::kValue}) {
if (auto* e = arg(usage)) {
out << ", ";
if (!EmitExpression(out, e)) {

View File

@@ -165,11 +165,11 @@ class GeneratorImpl : public TextGenerator {
/// Handles generating a call to a texture function (`textureSample`,
/// `textureSampleGrad`, etc)
/// @param out the output of the expression stream
/// @param expr the call expression
/// @param call the call expression
/// @param intrinsic the semantic information for the texture intrinsic
/// @returns true if the call expression is emitted
bool EmitTextureCall(std::ostream& out,
const ast::CallExpression* expr,
const sem::Call* call,
const sem::Intrinsic* intrinsic);
/// Handles generating a call to the `select()` intrinsic
/// @param out the output of the expression stream

View File

@@ -63,6 +63,42 @@ ExpectedResult expected_texture_overload(
case ValidTextureOverload::kDimensionsStorageWO2dArray:
case ValidTextureOverload::kDimensionsStorageWO3d:
return {"imageSize"};
case ValidTextureOverload::kGather2dF32:
return R"(textureGather(tint_symbol, vec2(1.0f, 2.0f), 0))";
case ValidTextureOverload::kGather2dOffsetF32:
return R"(textureGatherOffset(tint_symbol, vec2(1.0f, 2.0f), ivec2(3, 4), 0))";
case ValidTextureOverload::kGather2dArrayF32:
return R"(textureGather(tint_symbol, vec3(1.0f, 2.0f, float(3)), 0))";
case ValidTextureOverload::kGather2dArrayOffsetF32:
return R"(textureGatherOffset(tint_symbol, vec3(1.0f, 2.0f, float(3)), ivec2(4, 5), 0))";
case ValidTextureOverload::kGatherCubeF32:
return R"(textureGather(tint_symbol, vec3(1.0f, 2.0f, 3.0f), 0))";
case ValidTextureOverload::kGatherCubeArrayF32:
return R"(textureGather(tint_symbol, vec4(1.0f, 2.0f, 3.0f, float(4)), 0))";
case ValidTextureOverload::kGatherDepth2dF32:
return R"(textureGather(tint_symbol, vec2(1.0f, 2.0f)))";
case ValidTextureOverload::kGatherDepth2dOffsetF32:
return R"(textureGatherOffset(tint_symbol, vec2(1.0f, 2.0f), ivec2(3, 4)))";
case ValidTextureOverload::kGatherDepth2dArrayF32:
return R"(textureGather(tint_symbol, vec3(1.0f, 2.0f, float(3))))";
case ValidTextureOverload::kGatherDepth2dArrayOffsetF32:
return R"(textureGatherOffset(tint_symbol, vec3(1.0f, 2.0f, float(3)), ivec2(4, 5)))";
case ValidTextureOverload::kGatherDepthCubeF32:
return R"(textureGather(tint_symbol, vec3(1.0f, 2.0f, 3.0f)))";
case ValidTextureOverload::kGatherDepthCubeArrayF32:
return R"(textureGather(tint_symbol, vec4(1.0f, 2.0f, 3.0f, float(4))))";
case ValidTextureOverload::kGatherCompareDepth2dF32:
return R"(textureGather(tint_symbol, vec2(1.0f, 2.0f), 3.0f))";
case ValidTextureOverload::kGatherCompareDepth2dOffsetF32:
return R"(textureGatherOffset(tint_symbol, vec2(1.0f, 2.0f), 3.0f, ivec2(4, 5)))";
case ValidTextureOverload::kGatherCompareDepth2dArrayF32:
return R"(textureGather(tint_symbol, vec3(1.0f, 2.0f, float(3)), 4.0f))";
case ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32:
return R"(textureGatherOffset(tint_symbol, vec3(1.0f, 2.0f, float(3)), 4.0f, ivec2(5, 6)))";
case ValidTextureOverload::kGatherCompareDepthCubeF32:
return R"(textureGather(tint_symbol, vec3(1.0f, 2.0f, 3.0f), 4.0f))";
case ValidTextureOverload::kGatherCompareDepthCubeArrayF32:
return R"(textureGather(tint_symbol, vec4(1.0f, 2.0f, 3.0f, float(4)), 5.0f))";
case ValidTextureOverload::kNumLayers2dArray:
case ValidTextureOverload::kNumLayersDepth2dArray:
case ValidTextureOverload::kNumLayersCubeArray:

View File

@@ -671,7 +671,7 @@ bool GeneratorImpl::EmitIntrinsicCall(std::ostream& out,
const sem::Intrinsic* intrinsic) {
auto* expr = call->Declaration();
if (intrinsic->IsTexture()) {
return EmitTextureCall(out, expr, intrinsic);
return EmitTextureCall(out, call, intrinsic);
}
if (intrinsic->Type() == sem::IntrinsicType::kSelect) {
return EmitSelectCall(out, expr);
@@ -1749,11 +1749,12 @@ bool GeneratorImpl::EmitBarrierCall(std::ostream& out,
}
bool GeneratorImpl::EmitTextureCall(std::ostream& out,
const ast::CallExpression* expr,
const sem::Call* call,
const sem::Intrinsic* intrinsic) {
using Usage = sem::ParameterUsage;
auto& signature = intrinsic->Signature();
auto* expr = call->Declaration();
auto arguments = expr->args;
// Returns the argument with the given usage
@@ -1987,6 +1988,30 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
pack_level_in_coords = true;
}
break;
case sem::IntrinsicType::kTextureGather:
out << ".Gather";
if (intrinsic->Parameters()[0]->Usage() ==
sem::ParameterUsage::kComponent) {
switch (call->Arguments()[0]->ConstantValue().Elements()[0].i32) {
case 0:
out << "Red";
break;
case 1:
out << "Green";
break;
case 2:
out << "Blue";
break;
case 3:
out << "Alpha";
break;
}
}
out << "(";
break;
case sem::IntrinsicType::kTextureGatherCompare:
out << ".GatherCmp(";
break;
case sem::IntrinsicType::kTextureStore:
out << "[";
break;

View File

@@ -208,11 +208,11 @@ class GeneratorImpl : public TextGenerator {
/// Handles generating a call to a texture function (`textureSample`,
/// `textureSampleGrad`, etc)
/// @param out the output of the expression stream
/// @param expr the call expression
/// @param call the call expression
/// @param intrinsic the semantic information for the texture intrinsic
/// @returns true if the call expression is emitted
bool EmitTextureCall(std::ostream& out,
const ast::CallExpression* expr,
const sem::Call* call,
const sem::Intrinsic* intrinsic);
/// Handles generating a call to the `select()` intrinsic
/// @param out the output of the expression stream

View File

@@ -135,6 +135,42 @@ ExpectedResult expected_texture_overload(
)",
"tint_tmp.xy;",
};
case ValidTextureOverload::kGather2dF32:
return R"(tint_symbol.GatherRed(tint_symbol_1, float2(1.0f, 2.0f)))";
case ValidTextureOverload::kGather2dOffsetF32:
return R"(tint_symbol.GatherRed(tint_symbol_1, float2(1.0f, 2.0f), int2(3, 4)))";
case ValidTextureOverload::kGather2dArrayF32:
return R"(tint_symbol.GatherRed(tint_symbol_1, float3(1.0f, 2.0f, float(3))))";
case ValidTextureOverload::kGather2dArrayOffsetF32:
return R"(tint_symbol.GatherRed(tint_symbol_1, float3(1.0f, 2.0f, float(3)), int2(4, 5)))";
case ValidTextureOverload::kGatherCubeF32:
return R"(tint_symbol.GatherRed(tint_symbol_1, float3(1.0f, 2.0f, 3.0f)))";
case ValidTextureOverload::kGatherCubeArrayF32:
return R"(tint_symbol.GatherRed(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4))))";
case ValidTextureOverload::kGatherDepth2dF32:
return R"(tint_symbol.Gather(tint_symbol_1, float2(1.0f, 2.0f)))";
case ValidTextureOverload::kGatherDepth2dOffsetF32:
return R"(tint_symbol.Gather(tint_symbol_1, float2(1.0f, 2.0f), int2(3, 4)))";
case ValidTextureOverload::kGatherDepth2dArrayF32:
return R"(tint_symbol.Gather(tint_symbol_1, float3(1.0f, 2.0f, float(3))))";
case ValidTextureOverload::kGatherDepth2dArrayOffsetF32:
return R"(tint_symbol.Gather(tint_symbol_1, float3(1.0f, 2.0f, float(3)), int2(4, 5)))";
case ValidTextureOverload::kGatherDepthCubeF32:
return R"(tint_symbol.Gather(tint_symbol_1, float3(1.0f, 2.0f, 3.0f)))";
case ValidTextureOverload::kGatherDepthCubeArrayF32:
return R"(tint_symbol.Gather(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4))))";
case ValidTextureOverload::kGatherCompareDepth2dF32:
return R"(tint_symbol.GatherCmp(tint_symbol_1, float2(1.0f, 2.0f), 3.0f))";
case ValidTextureOverload::kGatherCompareDepth2dOffsetF32:
return R"(tint_symbol.GatherCmp(tint_symbol_1, float2(1.0f, 2.0f), 3.0f, int2(4, 5)))";
case ValidTextureOverload::kGatherCompareDepth2dArrayF32:
return R"(tint_symbol.GatherCmp(tint_symbol_1, float3(1.0f, 2.0f, float(3)), 4.0f))";
case ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32:
return R"(tint_symbol.GatherCmp(tint_symbol_1, float3(1.0f, 2.0f, float(3)), 4.0f, int2(5, 6)))";
case ValidTextureOverload::kGatherCompareDepthCubeF32:
return R"(tint_symbol.GatherCmp(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), 4.0f))";
case ValidTextureOverload::kGatherCompareDepthCubeArrayF32:
return R"(tint_symbol.GatherCmp(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4)), 5.0f))";
case ValidTextureOverload::kNumLayers2dArray:
case ValidTextureOverload::kNumLayersDepth2dArray:
case ValidTextureOverload::kNumLayersCubeArray:

View File

@@ -582,7 +582,7 @@ bool GeneratorImpl::EmitIntrinsicCall(std::ostream& out,
return EmitAtomicCall(out, expr, intrinsic);
}
if (intrinsic->IsTexture()) {
return EmitTextureCall(out, expr, intrinsic);
return EmitTextureCall(out, call, intrinsic);
}
auto name = generate_builtin_name(intrinsic);
@@ -839,12 +839,13 @@ bool GeneratorImpl::EmitAtomicCall(std::ostream& out,
}
bool GeneratorImpl::EmitTextureCall(std::ostream& out,
const ast::CallExpression* expr,
const sem::Call* call,
const sem::Intrinsic* intrinsic) {
using Usage = sem::ParameterUsage;
auto& signature = intrinsic->Signature();
auto arguments = expr->args;
auto* expr = call->Declaration();
auto& arguments = call->Arguments();
// Returns the argument with the given usage
auto arg = [&](Usage usage) {
@@ -852,7 +853,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
return (idx >= 0) ? arguments[idx] : nullptr;
};
auto* texture = arg(Usage::kTexture);
auto* texture = arg(Usage::kTexture)->Declaration();
if (!texture) {
TINT_ICE(Writer, diagnostics_) << "missing texture arg";
return false;
@@ -908,7 +909,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
}
out << ".get_" << name << "(";
if (auto* level = arg(Usage::kLevel)) {
if (!EmitExpression(out, level)) {
if (!EmitExpression(out, level->Declaration())) {
return false;
}
}
@@ -978,6 +979,12 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
case sem::IntrinsicType::kTextureSampleCompareLevel:
out << ".sample_compare(";
break;
case sem::IntrinsicType::kTextureGather:
out << ".gather(";
break;
case sem::IntrinsicType::kTextureGatherCompare:
out << ".gather_compare(";
break;
case sem::IntrinsicType::kTextureLoad:
out << ".read(";
lod_param_is_named = false;
@@ -1003,14 +1010,12 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
{Usage::kValue, Usage::kSampler, Usage::kCoords, Usage::kArrayIndex,
Usage::kDepthRef, Usage::kSampleIndex}) {
if (auto* e = arg(usage)) {
auto* sem_e = program_->Sem().Get(e);
maybe_write_comma();
// Cast the coordinates to unsigned integers if necessary.
bool casted = false;
if (usage == Usage::kCoords &&
sem_e->Type()->UnwrapRef()->is_integer_scalar_or_vector()) {
e->Type()->UnwrapRef()->is_integer_scalar_or_vector()) {
casted = true;
switch (texture_type->dim()) {
case ast::TextureDimension::k1d:
@@ -1030,7 +1035,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
}
}
if (!EmitExpression(out, e))
if (!EmitExpression(out, e->Declaration()))
return false;
if (casted) {
@@ -1042,7 +1047,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
if (auto* bias = arg(Usage::kBias)) {
maybe_write_comma();
out << "bias(";
if (!EmitExpression(out, bias)) {
if (!EmitExpression(out, bias->Declaration())) {
return false;
}
out << ")";
@@ -1052,7 +1057,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
if (lod_param_is_named) {
out << "level(";
}
if (!EmitExpression(out, level)) {
if (!EmitExpression(out, level->Declaration())) {
return false;
}
if (lod_param_is_named) {
@@ -1087,23 +1092,59 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out,
return false;
}
}
if (!EmitExpression(out, ddx)) {
if (!EmitExpression(out, ddx->Declaration())) {
return false;
}
out << ", ";
if (!EmitExpression(out, arg(Usage::kDdy))) {
if (!EmitExpression(out, arg(Usage::kDdy)->Declaration())) {
return false;
}
out << ")";
}
bool has_offset = false;
if (auto* offset = arg(Usage::kOffset)) {
has_offset = true;
maybe_write_comma();
if (!EmitExpression(out, offset)) {
if (!EmitExpression(out, offset->Declaration())) {
return false;
}
}
if (auto* component = arg(Usage::kComponent)) {
maybe_write_comma();
if (!has_offset) {
// offset argument may need to be provided if we have a component.
switch (texture_type->dim()) {
case ast::TextureDimension::k2d:
case ast::TextureDimension::k2dArray:
out << "int2(0), ";
break;
default:
break; // Other texture dimensions don't have an offset
}
}
auto c = component->ConstantValue().Elements()[0].i32;
switch (c) {
case 0:
out << "component::x";
break;
case 1:
out << "component::y";
break;
case 2:
out << "component::z";
break;
case 3:
out << "component::w";
break;
default:
TINT_ICE(Writer, diagnostics_)
<< "invalid textureGather component: " << c;
break;
}
}
out << ")";
return true;

View File

@@ -187,11 +187,11 @@ class GeneratorImpl : public TextGenerator {
/// Handles generating a call to a texture function (`textureSample`,
/// `textureSampleGrad`, etc)
/// @param out the output of the expression stream
/// @param expr the call expression
/// @param call the call expression
/// @param intrinsic the semantic information for the texture intrinsic
/// @returns true if the call expression is emitted
bool EmitTextureCall(std::ostream& out,
const ast::CallExpression* expr,
const sem::Call* call,
const sem::Intrinsic* intrinsic);
/// Handles generating a call to the `dot()` intrinsic
/// @param out the output of the expression stream

View File

@@ -55,6 +55,42 @@ std::string expected_texture_overload(
return R"(int2(texture.get_width(1), texture.get_height(1)))";
case ValidTextureOverload::kDimensions3dLevel:
return R"(int3(texture.get_width(1), texture.get_height(1), texture.get_depth(1)))";
case ValidTextureOverload::kGather2dF32:
return R"(texture.gather(sampler, float2(1.0f, 2.0f), int2(0), component::x))";
case ValidTextureOverload::kGather2dOffsetF32:
return R"(texture.gather(sampler, float2(1.0f, 2.0f), int2(3, 4), component::x))";
case ValidTextureOverload::kGather2dArrayF32:
return R"(texture.gather(sampler, float2(1.0f, 2.0f), 3, int2(0), component::x))";
case ValidTextureOverload::kGather2dArrayOffsetF32:
return R"(texture.gather(sampler, float2(1.0f, 2.0f), 3, int2(4, 5), component::x))";
case ValidTextureOverload::kGatherCubeF32:
return R"(texture.gather(sampler, float3(1.0f, 2.0f, 3.0f), component::x))";
case ValidTextureOverload::kGatherCubeArrayF32:
return R"(texture.gather(sampler, float3(1.0f, 2.0f, 3.0f), 4, component::x))";
case ValidTextureOverload::kGatherDepth2dF32:
return R"(texture.gather(sampler, float2(1.0f, 2.0f)))";
case ValidTextureOverload::kGatherDepth2dOffsetF32:
return R"(texture.gather(sampler, float2(1.0f, 2.0f), int2(3, 4)))";
case ValidTextureOverload::kGatherDepth2dArrayF32:
return R"(texture.gather(sampler, float2(1.0f, 2.0f), 3))";
case ValidTextureOverload::kGatherDepth2dArrayOffsetF32:
return R"(texture.gather(sampler, float2(1.0f, 2.0f), 3, int2(4, 5)))";
case ValidTextureOverload::kGatherDepthCubeF32:
return R"(texture.gather(sampler, float3(1.0f, 2.0f, 3.0f)))";
case ValidTextureOverload::kGatherDepthCubeArrayF32:
return R"(texture.gather(sampler, float3(1.0f, 2.0f, 3.0f), 4))";
case ValidTextureOverload::kGatherCompareDepth2dF32:
return R"(texture.gather_compare(sampler, float2(1.0f, 2.0f), 3.0f))";
case ValidTextureOverload::kGatherCompareDepth2dOffsetF32:
return R"(texture.gather_compare(sampler, float2(1.0f, 2.0f), 3.0f, int2(4, 5)))";
case ValidTextureOverload::kGatherCompareDepth2dArrayF32:
return R"(texture.gather_compare(sampler, float2(1.0f, 2.0f), 3, 4.0f))";
case ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32:
return R"(texture.gather_compare(sampler, float2(1.0f, 2.0f), 3, 4.0f, int2(5, 6)))";
case ValidTextureOverload::kGatherCompareDepthCubeF32:
return R"(texture.gather_compare(sampler, float3(1.0f, 2.0f, 3.0f), 4.0f))";
case ValidTextureOverload::kGatherCompareDepthCubeArrayF32:
return R"(texture.gather_compare(sampler, float3(1.0f, 2.0f, 3.0f), 4, 5.0f))";
case ValidTextureOverload::kNumLayers2dArray:
case ValidTextureOverload::kNumLayersCubeArray:
case ValidTextureOverload::kNumLayersDepth2dArray:

View File

@@ -2987,6 +2987,29 @@ bool Builder::GenerateTextureIntrinsic(const sem::Call* call,
spirv_params.emplace_back(gen_arg(Usage::kValue));
break;
}
case IntrinsicType::kTextureGather: {
op = spv::Op::OpImageGather;
append_result_type_and_id_to_spirv_params();
if (!append_image_and_coords_to_spirv_params()) {
return false;
}
if (signature.IndexOf(Usage::kComponent) < 0) {
spirv_params.emplace_back(
Operand::Int(GenerateConstantIfNeeded(ScalarConstant::I32(0))));
} else {
spirv_params.emplace_back(gen_arg(Usage::kComponent));
}
break;
}
case IntrinsicType::kTextureGatherCompare: {
op = spv::Op::OpImageDrefGather;
append_result_type_and_id_to_spirv_params();
if (!append_image_and_coords_to_spirv_params()) {
return false;
}
spirv_params.emplace_back(gen_arg(Usage::kDepthRef));
break;
}
case IntrinsicType::kTextureSample: {
op = spv::Op::OpImageSampleImplicitLod;
append_result_type_and_id_to_spirv_params_for_read();

View File

@@ -574,6 +574,520 @@ OpCapability ImageQuery
)",
R"(
OpCapability ImageQuery
)"};
case ValidTextureOverload::kGather2dF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%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 = OpTypeInt 32 1
%19 = OpConstant %18 0
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%8 = OpImageGather %9 %13 %17 %19
)",
R"(
)"};
case ValidTextureOverload::kGather2dOffsetF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 0 0 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%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 = OpTypeInt 32 1
%19 = OpConstant %18 0
%20 = OpTypeVector %18 2
%21 = OpConstant %18 3
%22 = OpConstant %18 4
%23 = OpConstantComposite %20 %21 %22
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%8 = OpImageGather %9 %13 %17 %19 ConstOffset %23
)",
R"(
)"};
case ValidTextureOverload::kGather2dArrayF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%9 = OpTypeVector %4 4
%12 = OpTypeSampledImage %3
%14 = OpTypeVector %4 3
%15 = OpConstant %4 1
%16 = OpConstant %4 2
%18 = OpTypeInt 32 1
%19 = OpConstant %18 3
%21 = OpConstant %18 0
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%17 = OpConvertSToF %4 %19
%20 = OpCompositeConstruct %14 %15 %16 %17
%8 = OpImageGather %9 %13 %20 %21
)",
R"(
)"};
case ValidTextureOverload::kGather2dArrayOffsetF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 0 1 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%9 = OpTypeVector %4 4
%12 = OpTypeSampledImage %3
%14 = OpTypeVector %4 3
%15 = OpConstant %4 1
%16 = OpConstant %4 2
%18 = OpTypeInt 32 1
%19 = OpConstant %18 3
%21 = OpConstant %18 0
%22 = OpTypeVector %18 2
%23 = OpConstant %18 4
%24 = OpConstant %18 5
%25 = OpConstantComposite %22 %23 %24
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%17 = OpConvertSToF %4 %19
%20 = OpCompositeConstruct %14 %15 %16 %17
%8 = OpImageGather %9 %13 %20 %21 ConstOffset %25
)",
R"(
)"};
case ValidTextureOverload::kGatherCubeF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 Cube 0 0 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%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 = OpTypeInt 32 1
%20 = OpConstant %19 0
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%8 = OpImageGather %9 %13 %18 %20
)",
R"(
)"};
case ValidTextureOverload::kGatherCubeArrayF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 Cube 0 1 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%9 = OpTypeVector %4 4
%12 = OpTypeSampledImage %3
%14 = OpConstant %4 1
%15 = OpConstant %4 2
%16 = OpConstant %4 3
%18 = OpTypeInt 32 1
%19 = OpConstant %18 4
%21 = OpConstant %18 0
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%17 = OpConvertSToF %4 %19
%20 = OpCompositeConstruct %9 %14 %15 %16 %17
%8 = OpImageGather %9 %13 %20 %21
)",
R"(
OpCapability SampledCubeArray
)"};
case ValidTextureOverload::kGatherDepth2dF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 1 0 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%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 = OpTypeInt 32 1
%19 = OpConstant %18 0
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%8 = OpImageGather %9 %13 %17 %19
)",
R"(
)"};
case ValidTextureOverload::kGatherDepth2dOffsetF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 1 0 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%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 = OpTypeInt 32 1
%19 = OpConstant %18 0
%20 = OpTypeVector %18 2
%21 = OpConstant %18 3
%22 = OpConstant %18 4
%23 = OpConstantComposite %20 %21 %22
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%8 = OpImageGather %9 %13 %17 %19 ConstOffset %23
)",
R"(
)"};
case ValidTextureOverload::kGatherDepth2dArrayF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%9 = OpTypeVector %4 4
%12 = OpTypeSampledImage %3
%14 = OpTypeVector %4 3
%15 = OpConstant %4 1
%16 = OpConstant %4 2
%18 = OpTypeInt 32 1
%19 = OpConstant %18 3
%21 = OpConstant %18 0
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%17 = OpConvertSToF %4 %19
%20 = OpCompositeConstruct %14 %15 %16 %17
%8 = OpImageGather %9 %13 %20 %21
)",
R"(
)"};
case ValidTextureOverload::kGatherDepth2dArrayOffsetF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%9 = OpTypeVector %4 4
%12 = OpTypeSampledImage %3
%14 = OpTypeVector %4 3
%15 = OpConstant %4 1
%16 = OpConstant %4 2
%18 = OpTypeInt 32 1
%19 = OpConstant %18 3
%21 = OpConstant %18 0
%22 = OpTypeVector %18 2
%23 = OpConstant %18 4
%24 = OpConstant %18 5
%25 = OpConstantComposite %22 %23 %24
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%17 = OpConvertSToF %4 %19
%20 = OpCompositeConstruct %14 %15 %16 %17
%8 = OpImageGather %9 %13 %20 %21 ConstOffset %25
)",
R"(
)"};
case ValidTextureOverload::kGatherDepthCubeF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 Cube 1 0 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%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 = OpTypeInt 32 1
%20 = OpConstant %19 0
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%8 = OpImageGather %9 %13 %18 %20
)",
R"(
)"};
case ValidTextureOverload::kGatherDepthCubeArrayF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 Cube 1 1 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%9 = OpTypeVector %4 4
%12 = OpTypeSampledImage %3
%14 = OpConstant %4 1
%15 = OpConstant %4 2
%16 = OpConstant %4 3
%18 = OpTypeInt 32 1
%19 = OpConstant %18 4
%21 = OpConstant %18 0
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%17 = OpConvertSToF %4 %19
%20 = OpCompositeConstruct %9 %14 %15 %16 %17
%8 = OpImageGather %9 %13 %20 %21
)",
R"(
OpCapability SampledCubeArray
)"};
case ValidTextureOverload::kGatherCompareDepth2dF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 1 0 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%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 = OpImageDrefGather %9 %13 %17 %18
)",
R"(
)"};
case ValidTextureOverload::kGatherCompareDepth2dOffsetF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 1 0 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%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 = OpImageDrefGather %9 %13 %17 %18 ConstOffset %23
)",
R"(
)"};
case ValidTextureOverload::kGatherCompareDepth2dArrayF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%9 = OpTypeVector %4 4
%12 = OpTypeSampledImage %3
%14 = OpTypeVector %4 3
%15 = OpConstant %4 1
%16 = OpConstant %4 2
%18 = OpTypeInt 32 1
%19 = OpConstant %18 3
%21 = OpConstant %4 4
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%17 = OpConvertSToF %4 %19
%20 = OpCompositeConstruct %14 %15 %16 %17
%8 = OpImageDrefGather %9 %13 %20 %21
)",
R"(
)"};
case ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 2D 1 1 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%9 = OpTypeVector %4 4
%12 = OpTypeSampledImage %3
%14 = OpTypeVector %4 3
%15 = OpConstant %4 1
%16 = OpConstant %4 2
%18 = OpTypeInt 32 1
%19 = OpConstant %18 3
%21 = OpConstant %4 4
%22 = OpTypeVector %18 2
%23 = OpConstant %18 5
%24 = OpConstant %18 6
%25 = OpConstantComposite %22 %23 %24
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%17 = OpConvertSToF %4 %19
%20 = OpCompositeConstruct %14 %15 %16 %17
%8 = OpImageDrefGather %9 %13 %20 %21 ConstOffset %25
)",
R"(
)"};
case ValidTextureOverload::kGatherCompareDepthCubeF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 Cube 1 0 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%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 = OpImageDrefGather %9 %13 %18 %19
)",
R"(
)"};
case ValidTextureOverload::kGatherCompareDepthCubeArrayF32:
return {R"(
%4 = OpTypeFloat 32
%3 = OpTypeImage %4 Cube 1 1 0 1 Unknown
%2 = OpTypePointer UniformConstant %3
%1 = OpVariable %2 UniformConstant
%7 = OpTypeSampler
%6 = OpTypePointer UniformConstant %7
%5 = OpVariable %6 UniformConstant
%9 = OpTypeVector %4 4
%12 = OpTypeSampledImage %3
%14 = OpConstant %4 1
%15 = OpConstant %4 2
%16 = OpConstant %4 3
%18 = OpTypeInt 32 1
%19 = OpConstant %18 4
%21 = OpConstant %4 5
)",
R"(
%10 = OpLoad %7 %5
%11 = OpLoad %3 %1
%13 = OpSampledImage %12 %11 %10
%17 = OpConvertSToF %4 %19
%20 = OpCompositeConstruct %9 %14 %15 %16 %17
%8 = OpImageDrefGather %9 %13 %20 %21
)",
R"(
OpCapability SampledCubeArray
)"};
case ValidTextureOverload::kNumLayers2dArray:
return {R"(