diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 57192d5210..82768136b7 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -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(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(namer_.Name(image->result_id()))); + params.push_back( + create(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(builtin_name); + auto* call_expr = create(ident, std::move(params)); + return EmitConstDefOrWriteToHoistedVar(inst, {result_type, call_expr}); +} + } // namespace spirv } // namespace reader } // namespace tint diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h index 3211dc92b0..cfebe80b01 100644 --- a/src/reader/spirv/function.h +++ b/src/reader/spirv/function.h @@ -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 diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc index 4c7e867216..4ea1e21e6e 100644 --- a/src/reader/spirv/parser_impl.cc +++ b/src/reader/spirv/parser_impl.cc @@ -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 = diff --git a/src/reader/spirv/parser_impl_handle_test.cc b/src/reader/spirv/parser_impl_handle_test.cc index 1d28d1d896..06109be16a 100644 --- a/src/reader/spirv/parser_impl_handle_test.cc +++ b/src/reader/spirv/parser_impl_handle_test.cc @@ -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>; + +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{ + // 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{ + // 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{ + // 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