spirv-val: Add Vulkan EXT builtins (#4115)

diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 7fb9908..3c9df9f 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -120,7 +120,7 @@
   VUIDErrorMax,
 } VUIDError;
 
-const static uint32_t NumVUIDBuiltins = 29;
+const static uint32_t NumVUIDBuiltins = 33;
 
 typedef struct {
   SpvBuiltIn builtIn;
@@ -158,6 +158,10 @@
     {SpvBuiltInWorldRayOriginKHR,         {4431, 4432, 4433}},
     {SpvBuiltInLaunchIdKHR,               {4266, 4267, 4268}},
     {SpvBuiltInLaunchSizeKHR,             {4269, 4270, 4271}},
+    {SpvBuiltInFragInvocationCountEXT,    {4217, 4218, 4219}},
+    {SpvBuiltInFragSizeEXT,               {4220, 4221, 4222}},
+    {SpvBuiltInFragStencilRefEXT,         {4223, 4224, 4225}},
+    {SpvBuiltInFullyCoveredEXT,           {4232, 4233, 4234}},
     // clang-format off
 } };
 
@@ -314,6 +318,14 @@
                                              const Instruction& inst);
   spv_result_t ValidateDeviceIndexAtDefinition(const Decoration& decoration,
                                                const Instruction& inst);
+  spv_result_t ValidateFragInvocationCountAtDefinition(const Decoration& decoration,
+                                               const Instruction& inst);
+  spv_result_t ValidateFragSizeAtDefinition(const Decoration& decoration,
+                                               const Instruction& inst);
+  spv_result_t ValidateFragStencilRefAtDefinition(const Decoration& decoration,
+                                               const Instruction& inst);
+  spv_result_t ValidateFullyCoveredAtDefinition(const Decoration& decoration,
+                                               const Instruction& inst);
   // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
   spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition(
       const Decoration& decoration, const Instruction& inst);
@@ -472,6 +484,26 @@
       const Instruction& referenced_inst,
       const Instruction& referenced_from_inst);
 
+  spv_result_t ValidateFragInvocationCountAtReference(
+      const Decoration& decoration, const Instruction& built_in_inst,
+      const Instruction& referenced_inst,
+      const Instruction& referenced_from_inst);
+
+  spv_result_t ValidateFragSizeAtReference(
+      const Decoration& decoration, const Instruction& built_in_inst,
+      const Instruction& referenced_inst,
+      const Instruction& referenced_from_inst);
+
+  spv_result_t ValidateFragStencilRefAtReference(
+      const Decoration& decoration, const Instruction& built_in_inst,
+      const Instruction& referenced_inst,
+      const Instruction& referenced_from_inst);
+
+  spv_result_t ValidateFullyCoveredAtReference(
+      const Decoration& decoration, const Instruction& built_in_inst,
+      const Instruction& referenced_inst,
+      const Instruction& referenced_from_inst);
+
   // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
   spv_result_t ValidateComputeShaderI32Vec3InputAtReference(
       const Decoration& decoration, const Instruction& built_in_inst,
@@ -525,6 +557,9 @@
   spv_result_t ValidateBool(
       const Decoration& decoration, const Instruction& inst,
       const std::function<spv_result_t(const std::string& message)>& diag);
+  spv_result_t ValidateI(
+      const Decoration& decoration, const Instruction& inst,
+      const std::function<spv_result_t(const std::string& message)>& diag);
   spv_result_t ValidateI32(
       const Decoration& decoration, const Instruction& inst,
       const std::function<spv_result_t(const std::string& message)>& diag);
@@ -717,6 +752,22 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t BuiltInsValidator::ValidateI(
+    const Decoration& decoration, const Instruction& inst,
+    const std::function<spv_result_t(const std::string& message)>& diag) {
+  uint32_t underlying_type = 0;
+  if (spv_result_t error =
+          GetUnderlyingType(_, decoration, inst, &underlying_type)) {
+    return error;
+  }
+
+  if (!_.IsIntScalarType(underlying_type)) {
+    return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar.");
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t BuiltInsValidator::ValidateI32(
     const Decoration& decoration, const Instruction& inst,
     const std::function<spv_result_t(const std::string& message)>& diag) {
@@ -3296,6 +3347,287 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtDefinition(const Decoration& decoration,
+                                            const Instruction& inst) {
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    if (spv_result_t error = ValidateI32(
+            decoration, inst,
+            [this, &inst, &builtin](const std::string& message) -> spv_result_t {
+              uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(vuid) << "According to the "
+                     << spvLogStringForEnv(_.context()->target_env)
+                     << " spec BuiltIn "
+                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+                                                      builtin)
+                     << " variable needs to be a 32-bit int scalar. "
+                     << message;
+            })) {
+      return error;
+    }
+  }
+
+  return ValidateFragInvocationCountAtReference(decoration, inst, inst, inst);
+}
+
+spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtReference(
+    const Decoration& decoration, const Instruction& built_in_inst,
+    const Instruction& referenced_inst,
+    const Instruction& referenced_from_inst) {
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != SpvStorageClassMax &&
+        storage_class != SpvStorageClassInput) {
+      uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
+      return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+             << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
+             << " spec allows BuiltIn "
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << " to be only used for variables with Input storage class. "
+             << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                 referenced_from_inst)
+             << " " << GetStorageClassDesc(referenced_from_inst);
+    }
+
+    for (const SpvExecutionModel execution_model : execution_models_) {
+      if (execution_model != SpvExecutionModelFragment) {
+        uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
+        return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+               << _.VkErrorID(vuid)
+               << spvLogStringForEnv(_.context()->target_env)
+               << " spec allows BuiltIn "
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << " to be used only with Fragment execution model. "
+               << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                   referenced_from_inst, execution_model);
+      }
+    }
+  }
+
+  if (function_id_ == 0) {
+    // Propagate this rule to all dependant ids in the global scope.
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+        &BuiltInsValidator::ValidateFragInvocationCountAtReference, this, decoration,
+        built_in_inst, referenced_from_inst, std::placeholders::_1));
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t BuiltInsValidator::ValidateFragSizeAtDefinition(const Decoration& decoration,
+                                            const Instruction& inst) {
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    if (spv_result_t error = ValidateI32Vec(
+            decoration, inst, 2,
+            [this, &inst, &builtin](const std::string& message) -> spv_result_t {
+              uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(vuid) << "According to the "
+                     << spvLogStringForEnv(_.context()->target_env)
+                     << " spec BuiltIn "
+                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+                                                      builtin)
+                     << " variable needs to be a 2-component 32-bit int vector. "
+                     << message;
+            })) {
+      return error;
+    }
+  }
+
+  return ValidateFragSizeAtReference(decoration, inst, inst, inst);
+}
+
+spv_result_t BuiltInsValidator::ValidateFragSizeAtReference(
+    const Decoration& decoration, const Instruction& built_in_inst,
+    const Instruction& referenced_inst,
+    const Instruction& referenced_from_inst) {
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != SpvStorageClassMax &&
+        storage_class != SpvStorageClassInput) {
+      uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
+      return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+             << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
+             << " spec allows BuiltIn "
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << " to be only used for variables with Input storage class. "
+             << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                 referenced_from_inst)
+             << " " << GetStorageClassDesc(referenced_from_inst);
+    }
+
+    for (const SpvExecutionModel execution_model : execution_models_) {
+      if (execution_model != SpvExecutionModelFragment) {
+        uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
+        return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+               << _.VkErrorID(vuid)
+               << spvLogStringForEnv(_.context()->target_env)
+               << " spec allows BuiltIn "
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << " to be used only with Fragment execution model. "
+               << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                   referenced_from_inst, execution_model);
+      }
+    }
+  }
+
+  if (function_id_ == 0) {
+    // Propagate this rule to all dependant ids in the global scope.
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+        &BuiltInsValidator::ValidateFragSizeAtReference, this, decoration,
+        built_in_inst, referenced_from_inst, std::placeholders::_1));
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t BuiltInsValidator::ValidateFragStencilRefAtDefinition(const Decoration& decoration,
+                                            const Instruction& inst) {
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    if (spv_result_t error = ValidateI(
+            decoration, inst,
+            [this, &inst, &builtin](const std::string& message) -> spv_result_t {
+              uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(vuid) << "According to the "
+                     << spvLogStringForEnv(_.context()->target_env)
+                     << " spec BuiltIn "
+                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+                                                      builtin)
+                     << " variable needs to be a int scalar. "
+                     << message;
+            })) {
+      return error;
+    }
+  }
+
+  return ValidateFragStencilRefAtReference(decoration, inst, inst, inst);
+}
+
+spv_result_t BuiltInsValidator::ValidateFragStencilRefAtReference(
+    const Decoration& decoration, const Instruction& built_in_inst,
+    const Instruction& referenced_inst,
+    const Instruction& referenced_from_inst) {
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != SpvStorageClassMax &&
+        storage_class != SpvStorageClassOutput) {
+      uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
+      return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+             << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
+             << " spec allows BuiltIn "
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << " to be only used for variables with Output storage class. "
+             << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                 referenced_from_inst)
+             << " " << GetStorageClassDesc(referenced_from_inst);
+    }
+
+    for (const SpvExecutionModel execution_model : execution_models_) {
+      if (execution_model != SpvExecutionModelFragment) {
+        uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
+        return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+               << _.VkErrorID(vuid)
+               << spvLogStringForEnv(_.context()->target_env)
+               << " spec allows BuiltIn "
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << " to be used only with Fragment execution model. "
+               << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                   referenced_from_inst, execution_model);
+      }
+    }
+  }
+
+  if (function_id_ == 0) {
+    // Propagate this rule to all dependant ids in the global scope.
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+        &BuiltInsValidator::ValidateFragStencilRefAtReference, this, decoration,
+        built_in_inst, referenced_from_inst, std::placeholders::_1));
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t BuiltInsValidator::ValidateFullyCoveredAtDefinition(const Decoration& decoration,
+                                               const Instruction& inst) {
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    if (spv_result_t error = ValidateBool(
+            decoration, inst,
+            [this, &inst, &builtin](const std::string& message) -> spv_result_t {
+              uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(vuid) << "According to the "
+                     << spvLogStringForEnv(_.context()->target_env)
+                     << " spec BuiltIn "
+                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+                                                      builtin)
+                     << " variable needs to be a bool scalar. "
+                     << message;
+            })) {
+      return error;
+    }
+  }
+
+  return ValidateFullyCoveredAtReference(decoration, inst, inst, inst);
+}
+
+spv_result_t BuiltInsValidator::ValidateFullyCoveredAtReference(
+    const Decoration& decoration, const Instruction& built_in_inst,
+    const Instruction& referenced_inst,
+    const Instruction& referenced_from_inst) {
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != SpvStorageClassMax &&
+        storage_class != SpvStorageClassInput) {
+      uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
+      return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+             << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
+             << " spec allows BuiltIn "
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << " to be only used for variables with Input storage class. "
+             << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                 referenced_from_inst)
+             << " " << GetStorageClassDesc(referenced_from_inst);
+    }
+
+    for (const SpvExecutionModel execution_model : execution_models_) {
+      if (execution_model != SpvExecutionModelFragment) {
+        uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
+        return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+               << _.VkErrorID(vuid)
+               << spvLogStringForEnv(_.context()->target_env)
+               << " spec allows BuiltIn "
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << " to be used only with Fragment execution model. "
+               << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                   referenced_from_inst, execution_model);
+      }
+    }
+  }
+
+  if (function_id_ == 0) {
+    // Propagate this rule to all dependant ids in the global scope.
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+        &BuiltInsValidator::ValidateFullyCoveredAtReference, this, decoration,
+        built_in_inst, referenced_from_inst, std::placeholders::_1));
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
@@ -3793,6 +4125,20 @@
     case SpvBuiltInDeviceIndex: {
       return ValidateDeviceIndexAtDefinition(decoration, inst);
     }
+    case SpvBuiltInFragInvocationCountEXT: {
+      // alias SpvBuiltInInvocationsPerPixelNV
+      return ValidateFragInvocationCountAtDefinition(decoration, inst);
+    }
+    case SpvBuiltInFragSizeEXT: {
+      // alias SpvBuiltInFragmentSizeNV
+      return ValidateFragSizeAtDefinition(decoration, inst);
+    }
+    case SpvBuiltInFragStencilRefEXT: {
+      return ValidateFragStencilRefAtDefinition(decoration, inst);
+    }
+    case SpvBuiltInFullyCoveredEXT:{
+      return ValidateFullyCoveredAtDefinition(decoration, inst);
+    }
     // Ray tracing builtins
     case SpvBuiltInHitKindKHR:  // alias SpvBuiltInHitKindNV
     case SpvBuiltInHitTNV:      // NOT present in KHR
@@ -3828,13 +4174,11 @@
     case SpvBuiltInBaryCoordSmoothCentroidAMD:
     case SpvBuiltInBaryCoordSmoothSampleAMD:
     case SpvBuiltInBaryCoordPullModelAMD:
-    case SpvBuiltInFragStencilRefEXT:
     case SpvBuiltInViewportMaskNV:
     case SpvBuiltInSecondaryPositionNV:
     case SpvBuiltInSecondaryViewportMaskNV:
     case SpvBuiltInPositionPerViewNV:
     case SpvBuiltInViewportMaskPerViewNV:
-    case SpvBuiltInFullyCoveredEXT:
     case SpvBuiltInMax:
     case SpvBuiltInTaskCountNV:
     case SpvBuiltInPrimitiveCountNV:
@@ -3846,9 +4190,6 @@
     case SpvBuiltInMeshViewIndicesNV:
     case SpvBuiltInBaryCoordNV:
     case SpvBuiltInBaryCoordNoPerspNV:
-    case SpvBuiltInFragmentSizeNV:         // alias SpvBuiltInFragSizeEXT
-    case SpvBuiltInInvocationsPerPixelNV:  // alias
-                                           // SpvBuiltInFragInvocationCountEXT
       // No validation rules (for the moment).
       break;
 
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 92b51c7..b9269db 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1340,12 +1340,36 @@
       return VUID_WRAP(VUID-FragDepth-FragDepth-04215);
     case 4216:
       return VUID_WRAP(VUID-FragDepth-FragDepth-04216);
+    case 4217:
+      return VUID_WRAP(VUID-FragInvocationCountEXT-FragInvocationCountEXT-04217);
+    case 4218:
+      return VUID_WRAP(VUID-FragInvocationCountEXT-FragInvocationCountEXT-04218);
+    case 4219:
+      return VUID_WRAP(VUID-FragInvocationCountEXT-FragInvocationCountEXT-04219);
+    case 4220:
+      return VUID_WRAP(VUID-FragSizeEXT-FragSizeEXT-04220);
+    case 4221:
+      return VUID_WRAP(VUID-FragSizeEXT-FragSizeEXT-04221);
+    case 4222:
+      return VUID_WRAP(VUID-FragSizeEXT-FragSizeEXT-04222);
+    case 4223:
+      return VUID_WRAP(VUID-FragStencilRefEXT-FragStencilRefEXT-04223);
+    case 4224:
+      return VUID_WRAP(VUID-FragStencilRefEXT-FragStencilRefEXT-04224);
+    case 4225:
+      return VUID_WRAP(VUID-FragStencilRefEXT-FragStencilRefEXT-04225);
     case 4229:
       return VUID_WRAP(VUID-FrontFacing-FrontFacing-04229);
     case 4230:
       return VUID_WRAP(VUID-FrontFacing-FrontFacing-04230);
     case 4231:
       return VUID_WRAP(VUID-FrontFacing-FrontFacing-04231);
+    case 4232:
+      return VUID_WRAP(VUID-FullyCoveredEXT-FullyCoveredEXT-04232);
+    case 4233:
+      return VUID_WRAP(VUID-FullyCoveredEXT-FullyCoveredEXT-04233);
+    case 4234:
+      return VUID_WRAP(VUID-FullyCoveredEXT-FullyCoveredEXT-04234);
     case 4236:
       return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04236);
     case 4237:
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index 66e73be..bbcdbb1 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -3941,6 +3941,184 @@
         Values(TestResult(SPV_ERROR_INVALID_DATA,
                           "According to the Vulkan spec BuiltIn ShadingRateKHR "
                           "variable needs to be a 32-bit int scalar."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragInvocationCountInputSuccess,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragInvocationCountEXT"), Values("Fragment"),
+            Values("Input"), Values("%u32"),
+            Values("OpCapability FragmentDensityEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"),
+            Values(nullptr), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragInvocationCountInvalidExecutionModel,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(
+        Values("FragInvocationCountEXT"), Values("Vertex"), Values("Input"),
+        Values("%u32"), Values("OpCapability FragmentDensityEXT\n"),
+        Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"),
+        Values("VUID-FragInvocationCountEXT-FragInvocationCountEXT-04217"),
+        Values(TestResult(SPV_ERROR_INVALID_DATA,
+                          "Vulkan spec allows BuiltIn FragInvocationCountEXT "
+                          "to be used only with Fragment execution model."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragInvocationCountInvalidStorageClass,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragInvocationCountEXT"), Values("Fragment"),
+            Values("Output"), Values("%u32"),
+            Values("OpCapability FragmentDensityEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"),
+            Values("VUID-FragInvocationCountEXT-FragInvocationCountEXT-04218"),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "Vulkan spec allows BuiltIn FragInvocationCountEXT to be only "
+                "used for variables with Input storage class."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragInvocationCountInvalidType,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragInvocationCountEXT"), Values("Fragment"),
+            Values("Input"), Values("%f32"),
+            Values("OpCapability FragmentDensityEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"),
+            Values("VUID-FragInvocationCountEXT-FragInvocationCountEXT-04219"),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "According to the Vulkan spec BuiltIn FragInvocationCountEXT "
+                "variable needs to be a 32-bit int scalar."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragSizeInputSuccess,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragSizeEXT"), Values("Fragment"), Values("Input"),
+            Values("%u32vec2"), Values("OpCapability FragmentDensityEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"),
+            Values(nullptr), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragSizeInvalidExecutionModel,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragSizeEXT"), Values("Vertex"), Values("Input"),
+            Values("%u32vec2"), Values("OpCapability FragmentDensityEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"),
+            Values("VUID-FragSizeEXT-FragSizeEXT-04220"),
+            Values(TestResult(SPV_ERROR_INVALID_DATA,
+                              "Vulkan spec allows BuiltIn FragSizeEXT to be "
+                              "used only with Fragment execution model."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragSizeInvalidStorageClass,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(
+        Values("FragSizeEXT"), Values("Fragment"), Values("Output"),
+        Values("%u32vec2"), Values("OpCapability FragmentDensityEXT\n"),
+        Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"),
+        Values("VUID-FragSizeEXT-FragSizeEXT-04221"),
+        Values(TestResult(SPV_ERROR_INVALID_DATA,
+                          "Vulkan spec allows BuiltIn FragSizeEXT to be only "
+                          "used for variables with Input storage class."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragSizeInvalidType,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragSizeEXT"), Values("Fragment"), Values("Input"),
+            Values("%u32vec3"), Values("OpCapability FragmentDensityEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"),
+            Values("VUID-FragSizeEXT-FragSizeEXT-04222"),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "According to the Vulkan spec BuiltIn FragSizeEXT variable "
+                "needs to be a 2-component 32-bit int vector."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragStencilRefOutputSuccess,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragStencilRefEXT"), Values("Fragment"), Values("Output"),
+            Values("%u32", "%u64"), Values("OpCapability StencilExportEXT\n"),
+            Values("OpExtension \"SPV_EXT_shader_stencil_export\"\n"),
+            Values(nullptr), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragStencilRefInvalidExecutionModel,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragStencilRefEXT"), Values("Vertex"), Values("Output"),
+            Values("%u32", "%u64"), Values("OpCapability StencilExportEXT\n"),
+            Values("OpExtension \"SPV_EXT_shader_stencil_export\"\n"),
+            Values("VUID-FragStencilRefEXT-FragStencilRefEXT-04223"),
+            Values(TestResult(SPV_ERROR_INVALID_DATA,
+                              "Vulkan spec allows BuiltIn FragStencilRefEXT to "
+                              "be used only with Fragment execution model."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragStencilRefInvalidStorageClass,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragStencilRefEXT"), Values("Fragment"), Values("Input"),
+            Values("%u32", "%u64"), Values("OpCapability StencilExportEXT\n"),
+            Values("OpExtension \"SPV_EXT_shader_stencil_export\"\n"),
+            Values("VUID-FragStencilRefEXT-FragStencilRefEXT-04224"),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "Vulkan spec allows BuiltIn FragStencilRefEXT to be only used "
+                "for variables with Output storage class."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FragStencilRefInvalidType,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FragStencilRefEXT"), Values("Fragment"), Values("Output"),
+            Values("%f32", "%f64", "%u32vec2"),
+            Values("OpCapability StencilExportEXT\n"),
+            Values("OpExtension \"SPV_EXT_shader_stencil_export\"\n"),
+            Values("VUID-FragStencilRefEXT-FragStencilRefEXT-04225"),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "According to the Vulkan spec BuiltIn FragStencilRefEXT "
+                "variable needs to be a int scalar."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FullyCoveredEXTInputSuccess,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FullyCoveredEXT"), Values("Fragment"), Values("Input"),
+            Values("%bool"), Values("OpCapability FragmentFullyCoveredEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_fully_covered\"\n"),
+            Values(nullptr), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    FullyCoveredEXTInvalidExecutionModel,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FullyCoveredEXT"), Values("Vertex"), Values("Input"),
+            Values("%bool"), Values("OpCapability FragmentFullyCoveredEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_fully_covered\"\n"),
+            Values("VUID-FullyCoveredEXT-FullyCoveredEXT-04232"),
+            Values(TestResult(SPV_ERROR_INVALID_DATA,
+                              "Vulkan spec allows BuiltIn FullyCoveredEXT to "
+                              "be used only with Fragment execution model."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FullyCoveredEXTInvalidStorageClass,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FullyCoveredEXT"), Values("Fragment"), Values("Output"),
+            Values("%bool"), Values("OpCapability FragmentFullyCoveredEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_fully_covered\"\n"),
+            Values("VUID-FullyCoveredEXT-FullyCoveredEXT-04233"),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "Vulkan spec allows BuiltIn FullyCoveredEXT to be only used "
+                "for variables with Input storage class."))));
+
+INSTANTIATE_TEST_SUITE_P(
+    FullyCoveredEXTInvalidType,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+    Combine(Values("FullyCoveredEXT"), Values("Fragment"), Values("Input"),
+            Values("%f32"), Values("OpCapability FragmentFullyCoveredEXT\n"),
+            Values("OpExtension \"SPV_EXT_fragment_fully_covered\"\n"),
+            Values("VUID-FullyCoveredEXT-FullyCoveredEXT-04234"),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "According to the Vulkan spec BuiltIn FullyCoveredEXT variable "
+                "needs to be a bool scalar."))));
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools