validation: Require interpolate(flat) for integers

This was made a warning in M97, and can now become a hard error.

Fixed: tint:1224
Change-Id: Ied72f6e28b3dc64a6ab832e0eac53f62ce045d40
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/77700
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: James Price <jrprice@google.com>
This commit is contained in:
James Price 2022-01-25 01:01:39 +00:00 committed by Tint LUCI CQ
parent d5560400a3
commit 1c02eb8cb0
25 changed files with 273 additions and 103 deletions

View File

@ -1,5 +1,12 @@
# Tint changes during Origin Trial # Tint changes during Origin Trial
## Changes for M100
### Breaking changes
* The `@interpolate(flat)` attribute must now be specified on integral user-defined IO. [tint:1224](crbug.com/tint/1224)
## Changes for M99 ## Changes for M99
### Breaking changes ### Breaking changes

View File

@ -303,7 +303,7 @@ TEST_P(InspectorGetEntryPointComponentAndCompositionTest, Test) {
std::function<const ast::Type*()> tint_type = std::function<const ast::Type*()> tint_type =
GetTypeFunction(component, composition); GetTypeFunction(component, composition);
auto* in_var = Param("in_var", tint_type(), {Location(0u)}); auto* in_var = Param("in_var", tint_type(), {Location(0u), Flat()});
Func("foo", {in_var}, tint_type(), {Return("in_var")}, Func("foo", {in_var}, tint_type(), {Return("in_var")},
{Stage(ast::PipelineStage::kFragment)}, {Location(0u)}); {Stage(ast::PipelineStage::kFragment)}, {Location(0u)});
Inspector& inspector = Build(); Inspector& inspector = Build();
@ -337,9 +337,9 @@ INSTANTIATE_TEST_SUITE_P(
CompositionType::kVec4))); CompositionType::kVec4)));
TEST_F(InspectorGetEntryPointTest, MultipleInOutVariables) { TEST_F(InspectorGetEntryPointTest, MultipleInOutVariables) {
auto* in_var0 = Param("in_var0", ty.u32(), {Location(0u)}); auto* in_var0 = Param("in_var0", ty.u32(), {Location(0u), Flat()});
auto* in_var1 = Param("in_var1", ty.u32(), {Location(1u)}); auto* in_var1 = Param("in_var1", ty.u32(), {Location(1u), Flat()});
auto* in_var4 = Param("in_var4", ty.u32(), {Location(4u)}); auto* in_var4 = Param("in_var4", ty.u32(), {Location(4u), Flat()});
Func("foo", {in_var0, in_var1, in_var4}, ty.u32(), {Return("in_var0")}, Func("foo", {in_var0, in_var1, in_var4}, ty.u32(), {Return("in_var0")},
{Stage(ast::PipelineStage::kFragment)}, {Location(0u)}); {Stage(ast::PipelineStage::kFragment)}, {Location(0u)});
Inspector& inspector = Build(); Inspector& inspector = Build();
@ -353,14 +353,20 @@ TEST_F(InspectorGetEntryPointTest, MultipleInOutVariables) {
EXPECT_EQ("in_var0", result[0].input_variables[0].name); EXPECT_EQ("in_var0", result[0].input_variables[0].name);
EXPECT_TRUE(result[0].input_variables[0].has_location_decoration); EXPECT_TRUE(result[0].input_variables[0].has_location_decoration);
EXPECT_EQ(0u, result[0].input_variables[0].location_decoration); EXPECT_EQ(0u, result[0].input_variables[0].location_decoration);
EXPECT_EQ(InterpolationType::kFlat,
result[0].input_variables[0].interpolation_type);
EXPECT_EQ(ComponentType::kUInt, result[0].input_variables[0].component_type); EXPECT_EQ(ComponentType::kUInt, result[0].input_variables[0].component_type);
EXPECT_EQ("in_var1", result[0].input_variables[1].name); EXPECT_EQ("in_var1", result[0].input_variables[1].name);
EXPECT_TRUE(result[0].input_variables[1].has_location_decoration); EXPECT_TRUE(result[0].input_variables[1].has_location_decoration);
EXPECT_EQ(1u, result[0].input_variables[1].location_decoration); EXPECT_EQ(1u, result[0].input_variables[1].location_decoration);
EXPECT_EQ(InterpolationType::kFlat,
result[0].input_variables[1].interpolation_type);
EXPECT_EQ(ComponentType::kUInt, result[0].input_variables[1].component_type); EXPECT_EQ(ComponentType::kUInt, result[0].input_variables[1].component_type);
EXPECT_EQ("in_var4", result[0].input_variables[2].name); EXPECT_EQ("in_var4", result[0].input_variables[2].name);
EXPECT_TRUE(result[0].input_variables[2].has_location_decoration); EXPECT_TRUE(result[0].input_variables[2].has_location_decoration);
EXPECT_EQ(4u, result[0].input_variables[2].location_decoration); EXPECT_EQ(4u, result[0].input_variables[2].location_decoration);
EXPECT_EQ(InterpolationType::kFlat,
result[0].input_variables[2].interpolation_type);
EXPECT_EQ(ComponentType::kUInt, result[0].input_variables[2].component_type); EXPECT_EQ(ComponentType::kUInt, result[0].input_variables[2].component_type);
ASSERT_EQ(1u, result[0].output_variables.size()); ASSERT_EQ(1u, result[0].output_variables.size());
@ -371,11 +377,11 @@ TEST_F(InspectorGetEntryPointTest, MultipleInOutVariables) {
} }
TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) { TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) {
auto* in_var_foo = Param("in_var_foo", ty.u32(), {Location(0u)}); auto* in_var_foo = Param("in_var_foo", ty.u32(), {Location(0u), Flat()});
Func("foo", {in_var_foo}, ty.u32(), {Return("in_var_foo")}, Func("foo", {in_var_foo}, ty.u32(), {Return("in_var_foo")},
{Stage(ast::PipelineStage::kFragment)}, {Location(0u)}); {Stage(ast::PipelineStage::kFragment)}, {Location(0u)});
auto* in_var_bar = Param("in_var_bar", ty.u32(), {Location(0u)}); auto* in_var_bar = Param("in_var_bar", ty.u32(), {Location(0u), Flat()});
Func("bar", {in_var_bar}, ty.u32(), {Return("in_var_bar")}, Func("bar", {in_var_bar}, ty.u32(), {Return("in_var_bar")},
{Stage(ast::PipelineStage::kFragment)}, {Location(1u)}); {Stage(ast::PipelineStage::kFragment)}, {Location(1u)});
@ -390,6 +396,8 @@ TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) {
EXPECT_EQ("in_var_foo", result[0].input_variables[0].name); EXPECT_EQ("in_var_foo", result[0].input_variables[0].name);
EXPECT_TRUE(result[0].input_variables[0].has_location_decoration); EXPECT_TRUE(result[0].input_variables[0].has_location_decoration);
EXPECT_EQ(0u, result[0].input_variables[0].location_decoration); EXPECT_EQ(0u, result[0].input_variables[0].location_decoration);
EXPECT_EQ(InterpolationType::kFlat,
result[0].input_variables[0].interpolation_type);
EXPECT_EQ(ComponentType::kUInt, result[0].input_variables[0].component_type); EXPECT_EQ(ComponentType::kUInt, result[0].input_variables[0].component_type);
ASSERT_EQ(1u, result[0].output_variables.size()); ASSERT_EQ(1u, result[0].output_variables.size());
@ -402,6 +410,8 @@ TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) {
EXPECT_EQ("in_var_bar", result[1].input_variables[0].name); EXPECT_EQ("in_var_bar", result[1].input_variables[0].name);
EXPECT_TRUE(result[1].input_variables[0].has_location_decoration); EXPECT_TRUE(result[1].input_variables[0].has_location_decoration);
EXPECT_EQ(0u, result[1].input_variables[0].location_decoration); EXPECT_EQ(0u, result[1].input_variables[0].location_decoration);
EXPECT_EQ(InterpolationType::kFlat,
result[1].input_variables[0].interpolation_type);
EXPECT_EQ(ComponentType::kUInt, result[1].input_variables[0].component_type); EXPECT_EQ(ComponentType::kUInt, result[1].input_variables[0].component_type);
ASSERT_EQ(1u, result[1].output_variables.size()); ASSERT_EQ(1u, result[1].output_variables.size());

View File

@ -55,7 +55,8 @@ const ast::Struct* InspectorBuilder::MakeInOutStruct(
std::string member_name; std::string member_name;
uint32_t location; uint32_t location;
std::tie(member_name, location) = var; std::tie(member_name, location) = var;
members.push_back(Member(member_name, ty.u32(), {Location(location)})); members.push_back(
Member(member_name, ty.u32(), {Location(location), Flat()}));
} }
return Structure(name, members); return Structure(name, members);
} }

View File

@ -2300,7 +2300,7 @@ class ProgramBuilder {
const ast::InterpolateDecoration* Interpolate( const ast::InterpolateDecoration* Interpolate(
const Source& source, const Source& source,
ast::InterpolationType type, ast::InterpolationType type,
ast::InterpolationSampling sampling) { ast::InterpolationSampling sampling = ast::InterpolationSampling::kNone) {
return create<ast::InterpolateDecoration>(source, type, sampling); return create<ast::InterpolateDecoration>(source, type, sampling);
} }
@ -2310,10 +2310,23 @@ class ProgramBuilder {
/// @returns the interpolate decoration pointer /// @returns the interpolate decoration pointer
const ast::InterpolateDecoration* Interpolate( const ast::InterpolateDecoration* Interpolate(
ast::InterpolationType type, ast::InterpolationType type,
ast::InterpolationSampling sampling) { ast::InterpolationSampling sampling = ast::InterpolationSampling::kNone) {
return create<ast::InterpolateDecoration>(source_, type, sampling); return create<ast::InterpolateDecoration>(source_, type, sampling);
} }
/// Creates an ast::InterpolateDecoration using flat interpolation
/// @param source the source information
/// @returns the interpolate decoration pointer
const ast::InterpolateDecoration* Flat(const Source& source) {
return Interpolate(source, ast::InterpolationType::kFlat);
}
/// Creates an ast::InterpolateDecoration using flat interpolation
/// @returns the interpolate decoration pointer
const ast::InterpolateDecoration* Flat() {
return Interpolate(ast::InterpolationType::kFlat);
}
/// Creates an ast::InvariantDecoration /// Creates an ast::InvariantDecoration
/// @param source the source information /// @param source the source information
/// @returns the invariant decoration pointer /// @returns the invariant decoration pointer

View File

@ -1790,6 +1790,10 @@ bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
} }
SetLocation(ast_decos, SetLocation(ast_decos,
create<ast::LocationDecoration>(Source{}, deco[1])); create<ast::LocationDecoration>(Source{}, deco[1]));
if (store_type->IsIntegerScalarOrVector()) {
// Default to flat interpolation for integral user-defined IO types.
type = ast::InterpolationType::kFlat;
}
break; break;
case SpvDecorationFlat: case SpvDecorationFlat:
type = ast::InterpolationType::kFlat; type = ast::InterpolationType::kFlat;

View File

@ -3727,14 +3727,14 @@ fn main_1() {
} }
struct main_out { struct main_out {
@location(0) @location(0) @interpolate(flat)
x_2_1 : u32; x_2_1 : u32;
@location(6) @location(6) @interpolate(flat)
x_4_1 : u32; x_4_1 : u32;
} }
@stage(fragment) @stage(fragment)
fn main(@location(0) x_1_param : u32, @location(30) x_3_param : u32) -> main_out { fn main(@location(0) @interpolate(flat) x_1_param : u32, @location(30) @interpolate(flat) x_3_param : u32) -> main_out {
x_1 = x_1_param; x_1 = x_1_param;
x_3 = x_3_param; x_3 = x_3_param;
main_1(); main_1();
@ -4679,7 +4679,6 @@ fn main(@location(9) x_1_param : f32, @location(11) x_1_param_1 : vec4<f32>) ->
TEST_F(SpvModuleScopeVarParserTest, TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Interpolation_Flat_Vertex_In) { EntryPointWrapping_Interpolation_Flat_Vertex_In) {
// Flat decorations are dropped for integral
const auto assembly = CommonCapabilities() + R"( const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Vertex %main "main" %1 %2 %3 %4 %5 %6 %10 OpEntryPoint Vertex %main "main" %1 %2 %3 %4 %5 %6 %10
OpDecorate %1 Location 1 OpDecorate %1 Location 1
@ -4764,7 +4763,6 @@ fn main(@location(1) @interpolate(flat) x_1_param : u32, @location(2) @interpola
TEST_F(SpvModuleScopeVarParserTest, TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Interpolation_Flat_Vertex_Output) { EntryPointWrapping_Interpolation_Flat_Vertex_Output) {
// Flat decorations are dropped for integral
const auto assembly = CommonCapabilities() + R"( const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Vertex %main "main" %1 %2 %3 %4 %5 %6 %10 OpEntryPoint Vertex %main "main" %1 %2 %3 %4 %5 %6 %10
OpDecorate %1 Location 1 OpDecorate %1 Location 1
@ -4855,7 +4853,6 @@ fn main() -> main_out {
TEST_F(SpvModuleScopeVarParserTest, TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Flatten_Interpolation_Flat_Fragment_In) { EntryPointWrapping_Flatten_Interpolation_Flat_Fragment_In) {
// Flat decorations are dropped for integral
const auto assembly = CommonCapabilities() + R"( const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Fragment %main "main" %1 %2 OpEntryPoint Fragment %main "main" %1 %2
OpExecutionMode %main OriginUpperLeft OpExecutionMode %main OriginUpperLeft
@ -4910,7 +4907,6 @@ fn main(@location(1) @interpolate(flat) x_1_param : f32, @location(2) @interpola
TEST_F(SpvModuleScopeVarParserTest, TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Interpolation_Floating_Fragment_In) { EntryPointWrapping_Interpolation_Floating_Fragment_In) {
// Flat decorations are dropped for integral
const auto assembly = CommonCapabilities() + R"( const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Fragment %main "main" %1 %2 %3 %4 %5 %6 OpEntryPoint Fragment %main "main" %1 %2 %3 %4 %5 %6
OpExecutionMode %main OriginUpperLeft OpExecutionMode %main OriginUpperLeft
@ -5056,7 +5052,6 @@ fn main(@location(1) x_1_param : f32, @location(2) @interpolate(perspective, cen
TEST_F(SpvModuleScopeVarParserTest, TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Interpolation_Floating_Fragment_Out) { EntryPointWrapping_Interpolation_Floating_Fragment_Out) {
// Flat decorations are dropped for integral
const auto assembly = CommonCapabilities() + R"( const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Fragment %main "main" %1 %2 %3 %4 %5 %6 OpEntryPoint Fragment %main "main" %1 %2 %3 %4 %5 %6
OpExecutionMode %main OriginUpperLeft OpExecutionMode %main OriginUpperLeft
@ -5221,6 +5216,162 @@ fn main() -> main_out {
EXPECT_EQ(got, expected) << got; EXPECT_EQ(got, expected) << got;
} }
TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Interpolation_Default_Vertex_Output) {
// Integral types default to @interpolate(flat).
// Floating types default to @interpolate(perspective, center), which is the
// same as WGSL and therefore dropped.
const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Vertex %main "main" %1 %2 %3 %4 %5 %6 %10
OpDecorate %1 Location 1
OpDecorate %2 Location 2
OpDecorate %3 Location 3
OpDecorate %4 Location 4
OpDecorate %5 Location 5
OpDecorate %6 Location 6
OpDecorate %10 BuiltIn Position
)" + CommonTypes() +
R"(
%ptr_out_uint = OpTypePointer Output %uint
%ptr_out_v2uint = OpTypePointer Output %v2uint
%ptr_out_int = OpTypePointer Output %int
%ptr_out_v2int = OpTypePointer Output %v2int
%ptr_out_float = OpTypePointer Output %float
%ptr_out_v2float = OpTypePointer Output %v2float
%1 = OpVariable %ptr_out_uint Output
%2 = OpVariable %ptr_out_v2uint Output
%3 = OpVariable %ptr_out_int Output
%4 = OpVariable %ptr_out_v2int Output
%5 = OpVariable %ptr_out_float Output
%6 = OpVariable %ptr_out_v2float Output
%ptr_out_v4float = OpTypePointer Output %v4float
%10 = OpVariable %ptr_out_v4float 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 = test::ToString(p->program());
const std::string expected =
R"(var<private> x_1 : u32;
var<private> x_2 : vec2<u32>;
var<private> x_3 : i32;
var<private> x_4 : vec2<i32>;
var<private> x_5 : f32;
var<private> x_6 : vec2<f32>;
var<private> x_10 : vec4<f32>;
fn main_1() {
return;
}
struct main_out {
@location(1) @interpolate(flat)
x_1_1 : u32;
@location(2) @interpolate(flat)
x_2_1 : vec2<u32>;
@location(3) @interpolate(flat)
x_3_1 : i32;
@location(4) @interpolate(flat)
x_4_1 : vec2<i32>;
@location(5)
x_5_1 : f32;
@location(6)
x_6_1 : vec2<f32>;
@builtin(position)
x_10_1 : vec4<f32>;
}
@stage(vertex)
fn main() -> main_out {
main_1();
return main_out(x_1, x_2, x_3, x_4, x_5, x_6, x_10);
}
)";
EXPECT_EQ(got, expected) << got;
}
TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Interpolation_Default_Fragment_In) {
// Integral types default to @interpolate(flat).
// Floating types default to @interpolate(perspective, center), which is the
// same as WGSL and therefore dropped.
const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Fragment %main "main" %1 %2 %3 %4 %5 %6
OpDecorate %1 Location 1
OpDecorate %2 Location 2
OpDecorate %3 Location 3
OpDecorate %4 Location 4
OpDecorate %5 Location 5
OpDecorate %6 Location 6
)" + CommonTypes() +
R"(
%ptr_in_uint = OpTypePointer Input %uint
%ptr_in_v2uint = OpTypePointer Input %v2uint
%ptr_in_int = OpTypePointer Input %int
%ptr_in_v2int = OpTypePointer Input %v2int
%ptr_in_float = OpTypePointer Input %float
%ptr_in_v2float = OpTypePointer Input %v2float
%1 = OpVariable %ptr_in_uint Input
%2 = OpVariable %ptr_in_v2uint Input
%3 = OpVariable %ptr_in_int Input
%4 = OpVariable %ptr_in_v2int Input
%5 = OpVariable %ptr_in_float Input
%6 = OpVariable %ptr_in_v2float 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 = test::ToString(p->program());
const std::string expected =
R"(var<private> x_1 : u32;
var<private> x_2 : vec2<u32>;
var<private> x_3 : i32;
var<private> x_4 : vec2<i32>;
var<private> x_5 : f32;
var<private> x_6 : vec2<f32>;
fn main_1() {
return;
}
@stage(fragment)
fn main(@location(1) @interpolate(flat) x_1_param : u32, @location(2) @interpolate(flat) x_2_param : vec2<u32>, @location(3) @interpolate(flat) x_3_param : i32, @location(4) @interpolate(flat) x_4_param : vec2<i32>, @location(5) x_5_param : f32, @location(6) x_6_param : vec2<f32>) {
x_1 = x_1_param;
x_2 = x_2_param;
x_3 = x_3_param;
x_4 = x_4_param;
x_5 = x_5_param;
x_6 = x_6_param;
main_1();
}
)";
EXPECT_EQ(got, expected) << got;
}
} // namespace } // namespace
} // namespace spirv } // namespace spirv
} // namespace reader } // namespace reader

View File

@ -1332,11 +1332,10 @@ TEST_F(InterpolateTest, FragmentInput_Integer_MissingFlatInterpolation) {
ty.void_(), {}, ty.void_(), {},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)}); ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
// TODO(crbug.com/tint/1224): Make this an error. EXPECT_FALSE(r()->Resolve());
EXPECT_TRUE(r()->Resolve()); EXPECT_EQ(
EXPECT_EQ(r()->error(), r()->error(),
"12:34 warning: integral user-defined fragment inputs must have a " R"(12:34 error: integral user-defined fragment inputs must have a flat interpolation attribute)");
"flat interpolation attribute");
} }
TEST_F(InterpolateTest, VertexOutput_Integer_MissingFlatInterpolation) { TEST_F(InterpolateTest, VertexOutput_Integer_MissingFlatInterpolation) {
@ -1350,11 +1349,11 @@ TEST_F(InterpolateTest, VertexOutput_Integer_MissingFlatInterpolation) {
Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))}, Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
ast::DecorationList{Stage(ast::PipelineStage::kVertex)}); ast::DecorationList{Stage(ast::PipelineStage::kVertex)});
// TODO(crbug.com/tint/1224): Make this an error. EXPECT_FALSE(r()->Resolve());
EXPECT_TRUE(r()->Resolve()); EXPECT_EQ(
EXPECT_EQ(r()->error(), r()->error(),
"12:34 warning: integral user-defined vertex outputs must have a " R"(12:34 error: integral user-defined vertex outputs must have a flat interpolation attribute
"flat interpolation attribute"); note: while analysing entry point 'main')");
} }
TEST_F(InterpolateTest, MissingLocationAttribute_Parameter) { TEST_F(InterpolateTest, MissingLocationAttribute_Parameter) {

View File

@ -371,9 +371,9 @@ static constexpr Params cases[] = {
TEST_P(TypeValidationTest, BareInputs) { TEST_P(TypeValidationTest, BareInputs) {
// @stage(fragment) // @stage(fragment)
// fn main(@location(0) a : *) {} // fn main(@location(0) @interpolate(flat) a : *) {}
auto params = GetParam(); auto params = GetParam();
auto* a = Param("a", params.create_ast_type(*this), {Location(0)}); auto* a = Param("a", params.create_ast_type(*this), {Location(0), Flat()});
Func(Source{{12, 34}}, "main", {a}, ty.void_(), {}, Func(Source{{12, 34}}, "main", {a}, ty.void_(), {},
{Stage(ast::PipelineStage::kFragment)}); {Stage(ast::PipelineStage::kFragment)});
@ -386,13 +386,13 @@ TEST_P(TypeValidationTest, BareInputs) {
TEST_P(TypeValidationTest, StructInputs) { TEST_P(TypeValidationTest, StructInputs) {
// struct Input { // struct Input {
// @location(0) a : *; // @location(0) @interpolate(flat) a : *;
// }; // };
// @stage(fragment) // @stage(fragment)
// fn main(a : Input) {} // fn main(a : Input) {}
auto params = GetParam(); auto params = GetParam();
auto* input = Structure( auto* input = Structure("Input", {Member("a", params.create_ast_type(*this),
"Input", {Member("a", params.create_ast_type(*this), {Location(0)})}); {Location(0), Flat()})});
auto* a = Param("a", ty.Of(input), {}); auto* a = Param("a", ty.Of(input), {});
Func(Source{{12, 34}}, "main", {a}, ty.void_(), {}, Func(Source{{12, 34}}, "main", {a}, ty.void_(), {},
{Stage(ast::PipelineStage::kFragment)}); {Stage(ast::PipelineStage::kFragment)});
@ -454,9 +454,9 @@ using LocationDecorationTests = ResolverTest;
TEST_F(LocationDecorationTests, Pass) { TEST_F(LocationDecorationTests, Pass) {
// @stage(fragment) // @stage(fragment)
// fn frag_main(@location(0) a: i32) {} // fn frag_main(@location(0) @interpolate(flat) a: i32) {}
auto* p = Param(Source{{12, 34}}, "a", ty.i32(), {Location(0)}); auto* p = Param(Source{{12, 34}}, "a", ty.i32(), {Location(0), Flat()});
Func("frag_main", {p}, ty.void_(), {}, Func("frag_main", {p}, ty.void_(), {},
{Stage(ast::PipelineStage::kFragment)}); {Stage(ast::PipelineStage::kFragment)});

View File

@ -1055,21 +1055,21 @@ bool Resolver::ValidateEntryPoint(const sem::Function* func) {
if (pipeline_io_attribute && if (pipeline_io_attribute &&
pipeline_io_attribute->Is<ast::LocationDecoration>()) { pipeline_io_attribute->Is<ast::LocationDecoration>()) {
if (ty->is_integer_scalar_or_vector() && !interpolate_attribute) { if (ty->is_integer_scalar_or_vector() && !interpolate_attribute) {
// TODO(crbug.com/tint/1224): Make these errors once downstream
// usages have caught up (no sooner than M99).
if (decl->PipelineStage() == ast::PipelineStage::kVertex && if (decl->PipelineStage() == ast::PipelineStage::kVertex &&
param_or_ret == ParamOrRetType::kReturnType) { param_or_ret == ParamOrRetType::kReturnType) {
AddWarning( AddError(
"integral user-defined vertex outputs must have a flat " "integral user-defined vertex outputs must have a flat "
"interpolation attribute", "interpolation attribute",
source); source);
return false;
} }
if (decl->PipelineStage() == ast::PipelineStage::kFragment && if (decl->PipelineStage() == ast::PipelineStage::kFragment &&
param_or_ret == ParamOrRetType::kParameter) { param_or_ret == ParamOrRetType::kParameter) {
AddWarning( AddError(
"integral user-defined fragment inputs must have a flat " "integral user-defined fragment inputs must have a flat "
"interpolation attribute", "interpolation attribute",
source); source);
return false;
} }
} }
} }

View File

@ -74,7 +74,7 @@ TEST_F(CanonicalizeEntryPointIOTest, Parameters_Spirv) {
auto* src = R"( auto* src = R"(
@stage(fragment) @stage(fragment)
fn frag_main(@location(1) loc1 : f32, fn frag_main(@location(1) loc1 : f32,
@location(2) loc2 : vec4<u32>, @location(2) @interpolate(flat) loc2 : vec4<u32>,
@builtin(position) coord : vec4<f32>) { @builtin(position) coord : vec4<f32>) {
var col : f32 = (coord.x * loc1); var col : f32 = (coord.x * loc1);
} }
@ -109,7 +109,7 @@ TEST_F(CanonicalizeEntryPointIOTest, Parameters_Msl) {
auto* src = R"( auto* src = R"(
@stage(fragment) @stage(fragment)
fn frag_main(@location(1) loc1 : f32, fn frag_main(@location(1) loc1 : f32,
@location(2) loc2 : vec4<u32>, @location(2) @interpolate(flat) loc2 : vec4<u32>,
@builtin(position) coord : vec4<f32>) { @builtin(position) coord : vec4<f32>) {
var col : f32 = (coord.x * loc1); var col : f32 = (coord.x * loc1);
} }
@ -119,7 +119,7 @@ fn frag_main(@location(1) loc1 : f32,
struct tint_symbol_1 { struct tint_symbol_1 {
@location(1) @location(1)
loc1 : f32; loc1 : f32;
@location(2) @location(2) @interpolate(flat)
loc2 : vec4<u32>; loc2 : vec4<u32>;
} }
@ -145,7 +145,7 @@ TEST_F(CanonicalizeEntryPointIOTest, Parameters_Hlsl) {
auto* src = R"( auto* src = R"(
@stage(fragment) @stage(fragment)
fn frag_main(@location(1) loc1 : f32, fn frag_main(@location(1) loc1 : f32,
@location(2) loc2 : vec4<u32>, @location(2) @interpolate(flat) loc2 : vec4<u32>,
@builtin(position) coord : vec4<f32>) { @builtin(position) coord : vec4<f32>) {
var col : f32 = (coord.x * loc1); var col : f32 = (coord.x * loc1);
} }
@ -155,7 +155,7 @@ fn frag_main(@location(1) loc1 : f32,
struct tint_symbol_1 { struct tint_symbol_1 {
@location(1) @location(1)
loc1 : f32; loc1 : f32;
@location(2) @location(2) @interpolate(flat)
loc2 : vec4<u32>; loc2 : vec4<u32>;
@builtin(position) @builtin(position)
coord : vec4<f32>; coord : vec4<f32>;
@ -222,7 +222,7 @@ struct FragBuiltins {
}; };
struct FragLocations { struct FragLocations {
@location(1) loc1 : f32; @location(1) loc1 : f32;
@location(2) loc2 : vec4<u32>; @location(2) @interpolate(flat) loc2 : vec4<u32>;
}; };
@stage(fragment) @stage(fragment)
@ -276,7 +276,7 @@ struct FragBuiltins {
}; };
struct FragLocations { struct FragLocations {
@location(1) loc1 : f32; @location(1) loc1 : f32;
@location(2) loc2 : vec4<u32>; @location(2) @interpolate(flat) loc2 : vec4<u32>;
}; };
@stage(fragment) @stage(fragment)
@ -302,7 +302,7 @@ struct tint_symbol_1 {
loc0 : f32; loc0 : f32;
@location(1) @location(1)
loc1 : f32; loc1 : f32;
@location(2) @location(2) @interpolate(flat)
loc2 : vec4<u32>; loc2 : vec4<u32>;
} }
@ -331,7 +331,7 @@ struct FragBuiltins {
}; };
struct FragLocations { struct FragLocations {
@location(1) loc1 : f32; @location(1) loc1 : f32;
@location(2) loc2 : vec4<u32>; @location(2) @interpolate(flat) loc2 : vec4<u32>;
}; };
@stage(fragment) @stage(fragment)
@ -357,7 +357,7 @@ struct tint_symbol_1 {
loc0 : f32; loc0 : f32;
@location(1) @location(1)
loc1 : f32; loc1 : f32;
@location(2) @location(2) @interpolate(flat)
loc2 : vec4<u32>; loc2 : vec4<u32>;
@builtin(position) @builtin(position)
coord : vec4<f32>; coord : vec4<f32>;
@ -1134,18 +1134,18 @@ struct VertexIn {
}; };
struct VertexOut { struct VertexOut {
@location(0) i : i32; @location(0) @interpolate(flat) i : i32;
@location(1) u : u32; @location(1) @interpolate(flat) u : u32;
@location(2) vi : vec4<i32>; @location(2) @interpolate(flat) vi : vec4<i32>;
@location(3) vu : vec4<u32>; @location(3) @interpolate(flat) vu : vec4<u32>;
@builtin(position) pos : vec4<f32>; @builtin(position) pos : vec4<f32>;
}; };
struct FragmentInterface { struct FragmentInterface {
@location(0) i : i32; @location(0) @interpolate(flat) i : i32;
@location(1) u : u32; @location(1) @interpolate(flat) u : u32;
@location(2) vi : vec4<i32>; @location(2) @interpolate(flat) vi : vec4<i32>;
@location(3) vu : vec4<u32>; @location(3) @interpolate(flat) vu : vec4<u32>;
}; };
@stage(vertex) @stage(vertex)
@ -1187,13 +1187,13 @@ fn frag_main(inputs : FragmentInterface) -> FragmentInterface {
@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vu_3 : vec4<u32>; @location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vu_3 : vec4<u32>;
@location(0) @internal(disable_validation__ignore_storage_class) var<out> i_4 : i32; @location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> i_4 : i32;
@location(1) @internal(disable_validation__ignore_storage_class) var<out> u_4 : u32; @location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> u_4 : u32;
@location(2) @internal(disable_validation__ignore_storage_class) var<out> vi_4 : vec4<i32>; @location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vi_4 : vec4<i32>;
@location(3) @internal(disable_validation__ignore_storage_class) var<out> vu_4 : vec4<u32>; @location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vu_4 : vec4<u32>;
struct VertexIn { struct VertexIn {
i : i32; i : i32;
@ -1389,15 +1389,15 @@ fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) { TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) {
auto* src = R"( auto* src = R"(
struct VertexOutput { struct VertexOutput {
@location(1) b : u32; @location(1) @interpolate(flat) b : u32;
@builtin(position) pos : vec4<f32>; @builtin(position) pos : vec4<f32>;
@location(3) d : u32; @location(3) @interpolate(flat) d : u32;
@location(0) a : f32; @location(0) a : f32;
@location(2) c : i32; @location(2) @interpolate(flat) c : i32;
}; };
struct FragmentInputExtra { struct FragmentInputExtra {
@location(3) d : u32; @location(3) @interpolate(flat) d : u32;
@builtin(position) pos : vec4<f32>; @builtin(position) pos : vec4<f32>;
@location(0) a : f32; @location(0) a : f32;
}; };
@ -1409,9 +1409,9 @@ fn vert_main() -> VertexOutput {
@stage(fragment) @stage(fragment)
fn frag_main(@builtin(front_facing) ff : bool, fn frag_main(@builtin(front_facing) ff : bool,
@location(2) c : i32, @location(2) @interpolate(flat) c : i32,
inputs : FragmentInputExtra, inputs : FragmentInputExtra,
@location(1) b : u32) { @location(1) @interpolate(flat) b : u32) {
} }
)"; )";
@ -1433,11 +1433,11 @@ struct FragmentInputExtra {
struct tint_symbol { struct tint_symbol {
@location(0) @location(0)
a : f32; a : f32;
@location(1) @location(1) @interpolate(flat)
b : u32; b : u32;
@location(2) @location(2) @interpolate(flat)
c : i32; c : i32;
@location(3) @location(3) @interpolate(flat)
d : u32; d : u32;
@builtin(position) @builtin(position)
pos : vec4<f32>; pos : vec4<f32>;
@ -1462,11 +1462,11 @@ fn vert_main() -> tint_symbol {
struct tint_symbol_2 { struct tint_symbol_2 {
@location(0) @location(0)
a : f32; a : f32;
@location(1) @location(1) @interpolate(flat)
b : u32; b : u32;
@location(2) @location(2) @interpolate(flat)
c : i32; c : i32;
@location(3) @location(3) @interpolate(flat)
d : u32; d : u32;
@builtin(position) @builtin(position)
pos : vec4<f32>; pos : vec4<f32>;

View File

@ -107,13 +107,14 @@ OpFunctionEnd
TEST_F(BuilderTest, EntryPoint_ReturnValue) { TEST_F(BuilderTest, EntryPoint_ReturnValue) {
// @stage(fragment) // @stage(fragment)
// fn frag_main(@location(0) loc_in : u32) -> @location(0) f32 { // fn frag_main(@location(0) @interpolate(flat) loc_in : u32)
// -> @location(0) f32 {
// if (loc_in > 10) { // if (loc_in > 10) {
// return 0.5; // return 0.5;
// } // }
// return 1.0; // return 1.0;
// } // }
auto* loc_in = Param("loc_in", ty.u32(), {Location(0)}); auto* loc_in = Param("loc_in", ty.u32(), {Location(0), Flat()});
auto* cond = create<ast::BinaryExpression>(ast::BinaryOp::kGreaterThan, auto* cond = create<ast::BinaryExpression>(ast::BinaryOp::kGreaterThan,
Expr("loc_in"), Expr(10u)); Expr("loc_in"), Expr(10u));
Func("frag_main", ast::VariableList{loc_in}, ty.f32(), Func("frag_main", ast::VariableList{loc_in}, ty.f32(),

View File

@ -6,7 +6,7 @@ fn f(x : i32) -> i32 {
} }
@stage(fragment) @stage(fragment)
fn main(@location(1) x: vec3<i32>) -> @location(2) i32 { fn main(@location(1) @interpolate(flat) x: vec3<i32>) -> @location(2) i32 {
var y = x.x; var y = x.x;
loop { loop {
let r = f(y); let r = f(y);

View File

@ -1,7 +1,3 @@
bug/tint/1081.wgsl:9:22 warning: integral user-defined fragment inputs must have a flat interpolation attribute
fn main(@location(1) x: vec3<i32>) -> @location(2) i32 {
^
int f(int x) { int f(int x) {
if (true) { if (true) {
if ((x == 10)) { if ((x == 10)) {
@ -14,7 +10,7 @@ int f(int x) {
} }
struct tint_symbol_1 { struct tint_symbol_1 {
int3 x : TEXCOORD1; nointerpolation int3 x : TEXCOORD1;
}; };
struct tint_symbol_2 { struct tint_symbol_2 {
int value : SV_Target2; int value : SV_Target2;

View File

@ -1,12 +1,8 @@
bug/tint/1081.wgsl:9:22 warning: integral user-defined fragment inputs must have a flat interpolation attribute
fn main(@location(1) x: vec3<i32>) -> @location(2) i32 {
^
#include <metal_stdlib> #include <metal_stdlib>
using namespace metal; using namespace metal;
struct tint_symbol_2 { struct tint_symbol_2 {
int3 x [[user(locn1)]]; int3 x [[user(locn1)]] [[flat]];
}; };
struct tint_symbol_3 { struct tint_symbol_3 {
int value [[color(2)]]; int value [[color(2)]];

View File

@ -1,7 +1,3 @@
bug/tint/1081.wgsl:9:22 warning: integral user-defined fragment inputs must have a flat interpolation attribute
fn main(@location(1) x: vec3<i32>) -> @location(2) i32 {
^
; SPIR-V ; SPIR-V
; Version: 1.3 ; Version: 1.3
; Generator: Google Tint Compiler; 0 ; Generator: Google Tint Compiler; 0

View File

@ -1,7 +1,3 @@
bug/tint/1081.wgsl:9:22 warning: integral user-defined fragment inputs must have a flat interpolation attribute
fn main(@location(1) x: vec3<i32>) -> @location(2) i32 {
^
fn f(x : i32) -> i32 { fn f(x : i32) -> i32 {
if ((x == 10)) { if ((x == 10)) {
discard; discard;
@ -10,7 +6,7 @@ fn f(x : i32) -> i32 {
} }
@stage(fragment) @stage(fragment)
fn main(@location(1) x : vec3<i32>) -> @location(2) i32 { fn main(@location(1) @interpolate(flat) x : vec3<i32>) -> @location(2) i32 {
var y = x.x; var y = x.x;
loop { loop {
let r = f(y); let r = f(y);

View File

@ -14,7 +14,7 @@ fn main_1() {
} }
struct main_out { struct main_out {
@location(0) @location(0) @interpolate(flat)
x_4_1 : u32; x_4_1 : u32;
@builtin(position) @builtin(position)
gl_Position : vec4<f32>; gl_Position : vec4<f32>;

View File

@ -14,7 +14,7 @@ fn main_1() {
struct main_out { struct main_out {
@builtin(position) @builtin(position)
gl_Position : vec4<f32>; gl_Position : vec4<f32>;
@location(0) @location(0) @interpolate(flat)
pos_1 : u32; pos_1 : u32;
}; };

View File

@ -14,7 +14,7 @@ fn main_1() {
} }
struct main_out { struct main_out {
@location(0) @location(0) @interpolate(flat)
x_4_1 : i32; x_4_1 : i32;
@builtin(position) @builtin(position)
gl_Position : vec4<f32>; gl_Position : vec4<f32>;

View File

@ -24,7 +24,7 @@ struct main_out {
}; };
@stage(fragment) @stage(fragment)
fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) x_3_param : i32) -> main_out { fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) @interpolate(flat) x_3_param : i32) -> main_out {
x_2 = x_2_param; x_2 = x_2_param;
x_3 = x_3_param; x_3 = x_3_param;
main_1(); main_1();

View File

@ -19,7 +19,7 @@ struct main_out {
}; };
@stage(fragment) @stage(fragment)
fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) x_3_param : i32) -> main_out { fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) @interpolate(flat) x_3_param : i32) -> main_out {
x_2 = x_2_param; x_2 = x_2_param;
x_3 = x_3_param; x_3 = x_3_param;
main_1(); main_1();

View File

@ -19,7 +19,7 @@ struct main_out {
}; };
@stage(fragment) @stage(fragment)
fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) x_3_param : i32) -> main_out { fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) @interpolate(flat) x_3_param : i32) -> main_out {
x_2 = x_2_param; x_2 = x_2_param;
x_3 = x_3_param; x_3 = x_3_param;
main_1(); main_1();

View File

@ -30,7 +30,7 @@ struct main_out {
}; };
@stage(fragment) @stage(fragment)
fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) x_3_param : i32) -> main_out { fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) @interpolate(flat) x_3_param : i32) -> main_out {
x_2 = x_2_param; x_2 = x_2_param;
x_3 = x_3_param; x_3 = x_3_param;
main_1(); main_1();

View File

@ -30,7 +30,7 @@ struct main_out {
}; };
@stage(fragment) @stage(fragment)
fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) x_3_param : i32) -> main_out { fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) @interpolate(flat) x_3_param : i32) -> main_out {
x_2 = x_2_param; x_2 = x_2_param;
x_3 = x_3_param; x_3 = x_3_param;
main_1(); main_1();

View File

@ -34,7 +34,7 @@ struct main_out {
}; };
@stage(fragment) @stage(fragment)
fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) x_3_param : i32) -> main_out { fn main(@builtin(position) x_2_param : vec4<f32>, @location(0) @interpolate(flat) x_3_param : i32) -> main_out {
x_2 = x_2_param; x_2 = x_2_param;
x_3 = x_3_param; x_3 = x_3_param;
main_1(); main_1();