[validator] Remove requirement to have an entry point
The SPIR-V and HLSL sanitizing transforms add an empty one if necessary. Fixed: tint:679 Change-Id: Ic98ff3109d7381b1fbc2de68d95d57e15c7a67c0 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/46700 Commit-Queue: Ben Clayton <bclayton@google.com> Auto-Submit: James Price <jrprice@google.com> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
1fe1a5e641
commit
2f04dc94ce
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "src/ast/stage_decoration.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/semantic/expression.h"
|
||||
|
@ -32,6 +33,7 @@ Transform::Output Hlsl::Run(const Program* in, const DataMap&) {
|
|||
ProgramBuilder out;
|
||||
CloneContext ctx(&out, in);
|
||||
PromoteArrayInitializerToConstVar(ctx);
|
||||
AddEmptyEntryPoint(ctx);
|
||||
ctx.Clone();
|
||||
return Output{Program(std::move(out))};
|
||||
}
|
||||
|
@ -105,5 +107,16 @@ void Hlsl::PromoteArrayInitializerToConstVar(CloneContext& ctx) const {
|
|||
}
|
||||
}
|
||||
|
||||
void Hlsl::AddEmptyEntryPoint(CloneContext& ctx) const {
|
||||
for (auto* func : ctx.src->AST().Functions()) {
|
||||
if (func->IsEntryPoint()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ctx.dst->Func(
|
||||
"_tint_unused_entry_point", {}, ctx.dst->ty.void_(), {},
|
||||
{ctx.dst->create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
|
||||
}
|
||||
|
||||
} // namespace transform
|
||||
} // namespace tint
|
||||
|
|
|
@ -44,6 +44,8 @@ class Hlsl : public Transform {
|
|||
/// the array usage statement.
|
||||
/// See crbug.com/tint/406 for more details
|
||||
void PromoteArrayInitializerToConstVar(CloneContext& ctx) const;
|
||||
/// Add an empty shader entry point if none exist in the module.
|
||||
void AddEmptyEntryPoint(CloneContext& ctx) const;
|
||||
};
|
||||
|
||||
} // namespace transform
|
||||
|
|
|
@ -143,6 +143,20 @@ fn main() -> void {
|
|||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(HlslTest, AddEmptyEntryPoint) {
|
||||
auto* src = R"()";
|
||||
|
||||
auto* expect = R"(
|
||||
[[stage(vertex)]]
|
||||
fn _tint_unused_entry_point() -> void {
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<Hlsl>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace transform
|
||||
} // namespace tint
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "src/ast/call_statement.h"
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/stage_decoration.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/semantic/function.h"
|
||||
#include "src/semantic/statement.h"
|
||||
|
@ -42,6 +43,7 @@ Transform::Output Spirv::Run(const Program* in, const DataMap&) {
|
|||
ProgramBuilder out2;
|
||||
CloneContext ctx2(&out2, &tmp);
|
||||
HandleSampleMaskBuiltins(ctx2);
|
||||
AddEmptyEntryPoint(ctx2);
|
||||
ctx2.Clone();
|
||||
|
||||
return Output{Program(std::move(out2))};
|
||||
|
@ -234,6 +236,17 @@ void Spirv::HandleSampleMaskBuiltins(CloneContext& ctx) const {
|
|||
}
|
||||
}
|
||||
|
||||
void Spirv::AddEmptyEntryPoint(CloneContext& ctx) const {
|
||||
for (auto* func : ctx.src->AST().Functions()) {
|
||||
if (func->IsEntryPoint()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ctx.dst->Func(
|
||||
"_tint_unused_entry_point", {}, ctx.dst->ty.void_(), {},
|
||||
{ctx.dst->create<ast::StageDecoration>(ast::PipelineStage::kCompute)});
|
||||
}
|
||||
|
||||
Symbol Spirv::HoistToInputVariables(
|
||||
CloneContext& ctx,
|
||||
const ast::Function* func,
|
||||
|
|
|
@ -47,6 +47,8 @@ class Spirv : public Transform {
|
|||
void HandleEntryPointIOTypes(CloneContext& ctx) const;
|
||||
/// Change type of sample mask builtin variables to single element arrays.
|
||||
void HandleSampleMaskBuiltins(CloneContext& ctx) const;
|
||||
/// Add an empty shader entry point if none exist in the module.
|
||||
void AddEmptyEntryPoint(CloneContext& ctx) const;
|
||||
|
||||
/// Recursively create module-scope input variables for `ty` and add
|
||||
/// function-scope variables for structs to `func`.
|
||||
|
|
|
@ -833,6 +833,20 @@ fn main() -> void {
|
|||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(SpirvTest, AddEmptyEntryPoint) {
|
||||
auto* src = R"()";
|
||||
|
||||
auto* expect = R"(
|
||||
[[stage(compute)]]
|
||||
fn _tint_unused_entry_point() -> void {
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<Spirv>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
// Test that different transforms within the sanitizer interact correctly.
|
||||
TEST_F(SpirvTest, MultipleTransforms) {
|
||||
auto* src = R"(
|
||||
|
|
|
@ -78,25 +78,7 @@ TEST_F(ValidateFunctionTest, PipelineStage_MustBeUnique_Fail) {
|
|||
"12:34 v-0020: only one stage decoration permitted per entry point");
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Pass) {
|
||||
// [[stage(vertex)]]
|
||||
// fn vtx_func() -> void { return; }
|
||||
|
||||
Func("vtx_func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(),
|
||||
},
|
||||
ast::DecorationList{
|
||||
create<ast::StageDecoration>(ast::PipelineStage::kVertex),
|
||||
});
|
||||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_TRUE(v.Validate()) << v.error();
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Fail) {
|
||||
// fn vtx_func() -> void { return; }
|
||||
TEST_F(ValidateFunctionTest, NoPipelineEntryPoints) {
|
||||
Func("vtx_func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{
|
||||
create<ast::ReturnStatement>(),
|
||||
|
@ -105,10 +87,7 @@ TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Fail) {
|
|||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_FALSE(v.Validate());
|
||||
EXPECT_EQ(v.error(),
|
||||
"v-0003: At least one of vertex, fragment or compute shader must "
|
||||
"be present");
|
||||
EXPECT_TRUE(v.Validate()) << v.error();
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionTest, FunctionVarInitWithParam) {
|
||||
|
|
|
@ -138,10 +138,8 @@ bool ValidatorImpl::ValidateGlobalVariable(const ast::Variable* var) {
|
|||
}
|
||||
|
||||
bool ValidatorImpl::ValidateEntryPoint(const ast::FunctionList& funcs) {
|
||||
auto shader_is_present = false;
|
||||
for (auto* func : funcs) {
|
||||
if (func->IsEntryPoint()) {
|
||||
shader_is_present = true;
|
||||
auto stage_deco_count = 0;
|
||||
for (auto* deco : func->decorations()) {
|
||||
if (deco->Is<ast::StageDecoration>()) {
|
||||
|
@ -158,12 +156,6 @@ bool ValidatorImpl::ValidateEntryPoint(const ast::FunctionList& funcs) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!shader_is_present) {
|
||||
add_error(Source{}, "v-0003",
|
||||
"At least one of vertex, fragment or compute shader must "
|
||||
"be present");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ TEST_F(ValidatorTest, GlobalConstNoStorageClass_Pass) {
|
|||
|
||||
ValidatorImpl& v = Build();
|
||||
|
||||
EXPECT_FALSE(v.Validate()) << v.error();
|
||||
EXPECT_TRUE(v.Validate()) << v.error();
|
||||
}
|
||||
|
||||
TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {
|
||||
|
|
Loading…
Reference in New Issue