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;
|
tip_type = ref_type->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recursively flatten matrices, arrays, and structures.
|
||||||
if (auto* matrix_type = tip_type->As<Matrix>()) {
|
if (auto* matrix_type = tip_type->As<Matrix>()) {
|
||||||
index_prefix.push_back(0);
|
index_prefix.push_back(0);
|
||||||
const auto num_columns = static_cast<int>(matrix_type->columns);
|
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) {
|
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
|
||||||
index_prefix.back() = i;
|
index_prefix.back() = i;
|
||||||
auto* location = parser_impl_.GetMemberLocation(*struct_type, i);
|
auto* location = parser_impl_.GetMemberLocation(*struct_type, i);
|
||||||
auto* saved_location = SetLocation(decos, location);
|
SetLocation(decos, location);
|
||||||
if (!EmitPipelineInput(var_name, var_type, decos, index_prefix,
|
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,
|
members[i], forced_param_type, params,
|
||||||
statements)) {
|
statements)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SetLocation(decos, saved_location);
|
// Copy the location as updated by nested expansion of the member.
|
||||||
|
SetLocation(decos, GetLocation(member_decos));
|
||||||
}
|
}
|
||||||
return success();
|
return success();
|
||||||
}
|
}
|
||||||
|
@ -1062,7 +1071,7 @@ ast::Decoration* FunctionEmitter::SetLocation(ast::DecorationList* decos,
|
||||||
}
|
}
|
||||||
for (auto*& deco : *decos) {
|
for (auto*& deco : *decos) {
|
||||||
if (deco->Is<ast::LocationDecoration>()) {
|
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
|
// The old one doesn't leak because it's kept in the builder's AST node
|
||||||
// list.
|
// list.
|
||||||
ast::Decoration* result = deco;
|
ast::Decoration* result = deco;
|
||||||
|
@ -1075,6 +1084,16 @@ ast::Decoration* FunctionEmitter::SetLocation(ast::DecorationList* decos,
|
||||||
return nullptr;
|
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,
|
bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
||||||
const Type* var_type,
|
const Type* var_type,
|
||||||
ast::DecorationList* decos,
|
ast::DecorationList* decos,
|
||||||
|
@ -1083,12 +1102,12 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
|
||||||
const Type* forced_member_type,
|
const Type* forced_member_type,
|
||||||
ast::StructMemberList* return_members,
|
ast::StructMemberList* return_members,
|
||||||
ast::ExpressionList* return_exprs) {
|
ast::ExpressionList* return_exprs) {
|
||||||
// TODO(dneto): Handle structs where the locations are annotated on members.
|
|
||||||
tip_type = tip_type->UnwrapAlias();
|
tip_type = tip_type->UnwrapAlias();
|
||||||
if (auto* ref_type = tip_type->As<Reference>()) {
|
if (auto* ref_type = tip_type->As<Reference>()) {
|
||||||
tip_type = ref_type->type;
|
tip_type = ref_type->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recursively flatten matrices, arrays, and structures.
|
||||||
if (auto* matrix_type = tip_type->As<Matrix>()) {
|
if (auto* matrix_type = tip_type->As<Matrix>()) {
|
||||||
index_prefix.push_back(0);
|
index_prefix.push_back(0);
|
||||||
const auto num_columns = static_cast<int>(matrix_type->columns);
|
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) {
|
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
|
||||||
index_prefix.back() = i;
|
index_prefix.back() = i;
|
||||||
auto* location = parser_impl_.GetMemberLocation(*struct_type, i);
|
auto* location = parser_impl_.GetMemberLocation(*struct_type, i);
|
||||||
auto* saved_location = SetLocation(decos, location);
|
SetLocation(decos, location);
|
||||||
if (!EmitPipelineOutput(var_name, var_type, decos, index_prefix,
|
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,
|
members[i], forced_member_type, return_members,
|
||||||
return_exprs)) {
|
return_exprs)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SetLocation(decos, saved_location);
|
// Copy the location as updated by nested expansion of the member.
|
||||||
|
SetLocation(decos, GetLocation(member_decos));
|
||||||
}
|
}
|
||||||
return success();
|
return success();
|
||||||
}
|
}
|
||||||
|
|
|
@ -479,10 +479,16 @@ class FunctionEmitter {
|
||||||
/// Assumes the list contains at most one Location decoration.
|
/// Assumes the list contains at most one Location decoration.
|
||||||
/// @param decos the decoration list to modify
|
/// @param decos the decoration list to modify
|
||||||
/// @param replacement the location decoration to place into the list
|
/// @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* SetLocation(ast::DecorationList* decos,
|
||||||
ast::Decoration* replacement);
|
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.
|
/// Create an ast::BlockStatement representing the body of the function.
|
||||||
/// This creates the statement stack, which is non-empty for the lifetime
|
/// This creates the statement stack, which is non-empty for the lifetime
|
||||||
/// of the function.
|
/// of the function.
|
||||||
|
|
|
@ -233,6 +233,24 @@ bool AssumesResultSignednessMatchesFirstOperand(GLSLstd450 extended_opcode) {
|
||||||
return false;
|
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
|
} // namespace
|
||||||
|
|
||||||
TypedExpression::TypedExpression() = default;
|
TypedExpression::TypedExpression() = default;
|
||||||
|
@ -1104,6 +1122,9 @@ const Type* ParserImpl::ConvertType(
|
||||||
break;
|
break;
|
||||||
case SpvDecorationLocation:
|
case SpvDecorationLocation:
|
||||||
case SpvDecorationFlat:
|
case SpvDecorationFlat:
|
||||||
|
case SpvDecorationNoPerspective:
|
||||||
|
case SpvDecorationCentroid:
|
||||||
|
case SpvDecorationSample:
|
||||||
// IO decorations are handled when emitting the entry point.
|
// IO decorations are handled when emitting the entry point.
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
|
@ -1571,6 +1592,7 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
|
||||||
const Type** store_type,
|
const Type** store_type,
|
||||||
ast::DecorationList* decorations,
|
ast::DecorationList* decorations,
|
||||||
bool transfer_pipeline_io) {
|
bool transfer_pipeline_io) {
|
||||||
|
DecorationList interpolation_decorations;
|
||||||
for (auto& deco : GetDecorationsFor(id)) {
|
for (auto& deco : GetDecorationsFor(id)) {
|
||||||
if (deco.empty()) {
|
if (deco.empty()) {
|
||||||
return Fail() << "malformed decoration on ID " << id << ": it is 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]));
|
create<ast::LocationDecoration>(Source{}, deco[1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (deco[0] == SpvDecorationFlat) {
|
if (transfer_pipeline_io && IsInterpolationDecoration(deco)) {
|
||||||
if (transfer_pipeline_io) {
|
interpolation_decorations.push_back(deco);
|
||||||
// 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 (deco[0] == SpvDecorationDescriptorSet) {
|
if (deco[0] == SpvDecorationDescriptorSet) {
|
||||||
if (deco.size() == 1) {
|
if (deco.size() == 1) {
|
||||||
|
@ -1671,6 +1685,98 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
|
||||||
create<ast::BindingDecoration>(Source{}, deco[1]));
|
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();
|
return success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -252,6 +252,15 @@ class ParserImpl : Reader {
|
||||||
ast::DecorationList* ast_decos,
|
ast::DecorationList* ast_decos,
|
||||||
bool transfer_pipeline_io);
|
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
|
/// Converts a SPIR-V struct member decoration. If the decoration is
|
||||||
/// recognized but deliberately dropped, then returns nullptr without a
|
/// recognized but deliberately dropped, then returns nullptr without a
|
||||||
/// diagnostic. On failure, emits a diagnostic and returns nullptr.
|
/// diagnostic. On failure, emits a diagnostic and returns nullptr.
|
||||||
|
@ -386,6 +395,13 @@ class ParserImpl : Reader {
|
||||||
ast::Decoration* GetMemberLocation(const Struct& struct_type,
|
ast::Decoration* GetMemberLocation(const Struct& struct_type,
|
||||||
int member_index);
|
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
|
/// Creates an AST Variable node for a SPIR-V ID, including any attached
|
||||||
/// decorations, unless it's an ignorable builtin variable.
|
/// decorations, unless it's an ignorable builtin variable.
|
||||||
/// @param id the SPIR-V result ID
|
/// @param id the SPIR-V result ID
|
||||||
|
|
|
@ -55,6 +55,7 @@ std::string MainBody() {
|
||||||
std::string CommonCapabilities() {
|
std::string CommonCapabilities() {
|
||||||
return R"(
|
return R"(
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
|
OpCapability SampleRateShading
|
||||||
OpMemoryModel Logical Simple
|
OpMemoryModel Logical Simple
|
||||||
)";
|
)";
|
||||||
}
|
}
|
||||||
|
@ -7760,6 +7761,633 @@ TEST_F(SpvModuleScopeVarParserTest,
|
||||||
EXPECT_EQ(got, expected) << got;
|
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
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
Loading…
Reference in New Issue