From c00d507443658225cdb375974066c60762e6ecb3 Mon Sep 17 00:00:00 2001 From: Pankaj Mistry Date: Wed, 27 Mar 2024 11:07:08 -0700 Subject: [PATCH] Add EXT_mesh_shader validation support 1. Each OpEntryPoint with the MeshEXT Execution Model can have at most one global OpVariable of storage class TaskPayloadWorkgroupEXT. 2. PerPrimitiveEXT only be used on a memory object declaration or a member of a structure type 3. PerPrimitiveEXT only Input in Fragment and Output in MeshEXT 4. Added Mesh vulkan validation support for following rules: VUID-Layer-Layer-07039 VUID-PrimitiveId-PrimitiveId-07040,VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042, VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048, VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07054, VUID-ViewportIndex-ViewportIndex-07060 VUID-StandaloneSpirv-ExecutionModel-07330 VUID-StandaloneSpirv-ExecutionModel-07331 --- source/val/validate_annotation.cpp | 14 + source/val/validate_builtins.cpp | 113 +++++++- source/val/validate_decorations.cpp | 14 + source/val/validate_mesh_shading.cpp | 59 +++- source/val/validate_mode_setting.cpp | 19 ++ source/val/validation_state.cpp | 22 +- test/val/val_builtins_test.cpp | 254 +++++++++++++++++ test/val/val_mesh_shading_test.cpp | 390 +++++++++++++++++++++++++++ test/val/val_modes_test.cpp | 104 +++++++ 9 files changed, 983 insertions(+), 6 deletions(-) diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp index dac3585788e..6432a550e3b 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp @@ -143,6 +143,11 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec, return fail(0) << "must be a variable"; } break; + case spv::Decoration::PerPrimitiveNV: + if (target->opcode() != spv::Op::OpVariable) { + return fail(0) << "must be a memory object declaration"; + } + break; case spv::Decoration::NoPerspective: case spv::Decoration::Flat: case spv::Decoration::Patch: @@ -361,6 +366,15 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _, << _.SpvDecorationString(decoration) << " cannot be applied to structure members"; } + + const auto target_id = inst->GetOperandAs(0); + const auto target = _.FindDef(target_id); + if (decoration == spv::Decoration::PerPrimitiveNV && + target->opcode() != spv::Op::OpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.SpvDecorationString(decoration) + << " must be a memory object declaration"; + } return SPV_SUCCESS; } diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp index a7e9942a0ff..78761ae7032 100644 --- a/source/val/validate_builtins.cpp +++ b/source/val/validate_builtins.cpp @@ -667,6 +667,37 @@ class BuiltInsValidator { // instruction. void Update(const Instruction& inst); + uint32_t GetMeshEntryPoint() { + if (mesh_entry_point_ == 0) { + for (const uint32_t entry_point : _.entry_points()) { + // Every entry point from which this function is called needs to have + // Execution Mode DepthReplacing. + const auto* models = _.GetExecutionModels(entry_point); + if (models->find(spv::ExecutionModel::MeshEXT ) != models->end() || + models->find(spv::ExecutionModel::MeshNV ) != models->end()) { + mesh_entry_point_ = entry_point; + break; + } + } + } + return mesh_entry_point_; + } + + bool isMeshInterfaceVar(const Instruction& inst) { + uint32_t mesh_entry_point = GetMeshEntryPoint(); + if (!mesh_entry_point) return false; + for (const auto& desc : _.entry_point_descriptions(mesh_entry_point)) { + bool found = false; + for (auto interface : desc.interfaces) { + if (inst.id() == interface) { + return true; + } + } + } + return false; + } + + ValidationState_t& _; // Mapping id -> list of rules which validate instruction referencing the @@ -684,7 +715,7 @@ class BuiltInsValidator { // or to no_entry_points_. The pointer is guaranteed to never be null. const std::vector no_entry_points; const std::vector* entry_points_ = &no_entry_points; - + uint32_t mesh_entry_point_ = 0; // Execution models with which the current function can be called. std::set execution_models_; }; @@ -2146,6 +2177,28 @@ spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition( return error; } } + + if (isMeshInterfaceVar(inst)) { + if (_.HasCapability(spv::Capability::MeshShadingEXT) && + !_.HasDecoration(inst.id(), spv::Decoration::PerPrimitiveEXT)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7040) + << "According to the Vulkan spec the variable decorated with " + "Builtin PrimitiveId within the MeshEXT Execution Model must " + "also be decorated with the PerPrimitiveEXT decoration. "; + } +#if 0 + const spv::StorageClass storage_class = + inst.GetOperandAs(2); + if (storage_class != spv::StorageClass::Output) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4336) + << "According to the Vulkan spec the variable decorated with " + "Builtin PrimitiveId within the MeshEXT Execution Model must " + "must be declared using the Output Storage Class. "; + } +#endif + } } // Seed at reference checks with this built-in. @@ -2753,6 +2806,21 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition( return error; } } + + if (isMeshInterfaceVar(inst) && + _.HasCapability(spv::Capability::MeshShadingEXT) && + !_.HasDecoration(inst.id(), spv::Decoration::PerPrimitiveEXT)) { + const spv::BuiltIn label = spv::BuiltIn(decoration.params()[0]); + uint32_t vkerrid = (label == spv::BuiltIn::Layer) ? 7039 : 7060; + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vkerrid) + << "According to the Vulkan spec the variable decorated with " + "Builtin " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " within the MeshEXT Execution Model must also be decorated " + "with the PerPrimitiveEXT decoration. "; + } } // Seed at reference checks with this built-in. @@ -3435,6 +3503,12 @@ spv_result_t BuiltInsValidator::ValidateViewIndexAtReference( uint32_t operand = decoration.params()[0]; if (spvIsVulkanEnv(_.context()->target_env)) { const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst); + + const char* refidname = _.grammar().lookupOperandName(SPV_OPERAND_TYPE_ID, + referenced_inst.id()); + const char* reffrominstidname = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_ID, referenced_from_inst.id()); + if (storage_class != spv::StorageClass::Max && storage_class != spv::StorageClass::Input) { return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) @@ -3458,6 +3532,7 @@ spv_result_t BuiltInsValidator::ValidateViewIndexAtReference( referenced_from_inst, execution_model); } } + } if (function_id_ == 0) { @@ -4167,6 +4242,23 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtReference( spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtDefinition( const Decoration& decoration, const Instruction& inst) { if (spvIsVulkanEnv(_.context()->target_env)) { + uint32_t mesh_entry_point = GetMeshEntryPoint(); + assert(mesh_entry_point); + bool execution_mode_OuputPoints = false; + bool execution_mode_OutputLinesEXT = false; + bool execution_mode_OutputTrianglesEXT = false; + + const auto* modes = _.GetExecutionModes(mesh_entry_point); + if (modes->find(spv::ExecutionMode::OutputPoints) != modes->end()) { + execution_mode_OuputPoints = true; + } + if (modes->find(spv::ExecutionMode::OutputLinesEXT) != modes->end()) { + execution_mode_OutputLinesEXT = true; + } + if (modes->find(spv::ExecutionMode::OutputTrianglesEXT) != + modes->end()) { + execution_mode_OutputTrianglesEXT = true; + } const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]); uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); if (builtin == spv::BuiltIn::PrimitivePointIndicesEXT) { @@ -4185,6 +4277,12 @@ spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtDefinition( })) { return error; } + if (!execution_mode_OuputPoints) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7042) + << "The PrimitivePointIndicesEXT decoration must be used with " + "the OutputPoints Execution Mode"; + } } if (builtin == spv::BuiltIn::PrimitiveLineIndicesEXT) { if (spv_result_t error = ValidateArrayedI32Vec( @@ -4203,6 +4301,12 @@ spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtDefinition( })) { return error; } + if (!execution_mode_OutputLinesEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7048) + << "The PrimitiveLineIndicesEXT decoration must be used with " + "the OutputLinesEXT Execution Mode"; + } } if (builtin == spv::BuiltIn::PrimitiveTriangleIndicesEXT) { if (spv_result_t error = ValidateArrayedI32Vec( @@ -4221,6 +4325,12 @@ spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtDefinition( })) { return error; } + if (!execution_mode_OutputTrianglesEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(7054) + << "The PrimitiveTriangleIndicesEXT decoration must be used with " + "the OutputTrianglesEXT Execution Mode"; + } } } // Seed at reference checks with this built-in. @@ -4249,7 +4359,6 @@ spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtReference( referenced_from_inst) << " " << GetStorageClassDesc(referenced_from_inst); } - for (const spv::ExecutionModel execution_model : execution_models_) { if (execution_model != spv::ExecutionModel::MeshEXT) { uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel); diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index bb1fea55024..fbbbbd4fd63 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -767,6 +767,7 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { int num_workgroup_variables = 0; int num_workgroup_variables_with_block = 0; int num_workgroup_variables_with_aliased = 0; + bool has_task_payload = false; for (const auto& desc : descs) { std::unordered_set seen_vars; for (auto interface : desc.interfaces) { @@ -779,6 +780,19 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { } const spv::StorageClass storage_class = var_instr->GetOperandAs(2); + if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 5)) { + // SPV_EXT_mesh_shader, at most one task payload is permitted + // per entry point + if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) { + if (has_task_payload) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "There can be at most one OpVariable with storage " + "class TaskPayloadWorkgroupEXT associated with " + "an OpEntryPoint"; + } + has_task_payload = true; + } + } if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { // Starting in 1.4, OpEntryPoint must list all global variables // it statically uses and those interfaces must be unique. diff --git a/source/val/validate_mesh_shading.cpp b/source/val/validate_mesh_shading.cpp index e569e251c08..de5a2439e3e 100644 --- a/source/val/validate_mesh_shading.cpp +++ b/source/val/validate_mesh_shading.cpp @@ -15,6 +15,7 @@ // Validates ray query instructions from SPV_KHR_ray_query #include "source/opcode.h" +#include "source/spirv_target_env.h" #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" @@ -22,6 +23,25 @@ namespace spvtools { namespace val { +bool IsInterfaceVariable(ValidationState_t& _, const Instruction* inst, + spv::ExecutionModel model, uint32_t& entryPointId) { + bool foundInterface = false; + for (auto entry_point : _.entry_points()) { + const auto* models = _.GetExecutionModels(entry_point); + if (models->find(model) == models->end()) return false; + for (const auto& desc : _.entry_point_descriptions(entry_point)) { + bool found = false; + for (auto interface : desc.interfaces) { + if (inst->id() == interface) { + foundInterface = true; + break; + } + } + } + } + return foundInterface; +} + spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) { const spv::Op opcode = inst->opcode(); switch (opcode) { @@ -103,7 +123,7 @@ spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Primitive Count must be a 32-bit unsigned int scalar"; } - + break; } @@ -111,7 +131,42 @@ spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) { // No validation rules (for the moment). break; } - + case spv::Op::OpVariable: { + if (_.HasCapability(spv::Capability::MeshShadingEXT)) { + uint32_t meshEntryPoint = 0; + bool meshInterfaceVar = IsInterfaceVariable( + _, inst, spv::ExecutionModel::MeshEXT, meshEntryPoint); + uint32_t fragEntryPoint = 0; + bool fragInterfaceVar = IsInterfaceVariable( + _, inst, spv::ExecutionModel::Fragment, fragEntryPoint); + + const spv::StorageClass storage_class = + inst->GetOperandAs(2); + bool storage_output = (storage_class == spv::StorageClass::Output); + bool storage_input = (storage_class == spv::StorageClass::Input); + + if (_.HasDecoration(inst->id(), spv::Decoration::PerPrimitiveEXT)) { + if (fragInterfaceVar && !storage_input) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "PerPrimitiveEXT decoration must be applied only to " + "variables in the Input Storage Class in the Fragment " + "Execution Model."; + } + + if (meshInterfaceVar && !storage_output) { + std::string vkerror = (spvIsVulkanEnv(_.context()->target_env)) + ? _.VkErrorID(4336) + : ""; + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << vkerror + << "PerPrimitiveEXT decoration must be applied only to " + "variables in the Output Storage Class in the " + "Storage Class in the MeshEXT Execution Model."; + } + } + } + break; + } default: break; } diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp index 199d8ed455b..698afcefc45 100644 --- a/source/val/validate_mode_setting.cpp +++ b/source/val/validate_mode_setting.cpp @@ -543,6 +543,15 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, "tessellation execution model."; } } + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.HasCapability(spv::Capability::MeshShadingEXT) && + inst->GetOperandAs(2) == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7330) + << "In mesh shaders using the MeshEXT Execution Model the " + "OutputVertices Execution Mode must be greater than 0"; + } + } break; case spv::ExecutionMode::OutputLinesEXT: case spv::ExecutionMode::OutputTrianglesEXT: @@ -557,6 +566,16 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, "execution " "model."; } + if (mode == spv::ExecutionMode::OutputPrimitivesEXT && + spvIsVulkanEnv(_.context()->target_env)) { + if (_.HasCapability(spv::Capability::MeshShadingEXT) && + inst->GetOperandAs(2) == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(7331) + << "In mesh shaders using the MeshEXT Execution Model the " + "OutputPrimitivesEXT Execution Mode must be greater than 0"; + } + } break; case spv::ExecutionMode::QuadDerivativesKHR: if (!std::all_of(models->begin(), models->end(), diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 896849930e0..d8b18c80163 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -1985,6 +1985,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04330); case 4334: return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04334); + case 4336: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04336); case 4337: return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04337); case 4345: @@ -2311,30 +2313,46 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808); case 6925: return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925); + case 7039: + return VUID_WRAP(VUID-Layer-Layer-07039); + case 7040: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-07040); case 7041: return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07041); + case 7042: + return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042); case 7043: return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07043); case 7044: return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044); case 7047: return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07047); + case 7048: + return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048); case 7049: return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07049); case 7050: return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07050); case 7053: return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07053); + case 7054: + return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07054); case 7055: return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07055); case 7056: return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07056); + case 7060: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-07060); case 7102: return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07102); - case 7320: - return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320); case 7290: return VUID_WRAP(VUID-StandaloneSpirv-Input-07290); + case 7320: + return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320); + case 7330: + return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07330); + case 7331: + return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07331); case 7650: return VUID_WRAP(VUID-StandaloneSpirv-Base-07650); case 7651: diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index 01049692da8..53545cf3311 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp @@ -4325,6 +4325,29 @@ TEST_F(ValidateBuiltIns, VulkanPrimitiveTriangleIndicesEXTSuccess) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateBuiltIns, VulkanPrimitiveTriangleIndicesEXTInvalidExecutionMode) { + const std::string declarations = R"( +%array = OpTypeArray %v3uint %uint_16 +%array_ptr = OpTypePointer Output %array +%var = OpVariable %array_ptr Output +%ptr = OpTypePointer Output %v3uint +)"; + const std::string body = R"( +%access = OpAccessChain %ptr %var %int_0 +)"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitiveTriangleIndicesEXT", + "OutputPoints", body, declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveTriangleIndicesEXT-" + "PrimitiveTriangleIndicesEXT-07054")); + +} + TEST_F(ValidateBuiltIns, VulkanPrimitiveTriangleIndicesEXTStorageClass) { const std::string declarations = R"( %array = OpTypeArray %v3uint %uint_16 @@ -4408,6 +4431,29 @@ TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTSuccess) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTInvalidExecutionMode) { + const std::string declarations = R"( + %array = OpTypeArray %v2uint %uint_16 + %array_ptr = OpTypePointer Output %array + %var = OpVariable %array_ptr Output + %ptr = OpTypePointer Output %v2uint + )"; + const std::string body = R"( + %access = OpAccessChain %ptr %var %int_0 + )"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitiveLineIndicesEXT", "OutputPoints", body, + declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048")); +} + + TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTStorageClass) { const std::string declarations = R"( %array = OpTypeArray %v2uint %uint_16 @@ -4471,6 +4517,28 @@ TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTSuccess) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } +TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTInvalidExecutionMode) { + const std::string declarations = R"( + %array = OpTypeArray %uint %uint_16 + %array_ptr = OpTypePointer Output %array + %var = OpVariable %array_ptr Output + %ptr = OpTypePointer Output %uint + )"; + const std::string body = R"( + %access = OpAccessChain %ptr %var %int_0 + )"; + + CompileSuccessfully( + GenerateMeshShadingCode("PrimitivePointIndicesEXT", "OutputTrianglesNV", + body, declarations) + .c_str(), + SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042")); +} + TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTStorageClass) { const std::string declarations = R"( %array = OpTypeArray %uint %uint_16 @@ -4515,6 +4583,192 @@ TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTType) { AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044")); } +TEST_F(ValidateBuiltIns, VulkanBuiltinPrimtiveIDWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_PrimitiveID + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputVertices 3 + OpExecutionMode %MainMesh OutputTrianglesNV + OpExecutionMode %MainMesh LocalSize 1 1 1 + OpSource Slang 1 + OpName %MainMesh "MainMesh" + OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId + OpDecorate %gl_PrimitiveID PerPrimitiveNV + %void = OpTypeVoid + %9 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %uint_0 = OpConstant %uint 0 + %v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %v3uint = OpTypeVector %uint 3 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%_ptr_Output_int = OpTypePointer Output %int +%_arr_int_int_1 = OpTypeArray %int %int_1 +%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1 +%gl_PrimitiveID = OpVariable %_ptr_Output__arr_int_int_1 Output + %MainMesh = OpFunction %void None %9 + %25 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimtiveIDWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_PrimitiveID + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputVertices 3 + OpExecutionMode %MainMesh OutputTrianglesNV + OpExecutionMode %MainMesh LocalSize 1 1 1 + OpSource Slang 1 + OpName %MainMesh "MainMesh" + OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId +%void = OpTypeVoid + %9 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%int_3 = OpConstant %int 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%uint_0 = OpConstant %uint 0 +%v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%v3uint = OpTypeVector %uint 3 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%_ptr_Output_int = OpTypePointer Output %int +%_arr_int_int_1 = OpTypeArray %int %int_1 +%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1 +%gl_PrimitiveID = OpVariable %_ptr_Output__arr_int_int_1 Output +%MainMesh = OpFunction %void None %9 + %25 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-PrimitiveId-PrimitiveId-07040")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_Layer + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputVertices 3 + OpExecutionMode %MainMesh OutputTrianglesNV + OpExecutionMode %MainMesh LocalSize 1 1 1 + OpSource Slang 1 + OpName %MainMesh "MainMesh" + OpDecorate %gl_Layer BuiltIn Layer + %void = OpTypeVoid + %9 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 +%uint_0 = OpConstant %uint 0 +%v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%v3uint = OpTypeVector %uint 3 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%_ptr_Output_int = OpTypePointer Output %int +%_arr_int_int_1 = OpTypeArray %int %int_1 +%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1 +%gl_Layer = OpVariable %_ptr_Output__arr_int_int_1 Output +%MainMesh = OpFunction %void None %9 + %25 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-07039")); +} + +TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexWithPerPrimitiveEXT) { + const std::string text = R"( + OpCapability MeshShadingEXT + OpCapability Shader + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_ViewportIndex + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputPrimitivesNV 1 + OpExecutionMode %MainMesh OutputVertices 3 + OpExecutionMode %MainMesh OutputTrianglesNV + OpExecutionMode %MainMesh LocalSize 1 1 1 + OpSource Slang 1 + OpName %MainMesh "MainMesh" + OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex +%void = OpTypeVoid +%9 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%float = OpTypeFloat 32 +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%int_3 = OpConstant %int 3 +%uint_0 = OpConstant %uint 0 +%v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%v3uint = OpTypeVector %uint 3 +%_ptr_Output_v3uint = OpTypePointer Output %v3uint +%_ptr_Output_int = OpTypePointer Output %int +%_arr_int_int_1 = OpTypeArray %int %int_1 +%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1 +%gl_ViewportIndex = OpVariable %_ptr_Output__arr_int_int_1 Output +%MainMesh = OpFunction %void None %9 +%25 = OpLabel + OpSetMeshOutputsEXT %uint_3 %uint_1 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-ViewportIndex-ViewportIndex-07060")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_mesh_shading_test.cpp b/test/val/val_mesh_shading_test.cpp index a7b96a4edcc..afd1726bf47 100644 --- a/test/val/val_mesh_shading_test.cpp +++ b/test/val/val_mesh_shading_test.cpp @@ -467,6 +467,82 @@ TEST_F(ValidateMeshShading, TaskPayloadWorkgroupBadExecutionModel) { "TaskEXT and MeshKHR execution model")); } +TEST_F(ValidateMeshShading, BadMultipleTaskPayloadWorkgroupEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" %payload %payload1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint + %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT + %payload1 = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT + %main = OpFunction %void None %func + %label = OpLabel + %load = OpLoad %uint %payload + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("There can be at most one OpVariable with storage " + "class TaskPayloadWorkgroupEXT associated with " + "an OpEntryPoint")); +} + + +TEST_F(ValidateMeshShading, TaskPayloadWorkgroupTaskExtExecutionModel) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint TaskEXT %main "main" %payload + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint + %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT + %main = OpFunction %void None %func + %label = OpLabel + %load = OpLoad %uint %payload + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + + +TEST_F(ValidateMeshShading, TaskPayloadWorkgroupMeshExtExecutionModel) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %payload + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main OutputPrimitivesEXT 1 + OpExecutionMode %main OutputTrianglesEXT + %void = OpTypeVoid + %func = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint + %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT + %main = OpFunction %void None %func + %label = OpLabel + %load = OpLoad %uint %payload + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + + TEST_F(ValidateMeshShading, OpSetMeshOutputsBadVertexCount) { const std::string body = R"( OpCapability MeshShadingEXT @@ -598,6 +674,320 @@ TEST_F(ValidateMeshShading, OpEmitMeshTasksZeroSuccess) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); } +TEST_F(ValidateMeshShading, BadPerPrimitiveEXTStorageClassInMeshEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationID %blk %triangleNormal + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesNV 32 + OpExecutionMode %main OutputTrianglesNV + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %iid "iid" + OpName %gl_LocalInvocationID "gl_LocalInvocationID" + OpName %myblock "myblock" + OpMemberName %myblock 0 "f" + OpName %blk "blk" + OpName %triangleNormal "triangleNormal" + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpMemberDecorate %myblock 0 PerPrimitiveEXT + OpDecorate %myblock Block + OpDecorate %blk Location 0 + OpDecorate %triangleNormal PerPrimitiveEXT + OpDecorate %triangleNormal Location 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %float = OpTypeFloat 32 + %myblock = OpTypeStruct %float + %uint_32 = OpConstant %uint 32 +%_arr_myblock_uint_32 = OpTypeArray %myblock %uint_32 +%_ptr_Output__arr_myblock_uint_32 = OpTypePointer Output %_arr_myblock_uint_32 + %blk = OpVariable %_ptr_Output__arr_myblock_uint_32 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_11 = OpConstant %float 11 +%_ptr_Output_float = OpTypePointer Output %float + %v3float = OpTypeVector %float 3 +%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32 +%_ptr_Output__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32 +%triangleNormal = OpVariable %_ptr_Output__arr_v3float_uint_32 Input + %33 = OpConstantComposite %v3float %float_11 %float_11 %float_11 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %iid = OpVariable %_ptr_Function_uint Function + %14 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0 + %15 = OpLoad %uint %14 + OpStore %iid %15 + %22 = OpLoad %uint %iid + %27 = OpAccessChain %_ptr_Output_float %blk %22 %int_0 + OpStore %27 %float_11 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("PerPrimitiveEXT decoration must be applied only to " + "variables in the Output Storage Class in the Storage " + "Class in the MeshEXT Execution Model.")); +} + +TEST_F(ValidateMeshShading, VulkanPerPrimitiveEXTStorageClassInMeshEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationID %blk %triangleNormal + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesNV 32 + OpExecutionMode %main OutputTrianglesNV + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %iid "iid" + OpName %gl_LocalInvocationID "gl_LocalInvocationID" + OpName %myblock "myblock" + OpMemberName %myblock 0 "f" + OpName %blk "blk" + OpName %triangleNormal "triangleNormal" + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpMemberDecorate %myblock 0 PerPrimitiveEXT + OpDecorate %myblock Block + OpDecorate %blk Location 0 + OpDecorate %triangleNormal PerPrimitiveEXT + OpDecorate %triangleNormal Location 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid + %3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint +%float = OpTypeFloat 32 +%myblock = OpTypeStruct %float +%uint_32 = OpConstant %uint 32 +%_arr_myblock_uint_32 = OpTypeArray %myblock %uint_32 +%_ptr_Output__arr_myblock_uint_32 = OpTypePointer Output %_arr_myblock_uint_32 +%blk = OpVariable %_ptr_Output__arr_myblock_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float_11 = OpConstant %float 11 +%_ptr_Output_float = OpTypePointer Output %float +%v3float = OpTypeVector %float 3 +%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32 +%_ptr_Output__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32 +%triangleNormal = OpVariable %_ptr_Output__arr_v3float_uint_32 Input +%33 = OpConstantComposite %v3float %float_11 %float_11 %float_11 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 +%main = OpFunction %void None %3 + %5 = OpLabel +%iid = OpVariable %_ptr_Function_uint Function +%14 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0 +%15 = OpLoad %uint %14 + OpStore %iid %15 +%22 = OpLoad %uint %iid +%27 = OpAccessChain %_ptr_Output_float %blk %22 %int_0 + OpStore %27 %float_11 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-PrimitiveId-PrimitiveId-04336")); +} + +TEST_F(ValidateMeshShading, BadPerPrimitiveEXTStorageClassInFrag) { + const std::string body = R"( + OpCapability Shader + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %triangleNormal + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %triangleNormal "triangleNormal" + OpDecorate %triangleNormal PerPrimitiveNV + OpDecorate %triangleNormal Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Output %_arr_v3float_uint_3 +%triangleNormal = OpVariable %_ptr_Input__arr_v3float_uint_3 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_Input_v3float %triangleNormal %int_0 + %19 = OpLoad %v3float %18 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("PerPrimitiveEXT decoration must be applied only to " + "variables in the Input Storage Class in the Fragment " + "Execution Model.")); +} + +TEST_F(ValidateMeshShading, PerPrimitiveEXTStorageClassInFrag) { + const std::string body = R"( + OpCapability Shader + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %res3 %triangleNormal + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %res3 "res3" + OpName %triangleNormal "triangleNormal" + OpDecorate %res3 Location 0 + OpDecorate %triangleNormal PerPrimitiveNV + OpDecorate %triangleNormal Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %_ptr_Output_v3float = OpTypePointer Output %v3float + %res3 = OpVariable %_ptr_Output_v3float Output + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 + %_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3 + %triangleNormal = OpVariable %_ptr_Input__arr_v3float_uint_3 Input + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %_ptr_Input_v3float = OpTypePointer Input %v3float + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_Input_v3float %triangleNormal %int_0 + %19 = OpLoad %v3float %18 + OpStore %res3 %19 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateMeshShading, PerPrimitiveEXTStorageClassInMeshEXT) { + const std::string body = R"( + OpCapability MeshShadingEXT + OpExtension "SPV_EXT_mesh_shader" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationID %blk %triangleNormal + OpExecutionMode %main LocalSize 32 1 1 + OpExecutionMode %main OutputVertices 81 + OpExecutionMode %main OutputPrimitivesNV 32 + OpExecutionMode %main OutputTrianglesNV + OpSource GLSL 450 + OpSourceExtension "GL_EXT_mesh_shader" + OpName %main "main" + OpName %iid "iid" + OpName %gl_LocalInvocationID "gl_LocalInvocationID" + OpName %myblock "myblock" + OpMemberName %myblock 0 "f" + OpName %blk "blk" + OpName %triangleNormal "triangleNormal" + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpMemberDecorate %myblock 0 PerPrimitiveNV + OpDecorate %myblock Block + OpDecorate %blk Location 0 + OpDecorate %triangleNormal PerPrimitiveNV + OpDecorate %triangleNormal Location 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %float = OpTypeFloat 32 + %myblock = OpTypeStruct %float + %uint_32 = OpConstant %uint 32 +%_arr_myblock_uint_32 = OpTypeArray %myblock %uint_32 +%_ptr_Output__arr_myblock_uint_32 = OpTypePointer Output %_arr_myblock_uint_32 + %blk = OpVariable %_ptr_Output__arr_myblock_uint_32 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_11 = OpConstant %float 11 +%_ptr_Output_float = OpTypePointer Output %float + %v3float = OpTypeVector %float 3 +%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32 +%_ptr_Output__arr_v3float_uint_32 = OpTypePointer Output %_arr_v3float_uint_32 +%triangleNormal = OpVariable %_ptr_Output__arr_v3float_uint_32 Output + %33 = OpConstantComposite %v3float %float_11 %float_11 %float_11 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %iid = OpVariable %_ptr_Function_uint Function + %14 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0 + %15 = OpLoad %uint %14 + OpStore %iid %15 + %22 = OpLoad %uint %iid + %27 = OpAccessChain %_ptr_Output_float %blk %22 %int_0 + OpStore %27 %float_11 + %32 = OpLoad %uint %iid + %35 = OpAccessChain %_ptr_Output_v3float %triangleNormal %32 + OpStore %35 %33 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index a0ea4288dca..69c055bce0e 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp @@ -814,6 +814,110 @@ OpExecutionMode %main OutputPoints EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateModeExecution, MeshEXTOutputVertices) { + const std::string spirv = R"( +OpCapability MeshShadingEXT +OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshEXT %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main OutputVertices 3 +OpExecutionMode %main OutputPrimitivesNV 1 +OpExecutionMode %main OutputTrianglesNV +OpSource GLSL 460 +OpSourceExtension "GL_EXT_mesh_shader" +OpName %main "main" +OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +OpSetMeshOutputsEXT %uint_3 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + + +TEST_F(ValidateModeExecution, VulkanBadMeshEXTOutputVertices) { + const std::string spirv = R"( +OpCapability MeshShadingEXT +OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshEXT %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main OutputVertices 0 +OpExecutionMode %main OutputPrimitivesNV 1 +OpExecutionMode %main OutputTrianglesNV +OpSource GLSL 460 +OpSourceExtension "GL_EXT_mesh_shader" +OpName %main "main" +OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +OpSetMeshOutputsEXT %uint_3 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-ExecutionModel-07330")); +} + +TEST_F(ValidateModeExecution, VulkanBadMeshEXTOutputOutputPrimitivesEXT) { + const std::string spirv = R"( +OpCapability MeshShadingEXT +OpExtension "SPV_EXT_mesh_shader" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshEXT %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpExecutionMode %main OutputVertices 1 +OpExecutionMode %main OutputPrimitivesNV 0 +OpExecutionMode %main OutputTrianglesNV +OpSource GLSL 460 +OpSourceExtension "GL_EXT_mesh_shader" +OpName %main "main" +OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 +%main = OpFunction %void None %3 +%5 = OpLabel +OpSetMeshOutputsEXT %uint_3 %uint_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-ExecutionModel-07331")); +} + TEST_F(ValidateModeExecution, MeshNVOutputVertices) { const std::string spirv = R"( OpCapability Shader