Inspector: Add all fragment builtin inputs to EntryPoint

This patch adds all the fragment builtin inputs (position, front_face,
sample_index) to EntryPoint for the validation on the total number of
fragment inputs. According to Vulkan SPEC: "All variables in both the
built-in interface block and the user-defined variable interface count
against these limits".

BUG=dawn:802

Change-Id: I8a8503c1a33646b50f010c6b6e38d74de9a40ff5
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/59421
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
This commit is contained in:
Jiawei Shao 2021-08-05 00:22:38 +00:00 committed by Tint LUCI CQ
parent a5d73ce965
commit b5025dbc82
4 changed files with 134 additions and 15 deletions

View File

@ -124,6 +124,13 @@ struct EntryPoint {
std::vector<OverridableConstant> overridable_constants;
/// Does the entry point use the sample_mask builtin
bool sample_mask_used = false;
/// Does the entry point use the position builtin as an input builtin
/// variable.
bool input_position_used = false;
/// Does the entry point use the front_facing builtin
bool front_facing_used = false;
/// Does the entry point use the sample_index builtin
bool sample_index_used = false;
/// @returns the size of the workgroup in {x,y,z} format
std::tuple<uint32_t, uint32_t, uint32_t> workgroup_size() {

View File

@ -160,16 +160,27 @@ std::vector<EntryPoint> Inspector::GetEntryPoints() {
program_->Symbols().NameFor(param->Declaration()->symbol()),
param->Type(), param->Declaration()->decorations(),
entry_point.input_variables);
entry_point.input_position_used |=
ContainsBuiltin(ast::Builtin::kPosition, param->Type(),
param->Declaration()->decorations());
entry_point.front_facing_used |=
ContainsBuiltin(ast::Builtin::kFrontFacing, param->Type(),
param->Declaration()->decorations());
entry_point.sample_index_used |=
ContainsBuiltin(ast::Builtin::kSampleIndex, param->Type(),
param->Declaration()->decorations());
}
if (!sem->ReturnType()->Is<sem::Void>()) {
AddEntryPointInOutVariables("<retval>", sem->ReturnType(),
func->return_type_decorations(),
entry_point.output_variables);
}
entry_point.sample_mask_used = ContainsSampleMaskBuiltin(
sem->ReturnType(), func->return_type_decorations());
entry_point.sample_mask_used =
ContainsBuiltin(ast::Builtin::kSampleMask, sem->ReturnType(),
func->return_type_decorations());
}
for (auto* var : sem->ReferencedModuleVariables()) {
auto* decl = var->Declaration();
@ -609,25 +620,26 @@ void Inspector::AddEntryPointInOutVariables(
variables.push_back(stage_variable);
}
bool Inspector::ContainsSampleMaskBuiltin(
sem::Type* type,
const ast::DecorationList& decorations) const {
bool Inspector::ContainsBuiltin(ast::Builtin builtin,
sem::Type* type,
const ast::DecorationList& decorations) const {
auto* unwrapped_type = type->UnwrapRef();
if (auto* struct_ty = unwrapped_type->As<sem::Struct>()) {
// Recurse into members.
for (auto* member : struct_ty->Members()) {
if (ContainsSampleMaskBuiltin(member->Type(),
member->Declaration()->decorations())) {
if (ContainsBuiltin(builtin, member->Type(),
member->Declaration()->decorations())) {
return true;
}
}
return false;
}
// Base case: check for [[builtin(sample_mask)]]
auto* builtin = ast::GetDecoration<ast::BuiltinDecoration>(decorations);
if (!builtin || builtin->value() != ast::Builtin::kSampleMask) {
// Base case: check for builtin
auto* builtin_declaration =
ast::GetDecoration<ast::BuiltinDecoration>(decorations);
if (!builtin_declaration || builtin_declaration->value() != builtin) {
return false;
}

View File

@ -166,11 +166,12 @@ class Inspector {
const ast::DecorationList& decorations,
std::vector<StageVariable>& variables) const;
/// Recursively determine if the type contains [[builtin(sample_mask)]]
/// Recursively determine if the type contains builtin.
/// If `type` is a struct, recurse into members to check for the decoration.
/// Otherwise, check `decorations` for the decoration.
bool ContainsSampleMaskBuiltin(sem::Type* type,
const ast::DecorationList& decorations) const;
bool ContainsBuiltin(ast::Builtin builtin,
sem::Type* type,
const ast::DecorationList& decorations) const;
/// Gathers all the texture resource bindings of the given type for the given
/// entry point.

View File

@ -629,7 +629,7 @@ TEST_F(InspectorGetEntryPointTest, NonOverridableConstantSkipped) {
EXPECT_EQ(0u, result[0].overridable_constants.size());
}
TEST_F(InspectorGetEntryPointTest, SampleMaskNotReferenced) {
TEST_F(InspectorGetEntryPointTest, BuiltinNotReferenced) {
MakeEmptyBodyFunction("ep_func", {Stage(ast::PipelineStage::kFragment)});
Inspector& inspector = Build();
@ -638,6 +638,9 @@ TEST_F(InspectorGetEntryPointTest, SampleMaskNotReferenced) {
ASSERT_EQ(1u, result.size());
EXPECT_FALSE(result[0].sample_mask_used);
EXPECT_FALSE(result[0].input_position_used);
EXPECT_FALSE(result[0].front_facing_used);
EXPECT_FALSE(result[0].sample_index_used);
}
TEST_F(InspectorGetEntryPointTest, SampleMaskSimpleReferenced) {
@ -673,6 +676,102 @@ TEST_F(InspectorGetEntryPointTest, SampleMaskStructReferenced) {
EXPECT_TRUE(result[0].sample_mask_used);
}
TEST_F(InspectorGetEntryPointTest, InputPositionSimpleReferenced) {
auto* in_var =
Param("in_var", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)});
Func("ep_func", {in_var}, ty.void_(), {Return()},
{Stage(ast::PipelineStage::kFragment)}, {});
Inspector& inspector = Build();
auto result = inspector.GetEntryPoints();
ASSERT_EQ(1u, result.size());
EXPECT_TRUE(result[0].input_position_used);
}
TEST_F(InspectorGetEntryPointTest, InputPositionStructReferenced) {
ast::StructMemberList members;
members.push_back(Member("inner_position", ty.vec4<f32>(),
{Builtin(ast::Builtin::kPosition)}));
Structure("in_struct", members, {});
auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
Func("ep_func", {in_var}, ty.void_(), {Return()},
{Stage(ast::PipelineStage::kFragment)}, {});
Inspector& inspector = Build();
auto result = inspector.GetEntryPoints();
ASSERT_EQ(1u, result.size());
EXPECT_TRUE(result[0].input_position_used);
}
TEST_F(InspectorGetEntryPointTest, FrontFacingSimpleReferenced) {
auto* in_var =
Param("in_var", ty.bool_(), {Builtin(ast::Builtin::kFrontFacing)});
Func("ep_func", {in_var}, ty.void_(), {Return()},
{Stage(ast::PipelineStage::kFragment)}, {});
Inspector& inspector = Build();
auto result = inspector.GetEntryPoints();
ASSERT_EQ(1u, result.size());
EXPECT_TRUE(result[0].front_facing_used);
}
TEST_F(InspectorGetEntryPointTest, FrontFacingStructReferenced) {
ast::StructMemberList members;
members.push_back(Member("inner_position", ty.bool_(),
{Builtin(ast::Builtin::kFrontFacing)}));
Structure("in_struct", members, {});
auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
Func("ep_func", {in_var}, ty.void_(), {Return()},
{Stage(ast::PipelineStage::kFragment)}, {});
Inspector& inspector = Build();
auto result = inspector.GetEntryPoints();
ASSERT_EQ(1u, result.size());
EXPECT_TRUE(result[0].front_facing_used);
}
TEST_F(InspectorGetEntryPointTest, SampleIndexSimpleReferenced) {
auto* in_var =
Param("in_var", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
Func("ep_func", {in_var}, ty.void_(), {Return()},
{Stage(ast::PipelineStage::kFragment)}, {});
Inspector& inspector = Build();
auto result = inspector.GetEntryPoints();
ASSERT_EQ(1u, result.size());
EXPECT_TRUE(result[0].sample_index_used);
}
TEST_F(InspectorGetEntryPointTest, SampleIndexStructReferenced) {
ast::StructMemberList members;
members.push_back(Member("inner_position", ty.u32(),
{Builtin(ast::Builtin::kSampleIndex)}));
Structure("in_struct", members, {});
auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
Func("ep_func", {in_var}, ty.void_(), {Return()},
{Stage(ast::PipelineStage::kFragment)}, {});
Inspector& inspector = Build();
auto result = inspector.GetEntryPoints();
ASSERT_EQ(1u, result.size());
EXPECT_TRUE(result[0].sample_index_used);
}
TEST_F(InspectorGetEntryPointTest, ImplicitInterpolate) {
ast::StructMemberList members;
members.push_back(Member("struct_inner", ty.f32(), {Location(0)}));