-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[HLSL] Add descriptor table metadata parsing #142492
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
[HLSL] Add descriptor table metadata parsing #142492
Conversation
return Flags == FlagT::DESCRIPTORS_VOLATILE; | ||
} | ||
|
||
// The data-specific flags are mutually exclusive. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the descriptor flags are also mutually exclusive. Do we want an explicit check for this? It seems like it is covered below. Maybe it is good for clarity?
DESCRIPTOR_RANGE(1, UAV) | ||
DESCRIPTOR_RANGE(2, CBV) | ||
DESCRIPTOR_RANGE(3, Sampler) | ||
DESCRIPTOR_RANGE(4, NONE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have a value for NONE here? A value of 4 in the metadata would certainly be invalid, and this appears to be unused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This came as a request from another PR, where Finn mentioned those values are useful in the frontend, since we are planning on sharing this, I've added those back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a link to that conversation handy? I'd like to see how NONE would be used here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is an example use case of NONE in the frontend:
std::optional<RootFlags> Flags = RootFlags::None; |
But that discussion was in the context of flags, I will remove this one from here, since it doesn't seem to match the pattern of other NONE values
.Default(-1u); | ||
|
||
if (Range.RangeType == -1u) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clearer to use ~0U
rather than -1U
, and this way avoids some warnings from MSVC in any case.
@@ -174,6 +174,94 @@ static bool parseRootDescriptors(LLVMContext *Ctx, | |||
return false; | |||
} | |||
|
|||
static bool parseDescriptorRange(LLVMContext *Ctx, | |||
mcdxbc::RootSignatureDesc &RSD, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is RSD passed in here?
if (Version == 1) { | ||
if (IsSampler) | ||
return Flags == FlagT::NONE; | ||
return Flags == FlagT::DESCRIPTORS_VOLATILE; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These values are still incorrect. As I mentioned in #142492 (comment) samplers can only have DESCRIPTORS_VOLATILE, and otherwise this needs to be DATA_VOLATILE.
Also, please add a test for this - we should be catching that this is obviously wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is some confusion in the spec, there are 2 sections saying different values are valid for this field. Reached in private to discuss this.
I'll add the test.
// When no descriptor flag is set, any data flag is allowed. | ||
return (Flags & ~DataFlags) == FlagT::NONE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct. Samplers can never have the DATA_*
flags set.
OS << indent(2) << "- Range Type: " << Range.RangeType << "\n"; | ||
OS << indent(4) << "Register Space: " << Range.RegisterSpace << "\n"; | ||
OS << indent(4) | ||
<< "Base Shader Register: " << Range.BaseShaderRegister << "\n"; | ||
OS << indent(4) << "Num Descriptors: " << Range.NumDescriptors | ||
<< "\n"; | ||
OS << indent(4) << "Offset In Descriptors From Table Start: " | ||
<< Range.OffsetInDescriptorsFromTableStart << "\n"; | ||
if (RS.Version > 1) | ||
OS << indent(4) << "Flags: " << Range.Flags << "\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's just me, but I find using the indent()
function with a small constant to be quite a bit less clear than just putting the spaces in the strings. Consider:
OS << " - Range Type: " << Range.RangeType << "\n"
<< " Register Space: " << Range.RegisterSpace << "\n"
<< " Base Shader Register: " << Range.BaseShaderRegister << "\n"
<< " Num Descriptors: " << Range.NumDescriptors << "\n"
<< " Offset In Descriptors From Table Start: "
<< Range.OffsetInDescriptorsFromTableStart << "\n";
if (RS.Version > 1)
OS << " Flags: " << Range.Flags << "\n";
Here, you can see how the strings line up just by looking at them. Though admittedly it can get a bit messier with longer lines. IMO indent()
is mostly useful when the indentation varies depending on context, or is large enough that just writing it out makes things less readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM other than a couple of fairly minor comments inline.
const bool IsSampler = | ||
(Type == llvm::to_underlying(dxbc::DescriptorRangeType::Sampler)); | ||
|
||
if (Version == 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth a comment.
if (Version == 1) { | |
if (Version == 1) { | |
// Since the metadata is unversioned, we expect to explicitly see the values | |
// that map to the version 1 behaviour here. |
// When no descriptor flag is set, any data flag is allowed. | ||
if (!IsSampler) | ||
return (Flags & ~DataFlags) == FlagT::NONE; | ||
return (Flags & ~FlagT::NONE) == FlagT::NONE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels weird to do this case differently than above, since they're doing the same thing. Technically we could simplify this just by removing the DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS
if condition, since that logic should be correct whether or not that flag is set, but I can see why you might avoid that so that the comments and the code flow are clear. In that case, I guess we should repeat the explicit approach as we did in the two other cases above:
// When no descriptor flag is set, any data flag is allowed. | |
if (!IsSampler) | |
return (Flags & ~DataFlags) == FlagT::NONE; | |
return (Flags & ~FlagT::NONE) == FlagT::NONE; | |
// When no descriptor flag is set, any data flag is allowed. | |
FlagT Mask = FlagT::None; | |
if (!IsSampler) { | |
Mask |= FlagT::DATA_VOLATILE; | |
Mask |= FlagT::DATA_STATIC; | |
Mask |= FlagT::DATA_STATIC_WHILE_SET_AT_EXECUTE; | |
} | |
return (Flags & ~Mask) == FlagT::NONE; |
|
||
if (!verifyDescriptorRangeFlag(RSD.Version, Range.RangeType, | ||
Range.Flags)) | ||
return reportValueError(Ctx, "DescriptorFlag", Range.Flags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this differentiate from non-range DescriptorFlag?
return reportValueError(Ctx, "DescriptorFlag", Range.Flags); | |
return reportValueError(Ctx, "DescriptorRangeFlag", Range.Flags); |
; DXC-NEXT: NumDescriptors: 0 | ||
; DXC-NEXT: BaseShaderRegister: 1 | ||
; DXC-NEXT: RegisterSpace: 0 | ||
; DXC-NEXT: OffsetInDescriptorsFromTableStart: 4294967295 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OffsetInDescriptorsFromTableStart
, is the 5th value in the metadata representation, in the first range this value is -1, which denote the special case where it was set to D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND
, here is a link to the docs describing this: OffsetInDescriptorsFromTableStart
Implements descriptor table parsing from root signature metadata. This is required to support root signatures in hlsl. Closes: #[126640](llvm#126640) --------- Co-authored-by: joaosaffran <[email protected]>
Implements descriptor table parsing from root signature metadata. This is required to support root signatures in hlsl.
Closes: #126640