Require vertex shaders to return builtin(position)

Fixup many tests that were just returning void.

Change-Id: Ic93db5b187c679dc1c24a356b48a64e41ba9a823
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/48560
Commit-Queue: James Price <jrprice@google.com>
Auto-Submit: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
James Price
2021-04-21 16:13:42 +00:00
committed by Commit Bot service account
parent 98c2cf0e91
commit 2dd393729c
26 changed files with 357 additions and 281 deletions

View File

@@ -94,7 +94,7 @@ TEST_P(FunctionReturnTypeDecorationTest, IsValid) {
Func("main", ast::VariableList{}, ty.f32(),
ast::StatementList{create<ast::ReturnStatement>(Expr(1.f))},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex)},
create<ast::StageDecoration>(ast::PipelineStage::kCompute)},
ast::DecorationList{createDecoration({}, *this, params.kind)});
if (params.should_pass) {

View File

@@ -32,7 +32,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Location) {
// [[stage(vertex)]]
// fn main() -> [[location(0)]] f32 { return 1.0; }
Func(Source{{12, 34}}, "main", {}, ty.f32(), {Return(Expr(1.0f))},
{Stage(ast::PipelineStage::kVertex)}, {Location(0)});
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
@@ -500,5 +500,17 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateLocation) {
12:34 note: while analysing entry point main)");
}
TEST_F(ResolverEntryPointValidationTest, VertexShaderMustReturnPosition) {
// [[stage(vertex)]]
// fn main() {}
Func(Source{{12, 34}}, "main", {}, ty.void_(), {},
{Stage(ast::PipelineStage::kVertex)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: a vertex shader must include the 'position' builtin "
"in its return type");
}
} // namespace
} // namespace tint

View File

@@ -47,7 +47,6 @@ TEST_F(ResolverFunctionValidationTest, FunctionNamesMustBeUnique_fail) {
TEST_F(ResolverFunctionValidationTest,
VoidFunctionEndWithoutReturnStatement_Pass) {
// [[stage(vertex)]]
// fn func { var a:i32 = 2; }
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
@@ -55,9 +54,6 @@ TEST_F(ResolverFunctionValidationTest,
ty.void_(),
ast::StatementList{
create<ast::VariableDeclStatement>(var),
},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -82,14 +78,10 @@ TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatement_Fail) {
TEST_F(ResolverFunctionValidationTest,
VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
// [[stage(vertex)]]
// fn func {}
Func(Source{Source::Location{12, 34}}, "func", ast::VariableList{},
ty.void_(), ast::StatementList{},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
ty.void_(), ast::StatementList{});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
@@ -109,15 +101,11 @@ TEST_F(ResolverFunctionValidationTest,
TEST_F(ResolverFunctionValidationTest,
FunctionTypeMustMatchReturnStatementType_Pass) {
// [[stage(vertex)]]
// fn func { return; }
Func("func", ast::VariableList{}, ty.void_(),
ast::StatementList{
create<ast::ReturnStatement>(),
},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -148,10 +136,6 @@ TEST_F(ResolverFunctionValidationTest,
Expr(2.f)),
},
ast::DecorationList{});
Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
@@ -183,10 +167,6 @@ TEST_F(ResolverFunctionValidationTest,
Expr(2.f)),
},
ast::DecorationList{});
Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
@@ -202,10 +182,6 @@ TEST_F(ResolverFunctionValidationTest,
Expr(2u)),
},
ast::DecorationList{});
Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),

View File

@@ -521,7 +521,8 @@ bool Resolver::ValidateParameter(const ast::Variable* param) {
return ValidateVariable(param);
}
bool Resolver::ValidateFunction(const ast::Function* func) {
bool Resolver::ValidateFunction(const ast::Function* func,
const FunctionInfo* info) {
if (symbol_to_function_.find(func->symbol()) != symbol_to_function_.end()) {
diagnostics_.add_error("v-0016",
"function names must be unique '" +
@@ -564,7 +565,7 @@ bool Resolver::ValidateFunction(const ast::Function* func) {
}
if (func->IsEntryPoint()) {
if (!ValidateEntryPoint(func)) {
if (!ValidateEntryPoint(func, info)) {
return false;
}
}
@@ -572,7 +573,8 @@ bool Resolver::ValidateFunction(const ast::Function* func) {
return true;
}
bool Resolver::ValidateEntryPoint(const ast::Function* func) {
bool Resolver::ValidateEntryPoint(const ast::Function* func,
const FunctionInfo* info) {
auto stage_deco_count = 0;
for (auto* deco : func->decorations()) {
if (deco->Is<ast::StageDecoration>()) {
@@ -750,9 +752,13 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func) {
}
}
// Clear IO sets after parameter validation. Builtin and location attributes
// in return types should be validated independently from those used in
// parameters.
builtins.clear();
locations.clear();
if (!func->return_type()->Is<sem::Void>()) {
builtins.clear();
locations.clear();
if (!validate_entry_point_decorations(func->return_type_decorations(),
func->return_type(), func->source(),
ParamOrRetType::kReturnType)) {
@@ -760,6 +766,28 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func) {
}
}
if (func->pipeline_stage() == ast::PipelineStage::kVertex &&
builtins.count(ast::Builtin::kPosition) == 0) {
// Check module-scope variables, as the SPIR-V sanitizer generates these.
bool found = false;
for (auto* var : info->referenced_module_vars) {
if (auto* builtin = ast::GetDecoration<ast::BuiltinDecoration>(
var->declaration->decorations())) {
if (builtin->value() == ast::Builtin::kPosition) {
found = true;
break;
}
}
}
if (!found) {
diagnostics_.add_error(
"a vertex shader must include the 'position' builtin in its return "
"type",
func->source());
return false;
}
}
return true;
}
@@ -863,7 +891,7 @@ bool Resolver::Function(ast::Function* func) {
Mark(deco);
}
if (!ValidateFunction(func)) {
if (!ValidateFunction(func, func_info)) {
return false;
}

View File

@@ -238,8 +238,8 @@ class Resolver {
// Each return true on success, false on failure.
bool ValidateAssignment(const ast::AssignmentStatement* a);
bool ValidateBinary(ast::BinaryExpression* expr);
bool ValidateEntryPoint(const ast::Function* func);
bool ValidateFunction(const ast::Function* func);
bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info);
bool ValidateFunction(const ast::Function* func, const FunctionInfo* info);
bool ValidateGlobalVariable(const VariableInfo* var);
bool ValidateMatrixConstructor(const sem::Matrix* matrix_type,
const ast::ExpressionList& values);

View File

@@ -1532,7 +1532,7 @@ TEST_F(ResolverTest, Function_EntryPoints_StageDecoration) {
create<ast::AssignmentStatement>(Expr("call_b"), Call("b")),
},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
create<ast::StageDecoration>(ast::PipelineStage::kCompute),
});
auto* ep_2 =
@@ -1541,7 +1541,7 @@ TEST_F(ResolverTest, Function_EntryPoints_StageDecoration) {
create<ast::AssignmentStatement>(Expr("call_c"), Call("c")),
},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
create<ast::StageDecoration>(ast::PipelineStage::kCompute),
});
ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -1620,7 +1620,7 @@ TEST_F(ResolverTest, Function_EntryPoints_LinearTime) {
create<ast::CallStatement>(Call(fn_b(0))),
},
{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
create<ast::StageDecoration>(ast::PipelineStage::kCompute),
});
ASSERT_TRUE(r()->Resolve()) << r()->error();

View File

@@ -65,11 +65,12 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsNonEntryPointReturnType) {
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderParam) {
auto* s = Structure(
"S", {Member("a", ty.f32(), {create<ast::LocationDecoration>(0)})});
auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
Func("main", {Param("param", s)}, ty.void_(), {},
{create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
Func("main", {Param("param", s)}, ty.vec4<f32>(),
{Return(Construct(ty.vec4<f32>()))},
{Stage(ast::PipelineStage::kVertex)},
{Builtin(ast::Builtin::kPosition)});
ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -81,10 +82,10 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderParam) {
TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderReturnType) {
auto* s = Structure(
"S", {Member("a", ty.f32(), {create<ast::LocationDecoration>(0)})});
"S", {Member("a", ty.f32(), {Builtin(ast::Builtin::kPosition)})});
Func("main", {}, s, {Return(Construct(s, Expr(0.f)))},
{create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
{Stage(ast::PipelineStage::kVertex)});
ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -142,13 +143,13 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsComputeShaderParam) {
TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) {
auto* s = Structure(
"S", {Member("a", ty.f32(), {create<ast::LocationDecoration>(0)})});
"S", {Member("a", ty.f32(), {Builtin(ast::Builtin::kPosition)})});
Func("vert_main", {Param("param", s)}, s, {Return(Construct(s, Expr(0.f)))},
{create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
{Stage(ast::PipelineStage::kVertex)});
Func("frag_main", {Param("param", s)}, ty.void_(), {},
{create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
{Stage(ast::PipelineStage::kFragment)});
ASSERT_TRUE(r()->Resolve()) << r()->error();

View File

@@ -112,9 +112,7 @@ TEST_F(ResolverTypeValidationTest,
auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
Func("my_func", ast::VariableList{}, ty.void_(), {Decl(var)},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
Func("my_func", ast::VariableList{}, ty.void_(), {Decl(var)});
Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
@@ -277,9 +275,6 @@ TEST_F(ResolverTypeValidationTest,
ast::StatementList{
create<ast::VariableDeclStatement>(Source{{13, 34}}, var1),
create<ast::ReturnStatement>(),
},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();

View File

@@ -280,9 +280,6 @@ TEST_F(ResolverValidationTest, UsingUndefinedVariableGlobalVariable_Pass) {
create<ast::AssignmentStatement>(Source{Source::Location{12, 34}},
Expr("global_var"), Expr(3.14f)),
create<ast::ReturnStatement>(),
},
ast::DecorationList{
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();