Add builtin validation for SPV_NV_shader_sm_builtins (#2656)
Also add a Builtin test generator variant that takes
capabilities and extensions.
Tests
- verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
accepted as Inputs in Vertex, Fragment, TessControl, TessEval, Geometry,
and Compute.
- verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
accepted as Inputs in MeshNV and TaskNV shaders.
- verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
accepted as Inputs in the 6 ray tracing stages
- verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
NOT accepted as Outputs.
- verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
NOT accepted as non-scalar integers (f32, uvec3)
- verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
NOT accepted as non-32-bit integers (u64)
diff --git a/.appveyor.yml b/.appveyor.yml
index 196f545..77cd89c 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -63,7 +63,7 @@
- ninja install
test_script:
- - ctest -C %CONFIGURATION% --output-on-failure --timeout 300
+ - ctest -C %CONFIGURATION% --output-on-failure --timeout 310
after_test:
# Zip build artifacts for uploading and deploying
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 568d729..0656065 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -208,6 +208,8 @@
// Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition(
const Decoration& decoration, const Instruction& inst);
+ spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration,
+ const Instruction& inst);
// The following section contains functions which are called when id defined
// by |referenced_inst| is
@@ -332,6 +334,11 @@
const Instruction& referenced_inst,
const Instruction& referenced_from_inst);
+ spv_result_t ValidateSMBuiltinsAtReference(
+ const Decoration& decoration, const Instruction& built_in_inst,
+ const Instruction& referenced_inst,
+ const Instruction& referenced_from_inst);
+
// Validates that |built_in_inst| is not (even indirectly) referenced from
// within a function which can be called with |execution_model|.
//
@@ -2654,6 +2661,61 @@
return SPV_SUCCESS;
}
+spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition(
+ const Decoration& decoration, const Instruction& inst) {
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ if (spv_result_t error = ValidateI32(
+ decoration, inst,
+ [this, &inst,
+ &decoration](const std::string& message) -> spv_result_t {
+ return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+ << "According to the "
+ << spvLogStringForEnv(_.context()->target_env)
+ << " spec BuiltIn "
+ << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+ decoration.params()[0])
+ << " variable needs to be a 32-bit int scalar. "
+ << message;
+ })) {
+ return error;
+ }
+ }
+
+ // Seed at reference checks with this built-in.
+ return ValidateSMBuiltinsAtReference(decoration, inst, inst, inst);
+}
+
+spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtReference(
+ const Decoration& decoration, const Instruction& built_in_inst,
+ const Instruction& referenced_inst,
+ const Instruction& referenced_from_inst) {
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
+ if (storage_class != SpvStorageClassMax &&
+ storage_class != SpvStorageClassInput) {
+ return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+ << spvLogStringForEnv(_.context()->target_env)
+ << " spec allows BuiltIn "
+ << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+ decoration.params()[0])
+ << " to be only used for "
+ "variables with Input storage class. "
+ << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+ referenced_from_inst)
+ << " " << GetStorageClassDesc(referenced_from_inst);
+ }
+ }
+
+ 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::ValidateSMBuiltinsAtReference, this, decoration,
+ built_in_inst, referenced_from_inst, std::placeholders::_1));
+ }
+
+ return SPV_SUCCESS;
+}
+
spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
const Decoration& decoration, const Instruction& inst) {
const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
@@ -2748,6 +2810,12 @@
case SpvBuiltInLocalInvocationIndex: {
return ValidateLocalInvocationIndexAtDefinition(decoration, inst);
}
+ case SpvBuiltInWarpsPerSMNV:
+ case SpvBuiltInSMCountNV:
+ case SpvBuiltInWarpIDNV:
+ case SpvBuiltInSMIDNV: {
+ return ValidateSMBuiltinsAtDefinition(decoration, inst);
+ }
case SpvBuiltInWorkDim:
case SpvBuiltInGlobalSize:
case SpvBuiltInEnqueuedWorkgroupSize:
@@ -2810,11 +2878,7 @@
case SpvBuiltInWorldToObjectNV:
case SpvBuiltInHitTNV:
case SpvBuiltInHitKindNV:
- case SpvBuiltInIncomingRayFlagsNV:
- case SpvBuiltInWarpsPerSMNV:
- case SpvBuiltInSMCountNV:
- case SpvBuiltInWarpIDNV:
- case SpvBuiltInSMIDNV: {
+ case SpvBuiltInIncomingRayFlagsNV: {
// No validation rules (for the moment).
break;
}
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index cff9d4a..df048ad 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -62,7 +62,10 @@
std::tuple<const char*, const char*, const char*, const char*, TestResult>>;
using ValidateWebGPUCombineBuiltInArrayedVariable = spvtest::ValidateBase<
std::tuple<const char*, const char*, const char*, const char*, TestResult>>;
-
+using ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult =
+ spvtest::ValidateBase<
+ std::tuple<const char*, const char*, const char*, const char*,
+ const char*, const char*, TestResult>>;
bool InitializerRequired(spv_target_env env, const char* const storage_class) {
return spvIsWebGPUEnv(env) && (strncmp(storage_class, "Output", 6) == 0 ||
@@ -74,11 +77,20 @@
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
+ const char* const capabilities,
+ const char* const extensions,
const char* const data_type) {
CodeGenerator generator =
spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator()
: CodeGenerator::GetDefaultShaderCodeGenerator();
+ if (capabilities) {
+ generator.capabilities_ += capabilities;
+ }
+ if (extensions) {
+ generator.extensions_ += extensions;
+ }
+
generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
@@ -144,8 +156,9 @@
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
- CodeGenerator generator = GetInMainCodeGenerator(
- SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
+ CodeGenerator generator =
+ GetInMainCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model,
+ storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
@@ -165,8 +178,9 @@
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
- CodeGenerator generator = GetInMainCodeGenerator(
- SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
+ CodeGenerator generator =
+ GetInMainCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model,
+ storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
@@ -179,15 +193,50 @@
}
}
+TEST_P(
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ InMain) {
+ const char* const built_in = std::get<0>(GetParam());
+ const char* const execution_model = std::get<1>(GetParam());
+ const char* const storage_class = std::get<2>(GetParam());
+ const char* const data_type = std::get<3>(GetParam());
+ const char* const capabilities = std::get<4>(GetParam());
+ const char* const extensions = std::get<5>(GetParam());
+ const TestResult& test_result = std::get<6>(GetParam());
+
+ CodeGenerator generator = GetInMainCodeGenerator(
+ SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class,
+ capabilities, extensions, data_type);
+
+ CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(test_result.validation_result,
+ ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ if (test_result.error_str) {
+ EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
+ }
+ if (test_result.error_str2) {
+ EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
+ }
+}
+
CodeGenerator GetInFunctionCodeGenerator(spv_target_env env,
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
+ const char* const capabilities,
+ const char* const extensions,
const char* const data_type) {
CodeGenerator generator =
spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator()
: CodeGenerator::GetDefaultShaderCodeGenerator();
+ if (capabilities) {
+ generator.capabilities_ += capabilities;
+ }
+ if (extensions) {
+ generator.extensions_ += extensions;
+ }
+
generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
@@ -267,8 +316,9 @@
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
- CodeGenerator generator = GetInFunctionCodeGenerator(
- SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
+ CodeGenerator generator =
+ GetInFunctionCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model,
+ storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
@@ -288,8 +338,9 @@
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
- CodeGenerator generator = GetInFunctionCodeGenerator(
- SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
+ CodeGenerator generator =
+ GetInFunctionCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model,
+ storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
@@ -302,15 +353,50 @@
}
}
+TEST_P(
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ InFunction) {
+ const char* const built_in = std::get<0>(GetParam());
+ const char* const execution_model = std::get<1>(GetParam());
+ const char* const storage_class = std::get<2>(GetParam());
+ const char* const data_type = std::get<3>(GetParam());
+ const char* const capabilities = std::get<4>(GetParam());
+ const char* const extensions = std::get<5>(GetParam());
+ const TestResult& test_result = std::get<6>(GetParam());
+
+ CodeGenerator generator = GetInFunctionCodeGenerator(
+ SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class,
+ capabilities, extensions, data_type);
+
+ CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(test_result.validation_result,
+ ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ if (test_result.error_str) {
+ EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
+ }
+ if (test_result.error_str2) {
+ EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
+ }
+}
+
CodeGenerator GetVariableCodeGenerator(spv_target_env env,
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
+ const char* const capabilities,
+ const char* const extensions,
const char* const data_type) {
CodeGenerator generator =
spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator()
: CodeGenerator::GetDefaultShaderCodeGenerator();
+ if (capabilities) {
+ generator.capabilities_ += capabilities;
+ }
+ if (extensions) {
+ generator.extensions_ += extensions;
+ }
+
generator.before_types_ = "OpDecorate %built_in_var BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
@@ -373,8 +459,9 @@
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
- CodeGenerator generator = GetVariableCodeGenerator(
- SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
+ CodeGenerator generator =
+ GetVariableCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model,
+ storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
@@ -394,8 +481,9 @@
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
- CodeGenerator generator = GetVariableCodeGenerator(
- SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
+ CodeGenerator generator =
+ GetVariableCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model,
+ storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
@@ -408,6 +496,32 @@
}
}
+TEST_P(
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Variable) {
+ const char* const built_in = std::get<0>(GetParam());
+ const char* const execution_model = std::get<1>(GetParam());
+ const char* const storage_class = std::get<2>(GetParam());
+ const char* const data_type = std::get<3>(GetParam());
+ const char* const capabilities = std::get<4>(GetParam());
+ const char* const extensions = std::get<5>(GetParam());
+ const TestResult& test_result = std::get<6>(GetParam());
+
+ CodeGenerator generator = GetVariableCodeGenerator(
+ SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class,
+ capabilities, extensions, data_type);
+
+ CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(test_result.validation_result,
+ ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ if (test_result.error_str) {
+ EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
+ }
+ if (test_result.error_str2) {
+ EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
+ }
+}
+
INSTANTIATE_TEST_SUITE_P(
ClipAndCullDistanceOutputSuccess,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
@@ -2028,6 +2142,81 @@
"needs to be a 32-bit float array",
"components are not float scalar"))));
+INSTANTIATE_TEST_SUITE_P(
+ SMBuiltinsInputSuccess,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
+ Values("Vertex", "Fragment", "TessellationControl",
+ "TessellationEvaluation", "Geometry", "GLCompute"),
+ Values("Input"), Values("%u32"),
+ Values("OpCapability ShaderSMBuiltinsNV\n"),
+ Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"),
+ Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ SMBuiltinsInputMeshSuccess,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(
+ Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
+ Values("MeshNV", "TaskNV"), Values("Input"), Values("%u32"),
+ Values("OpCapability ShaderSMBuiltinsNV\nOpCapability MeshShadingNV\n"),
+ Values("OpExtension \"SPV_NV_shader_sm_builtins\"\nOpExtension "
+ "\"SPV_NV_mesh_shader\"\n"),
+ Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ SMBuiltinsInputRaySuccess,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(
+ Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
+ Values("RayGenerationNV", "IntersectionNV", "AnyHitNV", "ClosestHitNV",
+ "MissNV", "CallableNV"),
+ Values("Input"), Values("%u32"),
+ Values("OpCapability ShaderSMBuiltinsNV\nOpCapability RayTracingNV\n"),
+ Values("OpExtension \"SPV_NV_shader_sm_builtins\"\nOpExtension "
+ "\"SPV_NV_ray_tracing\"\n"),
+ Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+ SMBuiltinsNotInput,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
+ Values("Vertex", "Fragment", "TessellationControl",
+ "TessellationEvaluation", "Geometry", "GLCompute"),
+ Values("Output"), Values("%u32"),
+ Values("OpCapability ShaderSMBuiltinsNV\n"),
+ Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"),
+ Values(TestResult(
+ SPV_ERROR_INVALID_DATA,
+ "to be only used for variables with Input storage class",
+ "uses storage class Output"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ SMBuiltinsNotIntScalar,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
+ Values("Vertex", "Fragment", "TessellationControl",
+ "TessellationEvaluation", "Geometry", "GLCompute"),
+ Values("Input"), Values("%f32", "%u32vec3"),
+ Values("OpCapability ShaderSMBuiltinsNV\n"),
+ Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "needs to be a 32-bit int scalar",
+ "is not an int scalar"))));
+
+INSTANTIATE_TEST_SUITE_P(
+ SMBuiltinsNotInt32,
+ ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
+ Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
+ Values("Vertex", "Fragment", "TessellationControl",
+ "TessellationEvaluation", "Geometry", "GLCompute"),
+ Values("Input"), Values("%u64"),
+ Values("OpCapability ShaderSMBuiltinsNV\n"),
+ Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"),
+ Values(TestResult(SPV_ERROR_INVALID_DATA,
+ "needs to be a 32-bit int scalar",
+ "has bit width 64"))));
+
CodeGenerator GetWorkgroupSizeSuccessGenerator(spv_target_env env) {
CodeGenerator generator =
env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator()