-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[DirectX] adding support in obj2yaml and yaml2obj to root constants #127840
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
Conversation
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.
A couple of minor things but this is looking fairly close.
My one major concern is that the error path of obj2yaml
behaves quite poorly and isn't tested.
Given the following:
// RUN: dxc -T cs_6_5 -rootsig-define ROOT_SIGNATURE %s -Fo %t.dxil
#define ROOT_SIGNATURE "SRV(t0),RootConstants(b0, num32BitConstants = 4),"
[numthreads(1,1,1)]
void main() {}
Running obj2yaml
on the output prints "Error: Invalid value for parameter type", generates YAML like the following, and exits successfully:
- Name: RTS0
Size: 72
RootSignature:
Version: 2
NumStaticSamplers: 0
StaticSamplersOffset: 72
Parameters:
- ParameterType: Constants32Bit
ShaderVisibility: All
Constants:
Num32BitValues: 4
RegisterSpace: 0
ShaderRegister: 0
Other than a single line of text that's easy to miss, there's no indication that anything is wrong here at all. We may need to think about what behaviour we want here.
#define ROOT_PARAMETER(Val, Enum) \ | ||
case Val: \ | ||
return dxbc::RootParameterType::Enum; | ||
inline llvm::Expected<dxbc::RootParameterType> | ||
safeParseParameterType(uint32_t V) { | ||
switch (V) { | ||
#include "DXContainerConstants.def" | ||
} | ||
return createStringError(std::errc::invalid_argument, | ||
"Invalid value for parameter type"); | ||
} |
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 might be simpler to just make this a "isValid" function and let the caller figure out what they want to do with it, ie:
#define SHADER_VISIBILITY(Val, Enum) \
case Val: \
return true;
inline bool isValidShaderVisibility(uint32_t V) {
switch (V) {
#include "DXContainerConstants.def"
}
return false;
}
and then in DXContainerYAML:
if (!dxbc::isValidParameterType(PH.ParameterType)) {
llvm::errs() << "Error: Unknown parameter: " << PH.ParameterType << "\n";
continue;
}
NewP.Type = static_cast<dxbc::RootParameterType>(PH.ParameterType);
if (Error E = ParamViewOrErr.takeError()) { | ||
llvm::errs() << "Error: " << E << "\n"; | ||
continue; | ||
} |
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.
You need to "consume" the error here, otherwise we crash like so when this is run in asserts:
Program aborted due to an unhandled Error:
Invalid value for parameter type
The easiest way to do that if we just want to print it is to std::move
the error into a toString
call:
llvm::errs() << "Error: " << toString(std::move(E)) << "\n";
We should really have tests for the error paths here so that this is obvious.
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.
I've added 2 new tests to address this scenario and a scenario for invalid shader visibility. I've also changed how the data is created, so we can propagate the errors properly.
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.
Just nitpicks left on the code. This looks good.
Please add a couple of tests where RootParametersOffset
is some number larger than 24 (one reading and one writing)
switch (Param.Type) { | ||
|
||
case static_cast<uint32_t>(dxbc::RootParameterType::Constants32Bit): |
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.
Unusual whitespace
switch (Param.Type) { | |
case static_cast<uint32_t>(dxbc::RootParameterType::Constants32Bit): | |
switch (Param.Type) { | |
case static_cast<uint32_t>(dxbc::RootParameterType::Constants32Bit): |
Val = (Flags & (uint32_t)dxbc::RootElementFlag::Val) > 0; | ||
RootSigDesc.Val = (Flags & (uint32_t)dxbc::RootElementFlag::Val) > 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.
I realize this isn't added, but it's probably clearer to use llvm::to_underlying
instead of the cast here (We don't have std::to_underlying
yet, that's c++23)
IO.mapRequired("ShaderVisibility", P.Visibility); | ||
|
||
switch (P.Type) { | ||
case static_cast<uint32_t>(dxbc::RootParameterType::Constants32Bit): |
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.
Better to use llvm::to_underlying
size_t Size = sizeof(dxbc::RootSignatureHeader) + | ||
Parameters.size() * sizeof(dxbc::RootParameterHeader); | ||
|
||
for (const auto &P : Parameters) { |
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.
Best to only use auto when the type is somewhere in the expression (or it's something obvious but long like an iterator)
for (const auto &P : Parameters) { | |
for (const mcdxbc::RootParameter &P : Parameters) { |
support::endian::write(BOS, Flags, llvm::endianness::little); | ||
|
||
SmallVector<uint32_t> ParamsOffsets; | ||
for (const auto &P : Parameters) { |
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.
Same here
DataSize = sizeof(dxbc::RootConstants); | ||
break; | ||
} | ||
auto EndOfSectionByte = getNumStaticSamplers() == 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.
size_t is clearer here.
auto EndOfSectionByte = getNumStaticSamplers() == 0 | |
size_t EndOfSectionByte = getNumStaticSamplers() == 0 |
assert(NumParameters == ParamsOffsets.size()); | ||
for (size_t I = 0; I < NumParameters; ++I) { | ||
rewriteOffsetToCurrentByte(BOS, ParamsOffsets[I]); | ||
const auto &P = Parameters[I]; |
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.
Another case of auto being less clear
uint32_t Flags = Data.getFlags(); | ||
for (const auto &PH : Data.param_headers()) { |
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 one too
I think you misunderstood me. I didn't mean we needed tests where we had an incorrect / invalid |
@@ -822,14 +823,65 @@ TEST(DXCFile, MalformedSignature) { | |||
} | |||
} | |||
|
|||
TEST(RootSignature, MalformedData) { |
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.
Need to rename this - this test isn't malformed
NumRootParameters: 2 | ||
RootParametersOffset: 24 | ||
NumStaticSamplers: 0 | ||
StaticSamplersOffset: 12 |
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 use 12 here? It's good to test that the value is round-tripped correctly, but it seems odd to use an obviously silly value (this says the samplers start in the middle of the header!). Probably better to use some number that would be after the parameters.
@@ -19,6 +19,7 @@ | |||
#include "llvm/Support/Errc.h" | |||
#include "llvm/Support/Error.h" | |||
#include "llvm/Support/raw_ostream.h" | |||
#include <cstdint> |
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.
Is cstdint
still needed?
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.
Thanks, missed that. It has been removed
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.
Two style nits, otherwise LGTM.
if (Error E = ParamViewOrErr.takeError()) { | ||
return std::move(E); | ||
} |
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.
if (Error E = ParamViewOrErr.takeError()) { | |
return std::move(E); | |
} | |
if (Error E = ParamViewOrErr.takeError()) | |
return std::move(E); |
if (Error E = ConstantsOrErr.takeError()) { | ||
return std::move(E); | ||
} |
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.
if (Error E = ConstantsOrErr.takeError()) { | |
return std::move(E); | |
} | |
if (Error E = ConstantsOrErr.takeError()) | |
return std::move(E); |
…lvm#127840) Adding support for Root Constant in MC, Object and obj2yaml and yaml2obj, this PR adds: - new structures to dxbc definition. - serialize and desirialize logic from dxcontainer to yaml - tests validating against dxc - adding support to multiple parts. Closes: llvm#126633 --------- Co-authored-by: joaosaffran <[email protected]>
Adding support for Root Constant in MC, Object and obj2yaml and yaml2obj, this PR adds:
Closes: #126633