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

Support SV_DispatchGrid semantic in a nested record #6931

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

tcorringham
Copy link
Collaborator

The SV_DispatchGrid DXIL metadata for a node input record was not generated in cases where:

  • the field with the SV_DispatchGrid semantic was in a nested record
  • the field with the SV_DispatchGrid semantic was in a record field
  • the field with the SV_DispatchGrid semantic was inherited from a base record
  • in any combinations of the above

Added FindDispatchGridSemantic() to be used by the AddHLSLNodeRecordTypeInfo() function, and added a test case.

Fixes #6928

The SV_DispatchGrid DXIL metadata for a node input record was not generated
in cases where:
- the field with the SV_DispatchGrid semantic was in a nested record
- the field with the SV_DispatchGrid semantic was in a record field
- the field with the SV_DispatchGrid semantic was inherited from a base record
- in any combinations of the above

Added FindDispatchGridSemantic() to be used by the AddHLSLNodeRecordTypeInfo()
function, and added a test case.

Fixes microsoft#6928
@tcorringham tcorringham self-assigned this Sep 24, 2024
@tcorringham tcorringham requested a review from a team as a code owner September 24, 2024 15:31
@damyanp damyanp assigned tex3d and unassigned tcorringham Sep 26, 2024
Copy link
Collaborator

@llvm-beanz llvm-beanz left a comment

Choose a reason for hiding this comment

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

We should also make sure we have error detection in Sema for the case of multiple fields in a single or nested structure definition having SV_DispatchGrid applied. Since the CodeGen can only handle a single declaration, we should error if a structure has more than one annotated field.

// Collect any non-virtual bases.
SmallVector<const CXXRecordDecl *, 4> Bases;
for (const CXXBaseSpecifier &Base : RD->bases()) {
if (!Base.isVirtual() && !Base.getType()->isDependentType())
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume it isn't actually possible in HLSL to have virtual base classes right? The language certainly shouldn't allow them, so I think this check is probably unnecessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, virtual base classes aren't possible - but the dependent type is, so we do need that check here. This condition just follows the style and layout of similar checks elsewhere - the isVirtual() check could safely be removed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yea, I think we should remove the isVirtual check since that will always be false and I can't imagine any world where we'd be adding virtual inheritance to HLSL...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

@tcorringham
Copy link
Collaborator Author

The presence of more than one SV_DispatchGrid field is already detected in Sema, and covers the cases of base classes and nested records.

Remove the check for a virtual base class from the code in
FindDispatchGridSemantic() as virtual classes can't appear
in HLSL code.
[NumThreads(32,1,1)]
void node5(DispatchNodeInputRecord<Record5> input) {}
// CHECK: , i32 1, ![[SVDG_5:[0-9]+]]
// CHECK: [[SVDG_5]] = !{i32 20, i32 5, i32 2}
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about some test cases with templates?

Something like:

template <typename T>
struct Base {
  T DG : SV_DispatchGrid;
};

struct Derived1 : Base<uint3> {
  int4 x;
};

[Shader("node")]
[NodeLaunch("broadcasting")]
[NodeMaxDispatchGrid(32,16,1)]
[NumThreads(32,1,1)]
void node6(DispatchNodeInputRecord<Derived1 > input) {}

template <typename T>
struct Derived2 : Base<T> {
  T Y;
};

[Shader("node")]
[NodeLaunch("broadcasting")]
[NodeMaxDispatchGrid(32,16,1)]
[NumThreads(32,1,1)]
void node7(DispatchNodeInputRecord<Derived2<uint2> > input) {}


template <typename T>
struct Derived3 {
  Derived2<T> V;
};

[Shader("node")]
[NodeLaunch("broadcasting")]
[NodeMaxDispatchGrid(32,16,1)]
[NumThreads(32,1,1)]
void node8(DispatchNodeInputRecord< Derived3 <uint3> > input) {}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Great idea! I've updated the test to include these cases.

Added test cases to cover nested SV_DispatchGrid used in
records using templates.
@damyanp damyanp requested a review from llvm-beanz January 8, 2025 18:43
@damyanp damyanp requested review from pow2clk and bob80905 January 30, 2025 19:29
@damyanp
Copy link
Member

damyanp commented Jan 30, 2025

@llvm-beanz - could you have a look at this with some urgency please (we want it in the upcoming release) when you're back?

Copy link
Member

@pow2clk pow2clk left a comment

Choose a reason for hiding this comment

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

Looks good! I think the parts I'm most concerned about are the repeated checks of parent classes and that I might not understand the motive for sorting base offsets. The other stuff is minor.

return true;
}
// Otherwise check this field for the SV_DispatchGrid semantic annotation
for (const hlsl::UnusualAnnotation *it : Field->getUnusualAnnotations()) {
Copy link
Member

Choose a reason for hiding this comment

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

could/should this be an else case? I don't think we can apply SV_DispatchGrid to a record?

Copy link
Contributor

Choose a reason for hiding this comment

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

According to the way semantics normally work, you could do this:

struct MyVec3 {
  uint3 value;
};
struct Record {
  MyVec3 DispatchGrid : SV_DispatchGrid;
};

Other code here won't handle this though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Currently the checks that are implemented in Sema will error if the SV_DispatchGrid is applied to a record as in the example above.
So, either there could be an else or Sema (and code here) is wrong.
If SV_DispatchGrid can be applied to a record then we would have to check that the first/only(?) field is a suitable type, and deal with nested records/inheritance - which sounds fairly horrible.

SDGRec.NumComponents = 1;
SDGRec.ByteOffset = (unsigned)FieldOffset.getQuantity();
if (const llvm::VectorType *VT = dyn_cast<llvm::VectorType>(FTy)) {
SDGRec.NumComponents = VT->getNumElements();
Copy link
Member

Choose a reason for hiding this comment

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

These used to assert it was an integer. was that invalid?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The types are checked in Sema so we won't get this far if the type is wrong.

const llvm::Type *FTy = CGM.getTypes().ConvertType(Field->getType());
const llvm::Type *ElTy = FTy;
SDGRec.NumComponents = 1;
SDGRec.ByteOffset = (unsigned)FieldOffset.getQuantity();
Copy link
Member

Choose a reason for hiding this comment

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

The removal of the struct check is because of the above that iterates into any records?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, we iterate into records above.

[&](const CXXRecordDecl *L, const CXXRecordDecl *R) {
return Layout.getBaseClassOffset(L) <
Layout.getBaseClassOffset(R);
});
Copy link
Member

Choose a reason for hiding this comment

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

What does ordering the bases give us? I could imagine this might be intended to prevent duplicate testing, but If there are multiple levels of bases, it seems this results in checking the highest parent base once for each descendent.

Copy link
Collaborator Author

@tcorringham tcorringham Feb 6, 2025

Choose a reason for hiding this comment

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

We need to order the bases so we can keep track of the offset from the start of the input record.
EDIT: I have only now realized there can't be more than one concrete base in HLSL, so the sort step is not necessary after all.

If a record type appears more than once then it can't contain a field with SV_DispatchGrid as that would be errored in Sema, so we could track which records we have already checked and skip checking the fields of any we have seen before. However, I'm not sure that such cases would occur often enough to make the extra housekeeping worthwhile - so currently we may check records more than once.


// Collect any bases.
SmallVector<const CXXRecordDecl *, 4> Bases;
for (const CXXBaseSpecifier &Base : RD->bases()) {
Copy link
Member

Choose a reason for hiding this comment

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

If I understand this right, parent classes will be checked for every descendent and every struct that has a common ancestor. Maybe that's hard to avoid?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, as per my comment above I don't expect such cases to occur often, so I believe the extra housekeeping probably isn't worthwhile.

@damyanp damyanp added this to the Release 1.8.2502 milestone Jan 31, 2025
tools/clang/lib/CodeGen/CGHLSLMS.cpp Outdated Show resolved Hide resolved
return true;
}
// Otherwise check this field for the SV_DispatchGrid semantic annotation
for (const hlsl::UnusualAnnotation *it : Field->getUnusualAnnotations()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

According to the way semantics normally work, you could do this:

struct MyVec3 {
  uint3 value;
};
struct Record {
  MyVec3 DispatchGrid : SV_DispatchGrid;
};

Other code here won't handle this though.

Adopting code suggested in review - there can only be one concrete base so the sort step isn't required.

Co-authored-by: Tex Riddell <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: New
Status: Triaged
Development

Successfully merging this pull request may close these issues.

SV_DispatchGrid semantic in a nested record
5 participants