spirv-reader: Start emitting sampled image builtins

- Emit (non-depth) sampler variables
- Emit sampled texture variables

- Test emission of textureSample, textureBias, textureLevel

TODO: convert unsigned offset parameter to signed. crbug.com/tint/348
TODO: support arrayed access, where we have to split out the array index
into a separate operand. crbug.com/tint/349
TODO: for explicit-lod sampling, we may have to convert coordinates to
floating point. crbug.com/tint/346

Bug: tint:109
Change-Id: I12558f99473ca234ce0d09a87fc0c2f4730497bc
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33342
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Auto-Submit: David Neto <dneto@google.com>
This commit is contained in:
David Neto 2020-11-25 19:23:17 +00:00 committed by Commit Bot service account
parent 4e91325bf0
commit eb913d3829
4 changed files with 693 additions and 43 deletions

View File

@ -350,6 +350,44 @@ ast::Intrinsic GetIntrinsic(SpvOp opcode) {
return ast::Intrinsic::kNone;
}
// @param opcode a SPIR-V opcode
// @returns true if the given instruction is an image access instruction
// whose first input operand is an OpSampledImage value.
bool IsSampledImageAccess(SpvOp opcode) {
switch (opcode) {
case SpvOpImageSampleImplicitLod:
case SpvOpImageSampleExplicitLod:
case SpvOpImageSampleDrefImplicitLod:
case SpvOpImageSampleDrefExplicitLod:
case SpvOpImageGather:
case SpvOpImageDrefGather:
case SpvOpImageQueryLod:
return true;
default:
// WGSL doesn't have *Proj* texturing.
break;
}
return false;
}
// @param opcode a SPIR-V opcode
// @returns true if the given instruction is an image access instruction
// whose first input operand is an OpImage value.
bool IsRawImageAccess(SpvOp opcode) {
switch (opcode) {
case SpvOpImageRead:
case SpvOpImageWrite:
case SpvOpImageFetch:
case SpvOpImageQuerySizeLod:
case SpvOpImageQueryLevels:
case SpvOpImageQuerySamples:
return true;
default:
break;
}
return false;
}
// @returns the merge block ID for the given basic block, or 0 if there is none.
uint32_t MergeFor(const spvtools::opt::BasicBlock& bb) {
// Get the OpSelectionMerge or OpLoopMerge instruction, if any.
@ -2597,27 +2635,37 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
// Handle combinatorial instructions.
const auto* def_info = GetDefInfo(result_id);
auto combinatorial_expr = MaybeEmitCombinatorialValue(inst);
if (combinatorial_expr.expr != nullptr) {
if (def_info == nullptr) {
return Fail() << "internal error: result ID %" << result_id
<< " is missing a def_info";
if (def_info) {
if (def_info->skip_generation) {
return true;
}
if (def_info->requires_hoisted_def || def_info->requires_named_const_def ||
def_info->num_uses != 1) {
// Generate a const definition or an assignment to a hoisted definition
// now and later use the const or variable name at the uses of this value.
return EmitConstDefOrWriteToHoistedVar(inst, combinatorial_expr);
auto combinatorial_expr = MaybeEmitCombinatorialValue(inst);
if (combinatorial_expr.expr != nullptr) {
if (def_info->requires_hoisted_def ||
def_info->requires_named_const_def || def_info->num_uses != 1) {
// Generate a const definition or an assignment to a hoisted definition
// now and later use the const or variable name at the uses of this
// value.
return EmitConstDefOrWriteToHoistedVar(inst, combinatorial_expr);
}
// It is harmless to defer emitting the expression until it's used.
// Any supporting statements have already been emitted.
singly_used_values_.insert(std::make_pair(result_id, combinatorial_expr));
return success();
}
// It is harmless to defer emitting the expression until it's used.
// Any supporting statements have already been emitted.
singly_used_values_.insert(std::make_pair(result_id, combinatorial_expr));
return success();
}
if (failed()) {
return false;
}
if (IsSampledImageAccess(inst.opcode())) {
return EmitSampledImageAccess(inst);
}
if (IsRawImageAccess(inst.opcode())) {
return Fail() << "raw image access is not implemented yet:"
<< inst.PrettyPrint();
}
switch (inst.opcode()) {
case SpvOpNop:
return true;
@ -3195,34 +3243,45 @@ bool FunctionEmitter::RegisterLocallyDefinedValues() {
}
def_info_[result_id] = std::make_unique<DefInfo>(inst, block_pos, index);
index++;
auto& info = def_info_[result_id];
// Determine storage class for pointer values. Do this in order because
// we might rely on the storage class for a previously-visited definition.
// Logical pointers can't be transmitted through OpPhi, so remaining
// pointer definitions are SSA values, and their definitions must be
// visited before their uses.
auto& storage_class = def_info_[result_id]->storage_class;
const auto* type = type_mgr_->GetType(inst.type_id());
if (type && type->AsPointer()) {
const auto* ast_type = parser_impl_.ConvertType(inst.type_id());
if (ast_type && ast_type->AsPointer()) {
storage_class = ast_type->AsPointer()->storage_class();
if (type) {
if (type->AsPointer()) {
const auto* ast_type = parser_impl_.ConvertType(inst.type_id());
if (ast_type && ast_type->AsPointer()) {
info->storage_class = ast_type->AsPointer()->storage_class();
}
switch (inst.opcode()) {
case SpvOpUndef:
case SpvOpVariable:
// Keep the default decision based on the result type.
break;
case SpvOpAccessChain:
case SpvOpCopyObject:
// Inherit from the first operand. We need this so we can pick up
// a remapped storage buffer.
info->storage_class = GetStorageClassForPointerValue(
inst.GetSingleWordInOperand(0));
break;
default:
return Fail()
<< "pointer defined in function from unknown opcode: "
<< inst.PrettyPrint();
}
if (info->storage_class == ast::StorageClass::kUniformConstant) {
info->skip_generation = true;
}
}
switch (inst.opcode()) {
case SpvOpUndef:
case SpvOpVariable:
// Keep the default decision based on the result type.
break;
case SpvOpAccessChain:
case SpvOpCopyObject:
// Inherit from the first operand. We need this so we can pick up
// a remapped storage buffer.
storage_class =
GetStorageClassForPointerValue(inst.GetSingleWordInOperand(0));
break;
default:
return Fail() << "pointer defined in function from unknown opcode: "
<< inst.PrettyPrint();
if (type->AsSampler() || type->AsImage() || type->AsSampledImage()) {
// Defer code generation until the instruction that actually acts on
// the image.
info->skip_generation = true;
}
}
}
@ -3563,6 +3622,99 @@ void FunctionEmitter::ApplySourceForInstruction(
}
}
bool FunctionEmitter::EmitSampledImageAccess(
const spvtools::opt::Instruction& inst) {
auto* result_type = parser_impl_.ConvertType(inst.type_id());
// The sampled image operand is always first.
const auto sampled_image_id = inst.GetSingleWordInOperand(0);
const auto* sampler =
parser_impl_.GetMemoryObjectDeclarationForHandle(sampled_image_id, false);
const auto* image =
parser_impl_.GetMemoryObjectDeclarationForHandle(sampled_image_id, true);
if (!sampler) {
return Fail() << "interal error: couldn't find sampler for "
<< inst.PrettyPrint();
}
if (!image) {
return Fail() << "interal error: couldn't find image for "
<< inst.PrettyPrint();
}
ast::ExpressionList params;
params.push_back(
create<ast::IdentifierExpression>(namer_.Name(image->result_id())));
params.push_back(
create<ast::IdentifierExpression>(namer_.Name(sampler->result_id())));
// Push the coordinates operand.
// TODO(dneto): For explicit-Lod variations, we may have to convert from
// integral coordinates to floating point coordinates.
// TODO(dneto): For arrayed access, split off the array layer.
params.push_back(MakeOperand(inst, 1).expr);
uint32_t arg_index = 2;
std::string builtin_name;
switch (inst.opcode()) {
case SpvOpImageSampleImplicitLod:
case SpvOpImageSampleExplicitLod:
builtin_name = "textureSample";
break;
case SpvOpImageGather:
case SpvOpImageDrefGather:
return Fail() << " image gather is not yet supported";
case SpvOpImageQueryLod:
return Fail() << " image query Lod is not yet supported";
default:
return Fail() << "internal error: sampled image access";
}
// Loop over the image operands, looking for extra operands to the builtin.
// Except we uroll the loop.
const auto num_args = inst.NumInOperands();
uint32_t image_operands_mask = 0;
if (arg_index < num_args) {
image_operands_mask = inst.GetSingleWordInOperand(arg_index);
arg_index++;
}
if (arg_index < num_args &&
(image_operands_mask & SpvImageOperandsBiasMask)) {
builtin_name += "Bias";
params.push_back(MakeOperand(inst, arg_index).expr);
image_operands_mask ^= SpvImageOperandsBiasMask;
arg_index++;
}
if (arg_index < num_args && (image_operands_mask & SpvImageOperandsLodMask)) {
builtin_name += "Level";
params.push_back(MakeOperand(inst, arg_index).expr);
image_operands_mask ^= SpvImageOperandsLodMask;
arg_index++;
}
if (arg_index + 1 < num_args &&
(image_operands_mask & SpvImageOperandsGradMask)) {
builtin_name += "Grad";
params.push_back(MakeOperand(inst, arg_index).expr);
params.push_back(MakeOperand(inst, arg_index + 1).expr);
image_operands_mask ^= SpvImageOperandsGradMask;
arg_index += 2;
}
if (arg_index < num_args &&
(image_operands_mask & SpvImageOperandsConstOffsetMask)) {
params.push_back(MakeOperand(inst, arg_index).expr);
image_operands_mask ^= SpvImageOperandsConstOffsetMask;
arg_index++;
}
if (image_operands_mask) {
return Fail() << "unsupported image operands (" << image_operands_mask
<< "): " << inst.PrettyPrint();
}
auto* ident = create<ast::IdentifierExpression>(builtin_name);
auto* call_expr = create<ast::CallExpression>(ident, std::move(params));
return EmitConstDefOrWriteToHoistedVar(inst, {result_type, call_expr});
}
} // namespace spirv
} // namespace reader
} // namespace tint

View File

@ -259,6 +259,17 @@ struct DefInfo {
/// that needs to be remapped to StorageBuffer storage class.
/// This is kNone for non-pointers.
ast::StorageClass storage_class = ast::StorageClass::kNone;
/// Should this instruction be skipped when generating code?
/// This is true for any intermediate value which is an sampler, image,
/// or sampled image, or any pointer to such object. Code is generated
/// for those objects only when emitting the image instructions that access
/// the image (read, write, sample, gather, fetch, or query). For example,
/// when encountering an OpImageSampleExplicitLod, a call to the
/// textureSampleLevel builtin function will be emitted, and the call will
/// directly reference the underlying texture and sampler (variable or
/// function parameter).
bool skip_generation = false;
};
inline std::ostream& operator<<(std::ostream& o, const DefInfo& di) {
@ -693,6 +704,12 @@ class FunctionEmitter {
/// @returns an expression
TypedExpression MakeIntrinsicCall(const spvtools::opt::Instruction& inst);
/// Emits a texture builtin function call for a SPIR-V instruction that
/// accesses a sampled image.
/// @param inst the SPIR-V instruction
/// @returns an expression
bool EmitSampledImageAccess(const spvtools::opt::Instruction& inst);
/// Returns an expression for an OpSelect, if its operands are scalars
/// or vectors. These translate directly to WGSL select. Otherwise, return
/// an expression with a null owned expression

View File

@ -1102,16 +1102,26 @@ bool ParserImpl::EmitModuleScopeVariables() {
if (!success_) {
return false;
}
auto* ast_type = id_to_type_[type_id];
if (ast_type == nullptr) {
return Fail() << "internal error: failed to register Tint AST type for "
"SPIR-V type with ID: "
<< var.type_id();
}
if (!ast_type->IsPointer()) {
return Fail() << "variable with ID " << var.result_id()
<< " has non-pointer type " << var.type_id();
ast::type::Type* ast_type = nullptr;
if (spirv_storage_class == SpvStorageClassUniformConstant) {
// These are opaque handles: samplers or textures
ast_type = GetTypeForHandleVar(var);
if (!ast_type) {
return false;
}
} else {
ast_type = id_to_type_[type_id];
if (ast_type == nullptr) {
return Fail() << "internal error: failed to register Tint AST type for "
"SPIR-V type with ID: "
<< var.type_id();
}
if (!ast_type->IsPointer()) {
return Fail() << "variable with ID " << var.result_id()
<< " has non-pointer type " << var.type_id();
}
}
auto* ast_store_type = ast_type->AsPointer()->type();
auto ast_storage_class = ast_type->AsPointer()->storage_class();
auto* ast_var =

View File

@ -48,10 +48,13 @@ std::string CommonTypes() {
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%int_3 = OpConstant %uint 3
%int_4 = OpConstant %uint 4
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%uint_100 = OpConstant %uint 100
%v2int = OpTypeVector %int 2
%v2uint = OpTypeVector %uint 2
%v4uint = OpTypeVector %uint 4
%v4int = OpTypeVector %int 4
@ -60,11 +63,13 @@ std::string CommonTypes() {
%v4float = OpTypeVector %float 4
%float_null = OpConstantNull %float
%float_7 = OpConstant %float 7
%v2float_null = OpConstantNull %v2float
%v3float_null = OpConstantNull %v3float
%v4float_null = OpConstantNull %v4float
%depth = OpConstant %float 0.2
%offsets2d = OpConstantComposite %v2int %int_3 %int_4
; Define types for all sampler and texture types that can map to WGSL,
; modulo texel formats for storage textures. For now, we limit
@ -1054,6 +1059,472 @@ INSTANTIATE_TEST_SUITE_P(
"%uint %im",
"Usage(Texture( is_sampled ms ))"}));
// Test emission of handle variables.
struct DeclSampledImageCase {
std::string inst; // The provoking image access instruction.
std::string var_decl; // WGSL variable declaration
std::string texture_builtin; // WGSL texture usage.
};
inline std::ostream& operator<<(std::ostream& out,
const DeclSampledImageCase& c) {
out << "UsageSampledImageCase(" << c.inst << "\n"
<< c.var_decl << "\n"
<< c.texture_builtin << ")";
return out;
}
using SpvParserTest_DeclHandle_SampledImage =
SpvParserTestBase<::testing::TestWithParam<DeclSampledImageCase>>;
TEST_P(SpvParserTest_DeclHandle_SampledImage, Variable) {
const auto assembly = Preamble() + R"(
OpDecorate %10 DescriptorSet 0
OpDecorate %10 Binding 0
OpDecorate %20 DescriptorSet 2
OpDecorate %20 Binding 1
)" + CommonTypes() + R"(
%si_ty = OpTypeSampledImage %f_texture_2d
%coords = OpConstantNull %v2float
%10 = OpVariable %ptr_sampler UniformConstant
%20 = OpVariable %ptr_f_texture_2d UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%sam = OpLoad %sampler %10
%im = OpLoad %f_texture_2d %20
%sampled_image = OpSampledImage %si_ty %im %sam
)" + GetParam().inst + R"(
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty()) << p->error();
const auto module = p->module().to_str();
EXPECT_THAT(module, HasSubstr(GetParam().var_decl))
<< "DECLARATIONS ARE BAD " << module;
EXPECT_THAT(module, HasSubstr(GetParam().texture_builtin))
<< "TEXTURE BUILTIN IS BAD " << module << assembly;
}
// TODO(dneto): Test variable declaration and texture builtins provoked by
// use of an image access instruction inside helper function.
TEST_P(SpvParserTest_RegisterHandleUsage_SampledImage, DISABLED_FunctionParam) {
const auto assembly = Preamble() + CommonTypes() + R"(
%f_ty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_2d
%si_ty = OpTypeSampledImage %f_texture_2d
%coords = OpConstantNull %v2float
%component = OpConstant %uint 1
%10 = OpVariable %ptr_sampler UniformConstant
%20 = OpVariable %ptr_f_texture_2d UniformConstant
%func = OpFunction %void None %f_ty
%110 = OpFunctionParameter %ptr_sampler
%120 = OpFunctionParameter %ptr_f_texture_2d
%func_entry = OpLabel
%sam = OpLoad %sampler %110
%im = OpLoad %f_texture_2d %120
%sampled_image = OpSampledImage %si_ty %im %sam
)" + GetParam().inst + R"(
OpReturn
OpFunctionEnd
%main = OpFunction %void None %voidfn
%entry = OpLabel
%foo = OpFunctionCall %void %func %10 %20
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildInternalModule()) << p->error() << assembly << std::endl;
EXPECT_TRUE(p->RegisterHandleUsage()) << p->error() << assembly << std::endl;
EXPECT_TRUE(p->error().empty()) << p->error() << assembly << std::endl;
Usage su = p->GetHandleUsage(10);
Usage iu = p->GetHandleUsage(20);
EXPECT_THAT(su.to_str(), Eq(GetParam().expected_sampler_usage));
EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
}
INSTANTIATE_TEST_SUITE_P(
DISABLED_ImageGather,
SpvParserTest_DeclHandle_SampledImage,
::testing::ValuesIn(std::vector<DeclSampledImageCase>{
// TODO(dneto): OpImageGather
// TODO(dneto): OpImageGather with ConstOffset (signed and unsigned)
// TODO(dneto): OpImageGather with Offset (signed and unsigned)
// TODO(dneto): OpImageGather with Offsets (signed and unsigned)
}));
INSTANTIATE_TEST_SUITE_P(
DISABLED_ImageDrefGather,
SpvParserTest_DeclHandle_SampledImage,
::testing::ValuesIn(std::vector<DeclSampledImageCase>{
// TODO(dneto): OpImageDrefGather
// TODO(dneto): OpImageDrefGather with ConstOffset (signed and
// unsigned)
// TODO(dneto): OpImageDrefGather with Offset (signed and unsigned)
// TODO(dneto): OpImageDrefGather with Offsets (signed and unsigned)
}));
INSTANTIATE_TEST_SUITE_P(
ImageSampleImplicitLod,
SpvParserTest_DeclHandle_SampledImage,
::testing::Values(
// OpImageSampleImplicitLod
DeclSampledImageCase{"%result = OpImageSampleImplicitLod "
"%v4float %sampled_image %coords",
R"(
DecoratedVariable{
Decorations{
SetDecoration{0}
BindingDecoration{0}
}
x_10
uniform_constant
__sampler_sampler
}
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__sampled_texture_2d__f32
})",
R"(
Call[not set]{
Identifier[not set]{textureSample}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
TypeConstructor[not set]{
__vec_2__f32
ScalarConstructor[not set]{0.000000}
ScalarConstructor[not set]{0.000000}
}
)
})"},
// OpImageSampleImplicitLod with ConstOffset
DeclSampledImageCase{
"%result = OpImageSampleImplicitLod "
"%v4float %sampled_image %coords ConstOffset %offsets2d",
R"(
DecoratedVariable{
Decorations{
SetDecoration{0}
BindingDecoration{0}
}
x_10
uniform_constant
__sampler_sampler
}
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__sampled_texture_2d__f32
})",
R"(
Call[not set]{
Identifier[not set]{textureSample}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
TypeConstructor[not set]{
__vec_2__f32
ScalarConstructor[not set]{0.000000}
ScalarConstructor[not set]{0.000000}
}
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{3}
ScalarConstructor[not set]{4}
}
)
})"},
// OpImageSampleImplicitLod with Bias
DeclSampledImageCase{"%result = OpImageSampleImplicitLod "
"%v4float %sampled_image %coords Bias %float_7",
R"(
DecoratedVariable{
Decorations{
SetDecoration{0}
BindingDecoration{0}
}
x_10
uniform_constant
__sampler_sampler
}
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__sampled_texture_2d__f32
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleBias}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
TypeConstructor[not set]{
__vec_2__f32
ScalarConstructor[not set]{0.000000}
ScalarConstructor[not set]{0.000000}
}
ScalarConstructor[not set]{7.000000}
)
})"},
// OpImageSampleImplicitLod with Bias and ConstOffset
// TODO(dneto): OpImageSampleImplicitLod with Bias and unsigned
// ConstOffset
DeclSampledImageCase{"%result = OpImageSampleImplicitLod "
"%v4float %sampled_image %coords Bias|ConstOffset "
"%float_7 %offsets2d",
R"(
DecoratedVariable{
Decorations{
SetDecoration{0}
BindingDecoration{0}
}
x_10
uniform_constant
__sampler_sampler
}
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__sampled_texture_2d__f32
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleBias}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
TypeConstructor[not set]{
__vec_2__f32
ScalarConstructor[not set]{0.000000}
ScalarConstructor[not set]{0.000000}
}
ScalarConstructor[not set]{7.000000}
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{3}
ScalarConstructor[not set]{4}
}
)
})"}
));
INSTANTIATE_TEST_SUITE_P(
DISABLED_ImageSampleDrefImplicitLod,
SpvParserTest_DeclHandle_SampledImage,
::testing::ValuesIn(std::vector<DeclSampledImageCase>{
// TODO(dneto): ImageSampleDrefImplicitLod
// TODO(dneto): ImageSampleDrefImplicitLod with ConstOffset (signed and
// unsigned)
// TODO(dneto): ImageSampleDrefImplicitLod with Bias
// TODO(dneto): ImageSampleDrefImplicitLod with Biase and ConstOffset
// (signed and unsigned)
}));
INSTANTIATE_TEST_SUITE_P(
DisabledimageSampleExplicitLod,
SpvParserTest_DeclHandle_SampledImage,
::testing::Values(
// OpImageSampleExplicitLod - using Lod
DeclSampledImageCase{"%result = OpImageSampleExplicitLod "
"%v4float %sampled_image %coords Lod %float_null",
R"(
DecoratedVariable{
Decorations{
SetDecoration{0}
BindingDecoration{0}
}
x_10
uniform_constant
__sampler_sampler
}
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__sampled_texture_2d__f32
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleLevel}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
TypeConstructor[not set]{
__vec_2__f32
ScalarConstructor[not set]{0.000000}
ScalarConstructor[not set]{0.000000}
}
ScalarConstructor[not set]{0.000000}
)
})"},
// OpImageSampleExplicitLod - using Lod and ConstOffset
// TODO(dneto) OpImageSampleExplicitLod - using Lod and unsigned
// ConstOffset
DeclSampledImageCase{"%result = OpImageSampleExplicitLod "
"%v4float %sampled_image %coords Lod|ConstOffset "
"%float_null %offsets2d",
R"(
DecoratedVariable{
Decorations{
SetDecoration{0}
BindingDecoration{0}
}
x_10
uniform_constant
__sampler_sampler
}
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__sampled_texture_2d__f32
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleLevel}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
TypeConstructor[not set]{
__vec_2__f32
ScalarConstructor[not set]{0.000000}
ScalarConstructor[not set]{0.000000}
}
ScalarConstructor[not set]{0.000000}
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{3}
ScalarConstructor[not set]{4}
}
)
})"},
// OpImageSampleExplicitLod - using Grad
DeclSampledImageCase{
"%result = OpImageSampleExplicitLod "
"%v4float %sampled_image %coords Grad %float_7 %float_null",
R"(
DecoratedVariable{
Decorations{
SetDecoration{0}
BindingDecoration{0}
}
x_10
uniform_constant
__sampler_sampler
}
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__sampled_texture_2d__f32
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleGrad}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
TypeConstructor[not set]{
__vec_2__f32
ScalarConstructor[not set]{0.000000}
ScalarConstructor[not set]{0.000000}
}
ScalarConstructor[not set]{7.000000}
ScalarConstructor[not set]{0.000000}
)
})"},
// OpImageSampleExplicitLod - using Grad and ConstOffset
// TODO(dneto): OpImageSampleExplicitLod - using Grad and unsigned
// ConstOffset
DeclSampledImageCase{"%result = OpImageSampleExplicitLod "
"%v4float %sampled_image %coords Grad|ConstOffset "
"%float_7 %float_null %offsets2d",
R"(
DecoratedVariable{
Decorations{
SetDecoration{0}
BindingDecoration{0}
}
x_10
uniform_constant
__sampler_sampler
}
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__sampled_texture_2d__f32
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleGrad}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
TypeConstructor[not set]{
__vec_2__f32
ScalarConstructor[not set]{0.000000}
ScalarConstructor[not set]{0.000000}
}
ScalarConstructor[not set]{7.000000}
ScalarConstructor[not set]{0.000000}
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{3}
ScalarConstructor[not set]{4}
}
)
})"}));
} // namespace
} // namespace spirv
} // namespace reader