spirv-reader: support remaining interpolation decorations
NoPerspective interpolation maps to 'linear' Centroid maps to 'centroid' Sample maps to 'sample' Otherwise, allow 'center' to be defaulted. Fixed: tint:935 Change-Id: I0b040da0c57d2a363f9dc9474c1ac889e0fe2278 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56840 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: James Price <jrprice@google.com> Commit-Queue: James Price <jrprice@google.com> Auto-Submit: David Neto <dneto@google.com>
This commit is contained in:
parent
71198438ac
commit
e087a95706
|
@ -949,6 +949,7 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
|||
tip_type = ref_type->type;
|
||||
}
|
||||
|
||||
// Recursively flatten matrices, arrays, and structures.
|
||||
if (auto* matrix_type = tip_type->As<Matrix>()) {
|
||||
index_prefix.push_back(0);
|
||||
const auto num_columns = static_cast<int>(matrix_type->columns);
|
||||
|
@ -981,13 +982,21 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
|||
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
|
||||
index_prefix.back() = i;
|
||||
auto* location = parser_impl_.GetMemberLocation(*struct_type, i);
|
||||
auto* saved_location = SetLocation(decos, location);
|
||||
if (!EmitPipelineInput(var_name, var_type, decos, index_prefix,
|
||||
SetLocation(decos, location);
|
||||
ast::DecorationList member_decos(*decos);
|
||||
if (!parser_impl_.ConvertInterpolationDecorations(
|
||||
struct_type,
|
||||
parser_impl_.GetMemberInterpolationDecorations(*struct_type, i),
|
||||
&member_decos)) {
|
||||
return false;
|
||||
}
|
||||
if (!EmitPipelineInput(var_name, var_type, &member_decos, index_prefix,
|
||||
members[i], forced_param_type, params,
|
||||
statements)) {
|
||||
return false;
|
||||
}
|
||||
SetLocation(decos, saved_location);
|
||||
// Copy the location as updated by nested expansion of the member.
|
||||
SetLocation(decos, GetLocation(member_decos));
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
@ -999,7 +1008,7 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
|
|||
const auto param_name = namer_.MakeDerivedName(var_name + "_param");
|
||||
// Create the parameter.
|
||||
// TODO(dneto): Note: If the parameter has non-location decorations,
|
||||
// then those decoration AST nodes will be reused between multiple elements
|
||||
// then those decoration AST nodes will be reused between multiple elements
|
||||
// of a matrix, array, or structure. Normally that's disallowed but currently
|
||||
// the SPIR-V reader will make duplicates when the entire AST is cloned
|
||||
// at the top level of the SPIR-V reader flow. Consider rewriting this
|
||||
|
@ -1062,7 +1071,7 @@ ast::Decoration* FunctionEmitter::SetLocation(ast::DecorationList* decos,
|
|||
}
|
||||
for (auto*& deco : *decos) {
|
||||
if (deco->Is<ast::LocationDecoration>()) {
|
||||
// Replace this location decoration with a new one with one higher index.
|
||||
// Replace this location decoration with the replacement.
|
||||
// The old one doesn't leak because it's kept in the builder's AST node
|
||||
// list.
|
||||
ast::Decoration* result = deco;
|
||||
|
@ -1075,6 +1084,16 @@ ast::Decoration* FunctionEmitter::SetLocation(ast::DecorationList* decos,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
ast::Decoration* FunctionEmitter::GetLocation(
|
||||
const ast::DecorationList& decos) {
|
||||
for (auto* const& deco : decos) {
|
||||
if (deco->Is<ast::LocationDecoration>()) {
|
||||
return deco;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
||||
const Type* var_type,
|
||||
ast::DecorationList* decos,
|
||||
|
@ -1083,12 +1102,12 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
|||
const Type* forced_member_type,
|
||||
ast::StructMemberList* return_members,
|
||||
ast::ExpressionList* return_exprs) {
|
||||
// TODO(dneto): Handle structs where the locations are annotated on members.
|
||||
tip_type = tip_type->UnwrapAlias();
|
||||
if (auto* ref_type = tip_type->As<Reference>()) {
|
||||
tip_type = ref_type->type;
|
||||
}
|
||||
|
||||
// Recursively flatten matrices, arrays, and structures.
|
||||
if (auto* matrix_type = tip_type->As<Matrix>()) {
|
||||
index_prefix.push_back(0);
|
||||
const auto num_columns = static_cast<int>(matrix_type->columns);
|
||||
|
@ -1123,13 +1142,21 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
|||
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
|
||||
index_prefix.back() = i;
|
||||
auto* location = parser_impl_.GetMemberLocation(*struct_type, i);
|
||||
auto* saved_location = SetLocation(decos, location);
|
||||
if (!EmitPipelineOutput(var_name, var_type, decos, index_prefix,
|
||||
SetLocation(decos, location);
|
||||
ast::DecorationList member_decos(*decos);
|
||||
if (!parser_impl_.ConvertInterpolationDecorations(
|
||||
struct_type,
|
||||
parser_impl_.GetMemberInterpolationDecorations(*struct_type, i),
|
||||
&member_decos)) {
|
||||
return false;
|
||||
}
|
||||
if (!EmitPipelineOutput(var_name, var_type, &member_decos, index_prefix,
|
||||
members[i], forced_member_type, return_members,
|
||||
return_exprs)) {
|
||||
return false;
|
||||
}
|
||||
SetLocation(decos, saved_location);
|
||||
// Copy the location as updated by nested expansion of the member.
|
||||
SetLocation(decos, GetLocation(member_decos));
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
|
|
@ -479,10 +479,16 @@ class FunctionEmitter {
|
|||
/// Assumes the list contains at most one Location decoration.
|
||||
/// @param decos the decoration list to modify
|
||||
/// @param replacement the location decoration to place into the list
|
||||
/// @returns the location decoration that was replaced, if one was replaced.
|
||||
/// @returns the location decoration that was replaced, if one was replaced,
|
||||
/// or null otherwise.
|
||||
ast::Decoration* SetLocation(ast::DecorationList* decos,
|
||||
ast::Decoration* replacement);
|
||||
|
||||
/// Returns the Location dcoration, if it exists.
|
||||
/// @param decos the list of decorations to search
|
||||
/// @returns the Location decoration, or nullptr if it doesn't exist
|
||||
ast::Decoration* GetLocation(const ast::DecorationList& decos);
|
||||
|
||||
/// Create an ast::BlockStatement representing the body of the function.
|
||||
/// This creates the statement stack, which is non-empty for the lifetime
|
||||
/// of the function.
|
||||
|
|
|
@ -233,6 +233,24 @@ bool AssumesResultSignednessMatchesFirstOperand(GLSLstd450 extended_opcode) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// @param a SPIR-V decoration
|
||||
// @return true when the given decoration is an interpolation decoration.
|
||||
bool IsInterpolationDecoration(const Decoration& deco) {
|
||||
if (deco.size() < 1) {
|
||||
return false;
|
||||
}
|
||||
switch (deco[0]) {
|
||||
case SpvDecorationFlat:
|
||||
case SpvDecorationNoPerspective:
|
||||
case SpvDecorationCentroid:
|
||||
case SpvDecorationSample:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TypedExpression::TypedExpression() = default;
|
||||
|
@ -1104,6 +1122,9 @@ const Type* ParserImpl::ConvertType(
|
|||
break;
|
||||
case SpvDecorationLocation:
|
||||
case SpvDecorationFlat:
|
||||
case SpvDecorationNoPerspective:
|
||||
case SpvDecorationCentroid:
|
||||
case SpvDecorationSample:
|
||||
// IO decorations are handled when emitting the entry point.
|
||||
break;
|
||||
default: {
|
||||
|
@ -1571,6 +1592,7 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
|
|||
const Type** store_type,
|
||||
ast::DecorationList* decorations,
|
||||
bool transfer_pipeline_io) {
|
||||
DecorationList interpolation_decorations;
|
||||
for (auto& deco : GetDecorationsFor(id)) {
|
||||
if (deco.empty()) {
|
||||
return Fail() << "malformed decoration on ID " << id << ": it is empty";
|
||||
|
@ -1643,16 +1665,8 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
|
|||
create<ast::LocationDecoration>(Source{}, deco[1]));
|
||||
}
|
||||
}
|
||||
if (deco[0] == SpvDecorationFlat) {
|
||||
if (transfer_pipeline_io) {
|
||||
// In WGSL, integral types are always flat, and so the decoration
|
||||
// is never specified.
|
||||
if (!(*store_type)->IsIntegerScalarOrVector()) {
|
||||
decorations->emplace_back(create<ast::InterpolateDecoration>(
|
||||
Source{}, ast::InterpolationType::kFlat,
|
||||
ast::InterpolationSampling::kNone));
|
||||
}
|
||||
}
|
||||
if (transfer_pipeline_io && IsInterpolationDecoration(deco)) {
|
||||
interpolation_decorations.push_back(deco);
|
||||
}
|
||||
if (deco[0] == SpvDecorationDescriptorSet) {
|
||||
if (deco.size() == 1) {
|
||||
|
@ -1671,6 +1685,98 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
|
|||
create<ast::BindingDecoration>(Source{}, deco[1]));
|
||||
}
|
||||
}
|
||||
|
||||
if (transfer_pipeline_io) {
|
||||
if (!ConvertInterpolationDecorations(*store_type, interpolation_decorations,
|
||||
decorations)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
DecorationList ParserImpl::GetMemberInterpolationDecorations(
|
||||
const Struct& struct_type,
|
||||
int member_index) {
|
||||
// Yes, I could have used std::copy_if or std::copy_if.
|
||||
DecorationList result;
|
||||
for (const auto& deco : GetDecorationsForMember(
|
||||
struct_id_for_symbol_[struct_type.name], member_index)) {
|
||||
if (IsInterpolationDecoration(deco)) {
|
||||
result.emplace_back(deco);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ParserImpl::ConvertInterpolationDecorations(
|
||||
const Type* store_type,
|
||||
const DecorationList& decorations,
|
||||
ast::DecorationList* ast_decos) {
|
||||
bool has_interpolate_no_perspective = false;
|
||||
bool has_interpolate_sampling_centroid = false;
|
||||
bool has_interpolate_sampling_sample = false;
|
||||
|
||||
for (const auto& deco : decorations) {
|
||||
if (deco[0] == SpvDecorationFlat) {
|
||||
// In WGSL, integral types are always flat, and so the decoration
|
||||
// is never specified.
|
||||
if (!store_type->IsIntegerScalarOrVector()) {
|
||||
ast_decos->emplace_back(create<ast::InterpolateDecoration>(
|
||||
Source{}, ast::InterpolationType::kFlat,
|
||||
ast::InterpolationSampling::kNone));
|
||||
// Only one interpolate attribute is allowed.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (deco[0] == SpvDecorationNoPerspective) {
|
||||
if (store_type->IsIntegerScalarOrVector()) {
|
||||
// This doesn't capture the array or struct case.
|
||||
return Fail() << "NoPerspective is invalid on integral IO";
|
||||
}
|
||||
has_interpolate_no_perspective = true;
|
||||
}
|
||||
if (deco[0] == SpvDecorationCentroid) {
|
||||
if (store_type->IsIntegerScalarOrVector()) {
|
||||
// This doesn't capture the array or struct case.
|
||||
return Fail()
|
||||
<< "Centroid interpolation sampling is invalid on integral IO";
|
||||
}
|
||||
has_interpolate_sampling_centroid = true;
|
||||
}
|
||||
if (deco[0] == SpvDecorationSample) {
|
||||
if (store_type->IsIntegerScalarOrVector()) {
|
||||
// This doesn't capture the array or struct case.
|
||||
return Fail()
|
||||
<< "Sample interpolation sampling is invalid on integral IO";
|
||||
}
|
||||
has_interpolate_sampling_sample = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply non-integral interpolation.
|
||||
if (has_interpolate_no_perspective || has_interpolate_sampling_centroid ||
|
||||
has_interpolate_sampling_sample) {
|
||||
const ast::InterpolationType type =
|
||||
has_interpolate_no_perspective ? ast::InterpolationType::kLinear
|
||||
: ast::InterpolationType::kPerspective;
|
||||
const ast::InterpolationSampling sampling =
|
||||
has_interpolate_sampling_centroid
|
||||
? ast::InterpolationSampling::kCentroid
|
||||
: (has_interpolate_sampling_sample
|
||||
? ast::InterpolationSampling::kSample
|
||||
: ast::InterpolationSampling::
|
||||
kNone /* Center is the default */);
|
||||
if (type == ast::InterpolationType::kPerspective &&
|
||||
sampling == ast::InterpolationSampling::kNone) {
|
||||
// This is the default. Don't add a decoration.
|
||||
} else {
|
||||
ast_decos->emplace_back(
|
||||
create<ast::InterpolateDecoration>(type, sampling));
|
||||
}
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
|
|
|
@ -252,6 +252,15 @@ class ParserImpl : Reader {
|
|||
ast::DecorationList* ast_decos,
|
||||
bool transfer_pipeline_io);
|
||||
|
||||
/// Converts SPIR-V interpolation decorations into AST decorations.
|
||||
/// @param store_type the store type for the variable or member
|
||||
/// @param decorations the SPIR-V interpolation decorations
|
||||
/// @param ast_decos the decoration list to populate.
|
||||
/// @returns false if conversion fails
|
||||
bool ConvertInterpolationDecorations(const Type* store_type,
|
||||
const DecorationList& decorations,
|
||||
ast::DecorationList* ast_decos);
|
||||
|
||||
/// Converts a SPIR-V struct member decoration. If the decoration is
|
||||
/// recognized but deliberately dropped, then returns nullptr without a
|
||||
/// diagnostic. On failure, emits a diagnostic and returns nullptr.
|
||||
|
@ -386,6 +395,13 @@ class ParserImpl : Reader {
|
|||
ast::Decoration* GetMemberLocation(const Struct& struct_type,
|
||||
int member_index);
|
||||
|
||||
/// Returns the SPIR-V interpolation decorations, if any, on a struct member.
|
||||
/// @param struct_type the parser's structure type.
|
||||
/// @param member_index the member index
|
||||
/// @returns a list of SPIR-V decorations.
|
||||
DecorationList GetMemberInterpolationDecorations(const Struct& struct_type,
|
||||
int member_index);
|
||||
|
||||
/// Creates an AST Variable node for a SPIR-V ID, including any attached
|
||||
/// decorations, unless it's an ignorable builtin variable.
|
||||
/// @param id the SPIR-V result ID
|
||||
|
|
|
@ -55,6 +55,7 @@ std::string MainBody() {
|
|||
std::string CommonCapabilities() {
|
||||
return R"(
|
||||
OpCapability Shader
|
||||
OpCapability SampleRateShading
|
||||
OpMemoryModel Logical Simple
|
||||
)";
|
||||
}
|
||||
|
@ -7760,6 +7761,633 @@ TEST_F(SpvModuleScopeVarParserTest,
|
|||
EXPECT_EQ(got, expected) << got;
|
||||
}
|
||||
|
||||
TEST_F(SpvModuleScopeVarParserTest,
|
||||
EntryPointWrapping_Interpolation_Floating_Fragment_In) {
|
||||
// Flat decorations are dropped for integral
|
||||
const auto assembly = CommonCapabilities() + R"(
|
||||
OpEntryPoint Fragment %main "main" %1 %2 %3 %4 %5 %6
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpDecorate %1 Location 1
|
||||
OpDecorate %2 Location 2
|
||||
OpDecorate %3 Location 3
|
||||
OpDecorate %4 Location 4
|
||||
OpDecorate %5 Location 5
|
||||
OpDecorate %6 Location 6
|
||||
|
||||
; %1 perspective center
|
||||
|
||||
OpDecorate %2 Centroid ; perspective centroid
|
||||
|
||||
OpDecorate %3 Sample ; perspective sample
|
||||
|
||||
OpDecorate %4 NoPerspective; linear center
|
||||
|
||||
OpDecorate %5 NoPerspective ; linear centroid
|
||||
OpDecorate %5 Centroid
|
||||
|
||||
OpDecorate %6 NoPerspective ; linear sample
|
||||
OpDecorate %6 Sample
|
||||
|
||||
)" + CommonTypes() +
|
||||
R"(
|
||||
%ptr_in_float = OpTypePointer Input %float
|
||||
%1 = OpVariable %ptr_in_float Input
|
||||
%2 = OpVariable %ptr_in_float Input
|
||||
%3 = OpVariable %ptr_in_float Input
|
||||
%4 = OpVariable %ptr_in_float Input
|
||||
%5 = OpVariable %ptr_in_float Input
|
||||
%6 = OpVariable %ptr_in_float Input
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto p = parser(test::Assemble(assembly));
|
||||
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModule());
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
const auto got = p->program().to_str();
|
||||
const std::string expected =
|
||||
R"(Module{
|
||||
Variable{
|
||||
x_1
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_2
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_3
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_4
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_5
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_6
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Function main_1 -> __void
|
||||
()
|
||||
{
|
||||
Return{}
|
||||
}
|
||||
Function main -> __void
|
||||
StageDecoration{fragment}
|
||||
(
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{1}
|
||||
}
|
||||
x_1_param
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{2}
|
||||
InterpolateDecoration{perspective centroid}
|
||||
}
|
||||
x_2_param
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{3}
|
||||
InterpolateDecoration{perspective sample}
|
||||
}
|
||||
x_3_param
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{4}
|
||||
InterpolateDecoration{linear none}
|
||||
}
|
||||
x_4_param
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{5}
|
||||
InterpolateDecoration{linear centroid}
|
||||
}
|
||||
x_5_param
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{6}
|
||||
InterpolateDecoration{linear sample}
|
||||
}
|
||||
x_6_param
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
)
|
||||
{
|
||||
Assignment{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{x_1_param}
|
||||
}
|
||||
Assignment{
|
||||
Identifier[not set]{x_2}
|
||||
Identifier[not set]{x_2_param}
|
||||
}
|
||||
Assignment{
|
||||
Identifier[not set]{x_3}
|
||||
Identifier[not set]{x_3_param}
|
||||
}
|
||||
Assignment{
|
||||
Identifier[not set]{x_4}
|
||||
Identifier[not set]{x_4_param}
|
||||
}
|
||||
Assignment{
|
||||
Identifier[not set]{x_5}
|
||||
Identifier[not set]{x_5_param}
|
||||
}
|
||||
Assignment{
|
||||
Identifier[not set]{x_6}
|
||||
Identifier[not set]{x_6_param}
|
||||
}
|
||||
Call[not set]{
|
||||
Identifier[not set]{main_1}
|
||||
(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
EXPECT_EQ(got, expected) << got;
|
||||
}
|
||||
|
||||
TEST_F(SpvModuleScopeVarParserTest,
|
||||
EntryPointWrapping_Flatten_Interpolation_Floating_Fragment_In) {
|
||||
const auto assembly = CommonCapabilities() + R"(
|
||||
OpEntryPoint Fragment %main "main" %1
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpDecorate %1 Location 1
|
||||
|
||||
; member 0 perspective center
|
||||
|
||||
OpMemberDecorate %10 1 Centroid ; perspective centroid
|
||||
|
||||
OpMemberDecorate %10 2 Sample ; perspective sample
|
||||
|
||||
OpMemberDecorate %10 3 NoPerspective; linear center
|
||||
|
||||
OpMemberDecorate %10 4 NoPerspective ; linear centroid
|
||||
OpMemberDecorate %10 4 Centroid
|
||||
|
||||
OpMemberDecorate %10 5 NoPerspective ; linear sample
|
||||
OpMemberDecorate %10 5 Sample
|
||||
|
||||
)" + CommonTypes() +
|
||||
R"(
|
||||
|
||||
%10 = OpTypeStruct %float %float %float %float %float %float
|
||||
%ptr_in_strct = OpTypePointer Input %10
|
||||
%1 = OpVariable %ptr_in_strct Input
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto p = parser(test::Assemble(assembly));
|
||||
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
const auto got = p->program().to_str();
|
||||
const std::string expected =
|
||||
R"(Module{
|
||||
Struct S {
|
||||
StructMember{field0: __f32}
|
||||
StructMember{field1: __f32}
|
||||
StructMember{field2: __f32}
|
||||
StructMember{field3: __f32}
|
||||
StructMember{field4: __f32}
|
||||
StructMember{field5: __f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
private
|
||||
undefined
|
||||
__type_name_S
|
||||
}
|
||||
Function main_1 -> __void
|
||||
()
|
||||
{
|
||||
Return{}
|
||||
}
|
||||
Function main -> __void
|
||||
StageDecoration{fragment}
|
||||
(
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{1}
|
||||
}
|
||||
x_1_param
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{2}
|
||||
InterpolateDecoration{perspective centroid}
|
||||
}
|
||||
x_1_param_1
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{3}
|
||||
InterpolateDecoration{perspective sample}
|
||||
}
|
||||
x_1_param_2
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{4}
|
||||
InterpolateDecoration{linear none}
|
||||
}
|
||||
x_1_param_3
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{5}
|
||||
InterpolateDecoration{linear centroid}
|
||||
}
|
||||
x_1_param_4
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
VariableConst{
|
||||
Decorations{
|
||||
LocationDecoration{6}
|
||||
InterpolateDecoration{linear sample}
|
||||
}
|
||||
x_1_param_5
|
||||
none
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
)
|
||||
{
|
||||
Assignment{
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field0}
|
||||
}
|
||||
Identifier[not set]{x_1_param}
|
||||
}
|
||||
Assignment{
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field1}
|
||||
}
|
||||
Identifier[not set]{x_1_param_1}
|
||||
}
|
||||
Assignment{
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field2}
|
||||
}
|
||||
Identifier[not set]{x_1_param_2}
|
||||
}
|
||||
Assignment{
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field3}
|
||||
}
|
||||
Identifier[not set]{x_1_param_3}
|
||||
}
|
||||
Assignment{
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field4}
|
||||
}
|
||||
Identifier[not set]{x_1_param_4}
|
||||
}
|
||||
Assignment{
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field5}
|
||||
}
|
||||
Identifier[not set]{x_1_param_5}
|
||||
}
|
||||
Call[not set]{
|
||||
Identifier[not set]{main_1}
|
||||
(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
EXPECT_EQ(got, expected) << got;
|
||||
}
|
||||
|
||||
TEST_F(SpvModuleScopeVarParserTest,
|
||||
EntryPointWrapping_Interpolation_Floating_Fragment_Out) {
|
||||
// Flat decorations are dropped for integral
|
||||
const auto assembly = CommonCapabilities() + R"(
|
||||
OpEntryPoint Fragment %main "main" %1 %2 %3 %4 %5 %6
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpDecorate %1 Location 1
|
||||
OpDecorate %2 Location 2
|
||||
OpDecorate %3 Location 3
|
||||
OpDecorate %4 Location 4
|
||||
OpDecorate %5 Location 5
|
||||
OpDecorate %6 Location 6
|
||||
|
||||
; %1 perspective center
|
||||
|
||||
OpDecorate %2 Centroid ; perspective centroid
|
||||
|
||||
OpDecorate %3 Sample ; perspective sample
|
||||
|
||||
OpDecorate %4 NoPerspective; linear center
|
||||
|
||||
OpDecorate %5 NoPerspective ; linear centroid
|
||||
OpDecorate %5 Centroid
|
||||
|
||||
OpDecorate %6 NoPerspective ; linear sample
|
||||
OpDecorate %6 Sample
|
||||
|
||||
)" + CommonTypes() +
|
||||
R"(
|
||||
%ptr_out_float = OpTypePointer Output %float
|
||||
%1 = OpVariable %ptr_out_float Output
|
||||
%2 = OpVariable %ptr_out_float Output
|
||||
%3 = OpVariable %ptr_out_float Output
|
||||
%4 = OpVariable %ptr_out_float Output
|
||||
%5 = OpVariable %ptr_out_float Output
|
||||
%6 = OpVariable %ptr_out_float Output
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto p = parser(test::Assemble(assembly));
|
||||
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModule());
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
const auto got = p->program().to_str();
|
||||
const std::string expected =
|
||||
R"(Module{
|
||||
Struct main_out {
|
||||
StructMember{[[ LocationDecoration{1}
|
||||
]] x_1_1: __f32}
|
||||
StructMember{[[ LocationDecoration{2}
|
||||
InterpolateDecoration{perspective centroid}
|
||||
]] x_2_1: __f32}
|
||||
StructMember{[[ LocationDecoration{3}
|
||||
InterpolateDecoration{perspective sample}
|
||||
]] x_3_1: __f32}
|
||||
StructMember{[[ LocationDecoration{4}
|
||||
InterpolateDecoration{linear none}
|
||||
]] x_4_1: __f32}
|
||||
StructMember{[[ LocationDecoration{5}
|
||||
InterpolateDecoration{linear centroid}
|
||||
]] x_5_1: __f32}
|
||||
StructMember{[[ LocationDecoration{6}
|
||||
InterpolateDecoration{linear sample}
|
||||
]] x_6_1: __f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_2
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_3
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_4
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_5
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Variable{
|
||||
x_6
|
||||
private
|
||||
undefined
|
||||
__f32
|
||||
}
|
||||
Function main_1 -> __void
|
||||
()
|
||||
{
|
||||
Return{}
|
||||
}
|
||||
Function main -> __type_name_main_out
|
||||
StageDecoration{fragment}
|
||||
()
|
||||
{
|
||||
Call[not set]{
|
||||
Identifier[not set]{main_1}
|
||||
(
|
||||
)
|
||||
}
|
||||
Return{
|
||||
{
|
||||
TypeConstructor[not set]{
|
||||
__type_name_main_out
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{x_2}
|
||||
Identifier[not set]{x_3}
|
||||
Identifier[not set]{x_4}
|
||||
Identifier[not set]{x_5}
|
||||
Identifier[not set]{x_6}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
EXPECT_EQ(got, expected) << got;
|
||||
}
|
||||
|
||||
TEST_F(SpvModuleScopeVarParserTest,
|
||||
EntryPointWrapping_Flatten_Interpolation_Floating_Fragment_Out) {
|
||||
const auto assembly = CommonCapabilities() + R"(
|
||||
OpEntryPoint Fragment %main "main" %1
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
|
||||
OpDecorate %1 Location 1
|
||||
|
||||
; member 0 perspective center
|
||||
|
||||
OpMemberDecorate %10 1 Centroid ; perspective centroid
|
||||
|
||||
OpMemberDecorate %10 2 Sample ; perspective sample
|
||||
|
||||
OpMemberDecorate %10 3 NoPerspective; linear center
|
||||
|
||||
OpMemberDecorate %10 4 NoPerspective ; linear centroid
|
||||
OpMemberDecorate %10 4 Centroid
|
||||
|
||||
OpMemberDecorate %10 5 NoPerspective ; linear sample
|
||||
OpMemberDecorate %10 5 Sample
|
||||
|
||||
)" + CommonTypes() +
|
||||
R"(
|
||||
|
||||
%10 = OpTypeStruct %float %float %float %float %float %float
|
||||
%ptr_in_strct = OpTypePointer Output %10
|
||||
%1 = OpVariable %ptr_in_strct Output
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto p = parser(test::Assemble(assembly));
|
||||
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModule());
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
const auto got = p->program().to_str();
|
||||
const std::string expected =
|
||||
R"(Module{
|
||||
Struct S {
|
||||
StructMember{field0: __f32}
|
||||
StructMember{field1: __f32}
|
||||
StructMember{field2: __f32}
|
||||
StructMember{field3: __f32}
|
||||
StructMember{field4: __f32}
|
||||
StructMember{field5: __f32}
|
||||
}
|
||||
Struct main_out {
|
||||
StructMember{[[ LocationDecoration{1}
|
||||
]] x_1_1: __f32}
|
||||
StructMember{[[ LocationDecoration{2}
|
||||
InterpolateDecoration{perspective centroid}
|
||||
]] x_1_2: __f32}
|
||||
StructMember{[[ LocationDecoration{3}
|
||||
InterpolateDecoration{perspective sample}
|
||||
]] x_1_3: __f32}
|
||||
StructMember{[[ LocationDecoration{4}
|
||||
InterpolateDecoration{linear none}
|
||||
]] x_1_4: __f32}
|
||||
StructMember{[[ LocationDecoration{5}
|
||||
InterpolateDecoration{linear centroid}
|
||||
]] x_1_5: __f32}
|
||||
StructMember{[[ LocationDecoration{6}
|
||||
InterpolateDecoration{linear sample}
|
||||
]] x_1_6: __f32}
|
||||
}
|
||||
Variable{
|
||||
x_1
|
||||
private
|
||||
undefined
|
||||
__type_name_S
|
||||
}
|
||||
Function main_1 -> __void
|
||||
()
|
||||
{
|
||||
Return{}
|
||||
}
|
||||
Function main -> __type_name_main_out
|
||||
StageDecoration{fragment}
|
||||
()
|
||||
{
|
||||
Call[not set]{
|
||||
Identifier[not set]{main_1}
|
||||
(
|
||||
)
|
||||
}
|
||||
Return{
|
||||
{
|
||||
TypeConstructor[not set]{
|
||||
__type_name_main_out
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field0}
|
||||
}
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field1}
|
||||
}
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field2}
|
||||
}
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field3}
|
||||
}
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field4}
|
||||
}
|
||||
MemberAccessor[not set]{
|
||||
Identifier[not set]{x_1}
|
||||
Identifier[not set]{field5}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
EXPECT_EQ(got, expected) << got;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace spirv
|
||||
} // namespace reader
|
||||
|
|
Loading…
Reference in New Issue