spirv-val: Add LocalInvocationIndex checks (#6395)
adds
- VUID-LocalInvocationIndex-LocalInvocationIndex-04284
- VUID-LocalInvocationIndex-LocalInvocationIndex-04285
- VUID-LocalInvocationIndex-LocalInvocationIndex-04286
... the functions (`ValidateLocalInvocationIndexAtReference`) where
there, but empty, not sure what happened, seems they were added in
WebGPU (https://github.com/KhronosGroup/SPIRV-Tools/pull/2335) then
stripped, but we need them for Vulkan
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index ba02047..c8586a7 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -123,7 +123,7 @@
VUIDErrorMax,
} VUIDError;
-const static uint32_t NumVUIDBuiltins = 40;
+const static uint32_t NumVUIDBuiltins = 41;
typedef struct {
spv::BuiltIn builtIn;
@@ -170,6 +170,7 @@
{spv::BuiltIn::CullMaskKHR, {6735, 6736, 6737}},
{spv::BuiltIn::BaryCoordKHR, {4154, 4155, 4156}},
{spv::BuiltIn::BaryCoordNoPerspKHR, {4160, 4161, 4162}},
+ {spv::BuiltIn::LocalInvocationIndex, {4284, 4285, 4286}},
{spv::BuiltIn::PrimitivePointIndicesEXT, {7041, 7043, 7044}},
{spv::BuiltIn::PrimitiveLineIndicesEXT, {7047, 7049, 7050}},
{spv::BuiltIn::PrimitiveTriangleIndicesEXT, {7053, 7055, 7056}},
@@ -2829,14 +2830,69 @@
spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition(
const Decoration& decoration, const Instruction& inst) {
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ if (spv_result_t error = ValidateI32(
+ decoration, inst,
+ [this, &inst](const std::string& message) -> spv_result_t {
+ uint32_t vuid = GetVUIDForBuiltin(
+ spv::BuiltIn::LocalInvocationIndex, VUIDErrorType);
+ return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+ << _.VkErrorID(vuid)
+ << "According to the Vulkan spec BuiltIn "
+ "LocalInvocationIndex variable needs to be a 32-bit "
+ "int scalar. "
+ << message;
+ })) {
+ return error;
+ }
+ }
+
// Seed at reference checks with this built-in.
return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst);
}
spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference(
const Decoration& decoration, const Instruction& built_in_inst,
- const Instruction&,
+ const Instruction& referenced_inst,
const Instruction& referenced_from_inst) {
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ const spv::StorageClass storage_class =
+ GetStorageClass(referenced_from_inst);
+ if (storage_class != spv::StorageClass::Max &&
+ storage_class != spv::StorageClass::Input) {
+ uint32_t vuid = GetVUIDForBuiltin(spv::BuiltIn::LocalInvocationIndex,
+ VUIDErrorStorageClass);
+ return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+ << _.VkErrorID(vuid)
+ << "Vulkan spec allows BuiltIn LocalInvocationIndex 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 spv::ExecutionModel execution_model : execution_models_) {
+ bool has_vulkan_model =
+ execution_model == spv::ExecutionModel::GLCompute ||
+ execution_model == spv::ExecutionModel::TaskNV ||
+ execution_model == spv::ExecutionModel::MeshNV ||
+ execution_model == spv::ExecutionModel::TaskEXT ||
+ execution_model == spv::ExecutionModel::MeshEXT;
+
+ if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
+ uint32_t vuid = GetVUIDForBuiltin(spv::BuiltIn::LocalInvocationIndex,
+ VUIDErrorExecutionModel);
+ return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+ << _.VkErrorID(vuid)
+ << "Vulkan spec allows BuiltIn LocalInvocationIndex to be used "
+ "only with GLCompute, MeshNV, TaskNV, MeshEXT or"
+ << " TaskEXT 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(
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 1c15601..410d3e6 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -2254,6 +2254,12 @@
return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04282);
case 4283:
return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04283);
+ case 4284:
+ return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04284);
+ case 4285:
+ return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04285);
+ case 4286:
+ return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04286);
case 4293:
return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04293);
case 4294:
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index 7f81ce6..769cc47 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -858,6 +858,45 @@
"has components with bit width 64"))));
INSTANTIATE_TEST_SUITE_P(
+ LocalInvocationIndexSuccess,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
+ Combine(Values("LocalInvocationIndex"), Values("GLCompute"),
+ Values("Input"), Values("%u32"), Values(nullptr),
+ Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ LocalInvocationIndexNotGLCompute,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
+ Combine(Values("LocalInvocationIndex"),
+ Values("Vertex", "Fragment", "Geometry", "TessellationControl",
+ "TessellationEvaluation"),
+ Values("Input"), Values("%u32"),
+ Values("VUID-LocalInvocationIndex-LocalInvocationIndex-04284"),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "to be used only with GLCompute, MeshNV, "
+ "TaskNV, MeshEXT or TaskEXT execution model"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ LocalInvocationIndexNotInput,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
+ Combine(Values("LocalInvocationIndex"), Values("GLCompute"),
+ Values("Output"), Values("%u32"),
+ Values("VUID-LocalInvocationIndex-LocalInvocationIndex-04285"),
+ Values(TestResult(
+ SPV_ERROR_INVALID_DATA,
+ "to be only used for variables with Input storage class",
+ "uses storage class Output"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ LocalInvocationIndexNot32Int,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
+ Combine(Values("LocalInvocationIndex"), Values("GLCompute"),
+ Values("Input"), Values("%u32vec3", "%f32"),
+ Values("VUID-LocalInvocationIndex-LocalInvocationIndex-04286"),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "needs to be a 32-bit int scalar"))));
+
+INSTANTIATE_TEST_SUITE_P(
InvocationIdSuccess,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
Combine(Values("InvocationId"), Values("Geometry", "TessellationControl"),