Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EXT_mesh_shader validation support #5640

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

pmistryNV
Copy link
Contributor

@pmistryNV pmistryNV commented Apr 10, 2024

  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

For bug #4919

@pmistryNV pmistryNV force-pushed the mesh_shader_Validation branch 2 times, most recently from fdd61c9 to 7e583d0 Compare April 10, 2024 22:24
@pmistryNV
Copy link
Contributor Author

@spencer-lunarg kindly review.

Copy link
Contributor

@spencer-lunarg spencer-lunarg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to do a deep review soon, but some small nits from a quick scan

source/val/validate_builtins.cpp Outdated Show resolved Hide resolved
source/val/validate_mesh_shading.cpp Outdated Show resolved Hide resolved
source/val/validate_builtins.cpp Outdated Show resolved Hide resolved
Comment on lines 147 to 148
if (target->opcode() != spv::Op::OpVariable) {
return fail(0) << "must be a memory object declaration";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory object declarations include OpFunctionParameter (and are handled in the next block of decorations). The spec matches that, is this a bug here or in the extension?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the SPIRV spec SPV_EXT_mesh_shader

PerPrimitiveEXT
Must only be used on a memory object declaration or a member of a structure type. Indicates that the variable has separate instances for each primitive in the output

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving it under the next block of decorations

Comment on lines 370 to 377
const auto target_id = inst->GetOperandAs<uint32_t>(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";
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it would be caught above already at line 346.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I am going to remove it

@@ -667,6 +667,36 @@ class BuiltInsValidator {
// instruction.
void Update(const Instruction& inst);

uint32_t GetMeshEntryPoint() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you only have a single mesh entry point in a module? I'm confused why this is a single entry point.

Copy link
Contributor Author

@pmistryNV pmistryNV Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will refactor the code to address this issue in updated commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kindly review the updated code.

@@ -779,6 +780,19 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
}
const spv::StorageClass storage_class =
var_instr->GetOperandAs<spv::StorageClass>(2);
if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 5)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this version gated on 1.5? SPV_EXT_mesh_shader requires SPIR-V 1.4. What is intended to be checked here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be 1.4, I am not sure why i added 1.5.
The intention here is to satisfy the requirement in SPV_EXT_mesh_shader spec
"There can be at most one of type OpVariable with storage class TaskPayloadWorkgroupEXT associated with an OpEntryPoint. This OpVariable should be a global OpVariable."

Basically we cannot have more then one taskPayloadSharedEXT type global variable associated with one entrypoint

sudonatalie added a commit to sudonatalie/DirectXShaderCompiler that referenced this pull request Apr 12, 2024
The existing logic from VK_NV_mesh_shader was incorrectly adapted
for the VK_EXT_mesh_shader implementation when it comes to the handling
of mesh payloads as in/out variables. Because TaskPayloadWorkgroupEXT
must be applied to a single global OpVariable for each Task/Mesh shader,
the struct should not be flattened. Further, as far as I can tell,
Location assignment is not necessary for these input and output
variables, so the usual reason for flattening structs does not apply.

This change now removes the inner struct member global variables and
ensures the parent payload is decorated with TaskPayloadWorkgroupEXT.
Note that for amplification/task shaders, the payload variable is
created with the groupshared decl, and then it's storage class needs to
be updated when that variable is used as a parameter to the DispatchMesh
call, as described in: https://docs.vulkan.org/spec/latest/proposals/proposals/VK_EXT_mesh_shader.html#_hlsl_changes

Tested with updated spirv-val from: KhronosGroup/SPIRV-Tools#5640

Fixes microsoft#5981
sudonatalie added a commit to microsoft/DirectXShaderCompiler that referenced this pull request Apr 17, 2024
)

The existing logic from `VK_NV_mesh_shader` was incorrectly adapted for
the `VK_EXT_mesh_shader` implementation when it comes to the handling of
payloads as in/out variables. Because `TaskPayloadWorkgroupEXT` must be
applied to a single global `OpVariable` for each task/mesh shader, the
struct should not be flattened. Further, Location assignment is not
necessary for these input and output variables, so the usual reason for
flattening structs does not apply.

This change now removes the inner struct member global variables and
ensures the parent payload is decorated with `TaskPayloadWorkgroupEXT`.
Note that for amplification/task shaders, the payload variable is
created with the `groupshared` decl, and then its storage class needs to
be updated when that variable is used as a parameter to the
`DispatchMesh` call, as described in:
https://docs.vulkan.org/spec/latest/proposals/proposals/VK_EXT_mesh_shader.html#_hlsl_changes

Tested with new validation checks from:
KhronosGroup/SPIRV-Tools#5640

Fixes #5981
@spencer-lunarg
Copy link
Contributor

@pmistryNV any update on the changes for this PR?

@danginsburg
Copy link
Contributor

Any updates on when this might be completed? I'm adding VK_EXT_mesh_shader support to our engine and would like to make sure validation is hooked up properly.

@KarenGhavam-lunarG
Copy link

Any updates on when this might be completed? I'm adding VK_EXT_mesh_shader support to our engine and would like to make sure validation is hooked up properly.

It would be good to know when this will be completed. I have had people from the ecosystem complain about lack of validation support for this extension

@pmistryNV pmistryNV force-pushed the mesh_shader_Validation branch 2 times, most recently from b45cd0d to d46e54b Compare January 7, 2025 07:36
@@ -163,6 +163,7 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
case spv::Decoration::Stream:
case spv::Decoration::RestrictPointer:
case spv::Decoration::AliasedPointer:
case spv::Decoration::PerPrimitiveNV:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
case spv::Decoration::PerPrimitiveNV:
case spv::Decoration::PerPrimitiveEXT: // alias PerPrimitiveNV

(makes it easier to grep/code search for the EXT name in the future)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

uint32_t primitiveArrayDim = 0;
// Strip the array, if present.
if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
underlying_type = _.FindDef(underlying_type)->word(3u);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use EvalConstantValUint64 here. That can handle some more cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kindly review the updated code that uses EvalConstantValUint64 to get the constant value

models->find(spv::ExecutionModel::MeshNV) != models->end()) {
for (const auto& desc : _.entry_point_descriptions(entry_point)) {
for (auto interface : desc.interfaces) {
if (inst.opcode() == spv::Op::OpTypeStruct) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems incorrect. The interface should be an OpVariable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interface is OpVariable but the mesh builtin definition instruction may not be. Below is the scenario that I am trying to address. Probably I can change the name if you have a better suggestion.

OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT

OpDecorate %gl_MeshPerPrimitiveEXT Block
OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveShadingRateKHR

%gl_MeshPerPrimitiveEXT = OpTypeStruct %int
%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In here PrimitiveShadingRateKHR is member of a block . Block is the interface variable. I need to validate the builtin.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was misreading the code. Thanks for the clarification

@@ -2382,30 +2384,62 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06924);
case 6925:
return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925);
case 7034:
return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07034);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should have a test for this VU

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add it in the updated commit.

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-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07046
   VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048
   VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07052
   VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07054
   VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07058
   VUID-ViewportIndex-ViewportIndex-07060
   VUID-StandaloneSpirv-ExecutionModel-07330
   VUID-StandaloneSpirv-ExecutionModel-07331
   VUID-PrimitiveId-PrimitiveId-04336
   VUID-Layer-Layer-07039
   VUID-ViewportIndex-ViewportIndex-07060

   VUID-CullPrimitiveEXT-CullPrimitiveEXT-07034
   VUID-CullPrimitiveEXT-CullPrimitiveEXT-07035
   VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036
@pmistryNV pmistryNV force-pushed the mesh_shader_Validation branch from 1bdf304 to da790d9 Compare January 9, 2025 19:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants