spirv-val: Add Mesh/Task to check for LocalSize (#6459)
obvious oversight of Mesh/Task that is being fixed up in the spec here
https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/7901
(update, we approved the spec language, will be out in the 1.4.336 spec)
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp
index f2b43b6..22d464f 100644
--- a/source/val/validate_mode_setting.cpp
+++ b/source/val/validate_mode_setting.cpp
@@ -27,6 +27,48 @@
namespace val {
namespace {
+// TODO - Make a common util if someone else needs it too outside this file
+const char* ExecutionModelToString(spv::ExecutionModel value) {
+ switch (value) {
+ case spv::ExecutionModel::Vertex:
+ return "Vertex";
+ case spv::ExecutionModel::TessellationControl:
+ return "TessellationControl";
+ case spv::ExecutionModel::TessellationEvaluation:
+ return "TessellationEvaluation";
+ case spv::ExecutionModel::Geometry:
+ return "Geometry";
+ case spv::ExecutionModel::Fragment:
+ return "Fragment";
+ case spv::ExecutionModel::GLCompute:
+ return "GLCompute";
+ case spv::ExecutionModel::Kernel:
+ return "Kernel";
+ case spv::ExecutionModel::TaskNV:
+ return "TaskNV";
+ case spv::ExecutionModel::MeshNV:
+ return "MeshNV";
+ case spv::ExecutionModel::RayGenerationKHR:
+ return "RayGenerationKHR";
+ case spv::ExecutionModel::IntersectionKHR:
+ return "IntersectionKHR";
+ case spv::ExecutionModel::AnyHitKHR:
+ return "AnyHitKHR";
+ case spv::ExecutionModel::ClosestHitKHR:
+ return "ClosestHitKHR";
+ case spv::ExecutionModel::MissKHR:
+ return "MissKHR";
+ case spv::ExecutionModel::CallableKHR:
+ return "CallableKHR";
+ case spv::ExecutionModel::TaskEXT:
+ return "TaskEXT";
+ case spv::ExecutionModel::MeshEXT:
+ return "MeshEXT";
+ default:
+ return "Unknown";
+ }
+}
+
spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
const auto entry_point_id = inst->GetOperandAs<uint32_t>(1);
auto entry_point = _.FindDef(entry_point_id);
@@ -306,74 +348,79 @@
}
if (spvIsVulkanEnv(_.context()->target_env)) {
- switch (execution_model) {
- case spv::ExecutionModel::GLCompute:
- if (!has_mode(spv::ExecutionMode::LocalSize)) {
- bool ok = has_workgroup_size || has_local_size_id;
- if (!ok && _.HasCapability(spv::Capability::TileShadingQCOM)) {
- ok = has_mode(spv::ExecutionMode::TileShadingRateQCOM);
- }
- if (!ok) {
+ // SPV_QCOM_tile_shading checks
+ if (execution_model == spv::ExecutionModel::GLCompute) {
+ if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
+ if (has_mode(spv::ExecutionMode::TileShadingRateQCOM) &&
+ (has_mode(spv::ExecutionMode::LocalSize) ||
+ has_mode(spv::ExecutionMode::LocalSizeId))) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "If the TileShadingRateQCOM execution mode is used, "
+ << "LocalSize and LocalSizeId must not be specified.";
+ }
+ if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "The NonCoherentTileAttachmentQCOM execution mode must "
+ "not be used in any stage other than fragment.";
+ }
+ } else {
+ if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "If the TileShadingRateQCOM execution mode is used, the "
+ "TileShadingQCOM capability must be enabled.";
+ }
+ }
+ } else {
+ if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "The TileShadingRateQCOM execution mode must not be used "
+ "in any stage other than compute.";
+ }
+ if (execution_model != spv::ExecutionModel::Fragment) {
+ if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << "The NonCoherentTileAttachmentQCOM execution mode must "
+ "not be used in any stage other than fragment.";
+ }
+ if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
+ return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+ << "The TileShadingQCOM capability must not be enabled in "
+ "any stage other than compute or fragment.";
+ }
+ } else {
+ if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
+ if (!_.HasCapability(spv::Capability::TileShadingQCOM)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << _.VkErrorID(10685)
- << "In the Vulkan environment, GLCompute execution model "
- "entry points require either the "
- << (_.HasCapability(spv::Capability::TileShadingQCOM)
- ? "TileShadingRateQCOM, "
- : "")
- << "LocalSize or LocalSizeId execution mode or an object "
- "decorated with WorkgroupSize must be specified.";
+ << "If the NonCoherentTileAttachmentReadQCOM execution "
+ "mode is used, the TileShadingQCOM capability must be "
+ "enabled.";
}
}
+ }
+ }
- if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
- if (has_mode(spv::ExecutionMode::TileShadingRateQCOM) &&
- (has_mode(spv::ExecutionMode::LocalSize) ||
- has_mode(spv::ExecutionMode::LocalSizeId))) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "If the TileShadingRateQCOM execution mode is used, "
- << "LocalSize and LocalSizeId must not be specified.";
- }
- if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "The NonCoherentTileAttachmentQCOM execution mode must "
- "not be used in any stage other than fragment.";
- }
- } else {
- if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "If the TileShadingRateQCOM execution mode is used, the "
- "TileShadingQCOM capability must be enabled.";
- }
+ switch (execution_model) {
+ case spv::ExecutionModel::GLCompute:
+ case spv::ExecutionModel::MeshEXT:
+ case spv::ExecutionModel::MeshNV:
+ case spv::ExecutionModel::TaskEXT:
+ case spv::ExecutionModel::TaskNV:
+ if (!has_mode(spv::ExecutionMode::LocalSize) && !has_workgroup_size &&
+ !has_local_size_id &&
+ !has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << _.VkErrorID(10685) << "In the Vulkan environment, "
+ << ExecutionModelToString(execution_model)
+ << " execution model "
+ "entry points require either the "
+ << (_.HasCapability(spv::Capability::TileShadingQCOM)
+ ? "TileShadingRateQCOM, "
+ : "")
+ << "LocalSize or LocalSizeId execution mode or an object "
+ "decorated with WorkgroupSize must be specified.";
}
break;
default:
- if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "The TileShadingRateQCOM execution mode must not be used "
- "in any stage other than compute.";
- }
- if (execution_model != spv::ExecutionModel::Fragment) {
- if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "The NonCoherentTileAttachmentQCOM execution mode must "
- "not be used in any stage other than fragment.";
- }
- if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
- return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
- << "The TileShadingQCOM capability must not be enabled in "
- "any stage other than compute or fragment.";
- }
- } else {
- if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
- if (!_.HasCapability(spv::Capability::TileShadingQCOM)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "If the NonCoherentTileAttachmentReadQCOM execution "
- "mode is used, the TileShadingQCOM capability must be "
- "enabled.";
- }
- }
- }
break;
}
}
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index 65d6950..8d5f362 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -141,7 +141,11 @@
execution_modes << "OpExecutionMode %" << entry_point.name
<< " OutputPoints\n";
}
- if (0 == std::strcmp(execution_model, "GLCompute")) {
+ if (0 == std::strcmp(execution_model, "GLCompute") ||
+ 0 == std::strcmp(execution_model, "MeshEXT") ||
+ 0 == std::strcmp(execution_model, "MeshNV") ||
+ 0 == std::strcmp(execution_model, "MeshEXT") ||
+ 0 == std::strcmp(execution_model, "TaskNV")) {
execution_modes << "OpExecutionMode %" << entry_point.name
<< " LocalSize 1 1 1\n";
}
@@ -303,7 +307,11 @@
execution_modes << "OpExecutionMode %" << entry_point.name
<< " OutputPoints\n";
}
- if (0 == std::strcmp(execution_model, "GLCompute")) {
+ if (0 == std::strcmp(execution_model, "GLCompute") ||
+ 0 == std::strcmp(execution_model, "MeshEXT") ||
+ 0 == std::strcmp(execution_model, "MeshNV") ||
+ 0 == std::strcmp(execution_model, "MeshEXT") ||
+ 0 == std::strcmp(execution_model, "TaskNV")) {
execution_modes << "OpExecutionMode %" << entry_point.name
<< " LocalSize 1 1 1\n";
}
@@ -452,7 +460,11 @@
execution_modes << "OpExecutionMode %" << entry_point.name
<< " OutputPoints\n";
}
- if (0 == std::strcmp(execution_model, "GLCompute")) {
+ if (0 == std::strcmp(execution_model, "GLCompute") ||
+ 0 == std::strcmp(execution_model, "MeshEXT") ||
+ 0 == std::strcmp(execution_model, "MeshNV") ||
+ 0 == std::strcmp(execution_model, "MeshEXT") ||
+ 0 == std::strcmp(execution_model, "TaskNV")) {
execution_modes << "OpExecutionMode %" << entry_point.name
<< " LocalSize 1 1 1\n";
}
@@ -2703,7 +2715,11 @@
execution_modes << "OpExecutionMode %" << entry_point.name
<< " OutputPoints\n";
}
- if (0 == std::strcmp(execution_model, "GLCompute")) {
+ if (0 == std::strcmp(execution_model, "GLCompute") ||
+ 0 == std::strcmp(execution_model, "MeshEXT") ||
+ 0 == std::strcmp(execution_model, "MeshNV") ||
+ 0 == std::strcmp(execution_model, "MeshEXT") ||
+ 0 == std::strcmp(execution_model, "TaskNV")) {
execution_modes << "OpExecutionMode %" << entry_point.name
<< " LocalSize 1 1 1\n";
}
@@ -3615,6 +3631,7 @@
EntryPoint entry_point;
entry_point.name = "main_d_r";
entry_point.execution_model = "MeshNV";
+ entry_point.execution_modes = "OpExecutionMode %main_d_r LocalSize 1 1 1";
entry_point.interfaces = "%gl_PrimitiveID %gl_Layer %gl_ViewportIndex";
generator.entry_points_.push_back(std::move(entry_point));
@@ -3653,6 +3670,7 @@
EntryPoint entry_point;
entry_point.name = "main_d_r";
entry_point.execution_model = "MeshNV";
+ entry_point.execution_modes = "OpExecutionMode %main_d_r LocalSize 1 1 1";
entry_point.interfaces = "%gl_PrimitiveID %gl_Layer %gl_ViewportIndex";
entry_point.body = "%ref_load = OpLoad %_arr_float_uint_81 %gl_PrimitiveID";
generator.entry_points_.push_back(std::move(entry_point));
diff --git a/test/val/val_mesh_shading_test.cpp b/test/val/val_mesh_shading_test.cpp
index 49d5ebc..1897015 100644
--- a/test/val/val_mesh_shading_test.cpp
+++ b/test/val/val_mesh_shading_test.cpp
@@ -234,6 +234,8 @@
OpMemoryModel Logical GLSL450
OpEntryPoint MeshEXT %mainMesh "mainMesh"
OpEntryPoint TaskEXT %mainTask "mainTask"
+ OpExecutionMode %mainMesh LocalSize 1 1 1
+ OpExecutionMode %mainTask LocalSize 1 1 1
OpExecutionMode %mainMesh OutputVertices 1
OpExecutionMode %mainMesh OutputPrimitivesEXT 1
OpExecutionMode %mainMesh OutputTrianglesEXT
diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp
index 58e41ed..01e215c 100644
--- a/test/val/val_modes_test.cpp
+++ b/test/val/val_modes_test.cpp
@@ -73,6 +73,53 @@
"or an object decorated with WorkgroupSize must be specified."));
}
+TEST_F(ValidateMode, MeshNoModeVulkan) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability MeshShadingEXT
+OpExtension "SPV_EXT_mesh_shader"
+OpMemoryModel Logical GLSL450
+OpEntryPoint MeshEXT %main "main"
+OpExecutionMode %main OutputVertices 81
+OpExecutionMode %main OutputPrimitivesEXT 16
+OpExecutionMode %main OutputPoints
+)" + kVoidFunction;
+
+ spv_target_env env = SPV_ENV_VULKAN_1_3;
+ CompileSuccessfully(spirv, env);
+ EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-None-10685"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "In the Vulkan environment, MeshEXT execution model entry "
+ "points require either the LocalSize or LocalSizeId execution mode "
+ "or an object decorated with WorkgroupSize must be specified."));
+}
+
+TEST_F(ValidateMode, TaskNoModeVulkan) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability MeshShadingEXT
+OpExtension "SPV_EXT_mesh_shader"
+OpMemoryModel Logical GLSL450
+OpEntryPoint TaskEXT %main "main"
+)" + kVoidFunction;
+
+ spv_target_env env = SPV_ENV_VULKAN_1_3;
+ CompileSuccessfully(spirv, env);
+ EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-None-10685"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "In the Vulkan environment, TaskEXT execution model entry "
+ "points require either the LocalSize or LocalSizeId execution mode "
+ "or an object decorated with WorkgroupSize must be specified."));
+}
+
TEST_F(ValidateMode, GLComputeNoModeVulkanWorkgroupSize) {
const std::string spirv = R"(
OpCapability Shader
@@ -2926,7 +2973,6 @@
OpExtension "SPV_QCOM_tile_shading"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main NonCoherentTileAttachmentReadQCOM
)" + kVoidFunction;
spv_target_env env = SPV_ENV_VULKAN_1_4;