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>
diff --git a/src/inspector/entry_point.h b/src/inspector/entry_point.h
index c96017a..167702d 100644
--- a/src/inspector/entry_point.h
+++ b/src/inspector/entry_point.h
@@ -124,6 +124,13 @@
   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() {
diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc
index de1f65e..91ef621 100644
--- a/src/inspector/inspector.cc
+++ b/src/inspector/inspector.cc
@@ -160,16 +160,27 @@
           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 @@
   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;
   }
 
diff --git a/src/inspector/inspector.h b/src/inspector/inspector.h
index ce8ce63..2005640 100644
--- a/src/inspector/inspector.h
+++ b/src/inspector/inspector.h
@@ -166,11 +166,12 @@
                                    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.
diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc
index 6a886d8..6a8c946 100644
--- a/src/inspector/inspector_test.cc
+++ b/src/inspector/inspector_test.cc
@@ -629,7 +629,7 @@
   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 @@
 
   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 @@
   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)}));