-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[DirectX] Add static sampler support to root signature #143422
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
Changes from all commits
0abacfc
8b8c02a
7ac9641
c105458
efe76aa
a928e9d
a38f10b
9a7c359
d6c2b55
93e4cf2
b45b1b6
f804a23
15eb6f5
b9d7f07
46cc8c1
1b3e10a
1f31957
e8fbfce
a31e5a5
a394ad0
ad415a7
8ff4845
f875555
4f7f998
3eb5e10
58e1789
81915ad
a515e28
0d54162
7f70dc5
3e6b07e
bb1a61f
8979ab7
be60d51
0c570c8
c3d24b6
d1ca37d
cb0780b
eeffded
3cbe0cf
92b766b
0259cf7
8732594
fdb8b98
8b61ffd
0b3e16b
750a6d5
72b8dbc
8f069c7
440566c
72ca44b
90f7403
7a4382e
1c608f4
3b8b2e1
e0bc5e3
35a2e0b
655b3b4
91346a7
e8066df
9f51858
fb9c7c4
611f0bb
42beb33
0995050
24f38bd
15f4f49
638d961
8b831f8
5c677a5
bce790c
fefe820
e577503
746c54a
c5a1009
a66e6a3
351eee2
4a0a955
4e655da
c31af32
2507ea3
cf11ce0
91cb708
7040af7
e2eee99
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ | |
#include "llvm/Support/Error.h" | ||
#include "llvm/Support/ErrorHandling.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
#include <cmath> | ||
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#include <cstdint> | ||
#include <optional> | ||
#include <utility> | ||
|
@@ -55,6 +56,13 @@ static std::optional<uint32_t> extractMdIntValue(MDNode *Node, | |
return std::nullopt; | ||
} | ||
|
||
static std::optional<float> extractMdFloatValue(MDNode *Node, | ||
unsigned int OpId) { | ||
if (auto *CI = mdconst::dyn_extract<ConstantFP>(Node->getOperand(OpId).get())) | ||
return CI->getValueAPF().convertToFloat(); | ||
return std::nullopt; | ||
} | ||
|
||
static std::optional<StringRef> extractMdStringValue(MDNode *Node, | ||
unsigned int OpId) { | ||
MDString *NodeText = dyn_cast<MDString>(Node->getOperand(OpId)); | ||
|
@@ -261,6 +269,81 @@ static bool parseDescriptorTable(LLVMContext *Ctx, | |
return false; | ||
} | ||
|
||
static bool parseStaticSampler(LLVMContext *Ctx, mcdxbc::RootSignatureDesc &RSD, | ||
MDNode *StaticSamplerNode) { | ||
if (StaticSamplerNode->getNumOperands() != 14) | ||
return reportError(Ctx, "Invalid format for Static Sampler"); | ||
|
||
dxbc::RTS0::v1::StaticSampler Sampler; | ||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 1)) | ||
Sampler.Filter = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for Filter"); | ||
|
||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 2)) | ||
Sampler.AddressU = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for AddressU"); | ||
|
||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 3)) | ||
Sampler.AddressV = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for AddressV"); | ||
|
||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 4)) | ||
Sampler.AddressW = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for AddressW"); | ||
|
||
if (std::optional<float> Val = extractMdFloatValue(StaticSamplerNode, 5)) | ||
Sampler.MipLODBias = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for MipLODBias"); | ||
|
||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 6)) | ||
Sampler.MaxAnisotropy = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for MaxAnisotropy"); | ||
|
||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 7)) | ||
Sampler.ComparisonFunc = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for ComparisonFunc "); | ||
|
||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 8)) | ||
Sampler.BorderColor = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for ComparisonFunc "); | ||
|
||
if (std::optional<float> Val = extractMdFloatValue(StaticSamplerNode, 9)) | ||
Sampler.MinLOD = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for MinLOD"); | ||
|
||
if (std::optional<float> Val = extractMdFloatValue(StaticSamplerNode, 10)) | ||
Sampler.MaxLOD = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for MaxLOD"); | ||
|
||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 11)) | ||
Sampler.ShaderRegister = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for ShaderRegister"); | ||
|
||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 12)) | ||
Sampler.RegisterSpace = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for RegisterSpace"); | ||
|
||
if (std::optional<uint32_t> Val = extractMdIntValue(StaticSamplerNode, 13)) | ||
Sampler.ShaderVisibility = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for ShaderVisibility"); | ||
|
||
RSD.StaticSamplers.push_back(Sampler); | ||
return false; | ||
} | ||
|
||
static bool parseRootSignatureElement(LLVMContext *Ctx, | ||
mcdxbc::RootSignatureDesc &RSD, | ||
MDNode *Element) { | ||
|
@@ -276,6 +359,7 @@ static bool parseRootSignatureElement(LLVMContext *Ctx, | |
.Case("RootSRV", RootSignatureElementKind::SRV) | ||
.Case("RootUAV", RootSignatureElementKind::UAV) | ||
.Case("DescriptorTable", RootSignatureElementKind::DescriptorTable) | ||
.Case("StaticSampler", RootSignatureElementKind::StaticSamplers) | ||
.Default(RootSignatureElementKind::Error); | ||
|
||
switch (ElementKind) { | ||
|
@@ -290,6 +374,8 @@ static bool parseRootSignatureElement(LLVMContext *Ctx, | |
return parseRootDescriptors(Ctx, RSD, Element, ElementKind); | ||
case RootSignatureElementKind::DescriptorTable: | ||
return parseDescriptorTable(Ctx, RSD, Element); | ||
case RootSignatureElementKind::StaticSamplers: | ||
return parseStaticSampler(Ctx, RSD, Element); | ||
case RootSignatureElementKind::Error: | ||
return reportError(Ctx, "Invalid Root Signature Element: " + *ElementText); | ||
} | ||
|
@@ -406,6 +492,58 @@ static bool verifyDescriptorRangeFlag(uint32_t Version, uint32_t Type, | |
return (Flags & ~Mask) == FlagT::NONE; | ||
} | ||
|
||
static bool verifySamplerFilter(uint32_t Value) { | ||
switch (Value) { | ||
#define STATIC_SAMPLER_FILTER(Num, Val) \ | ||
case llvm::to_underlying(dxbc::StaticSamplerFilter::Val): | ||
#include "llvm/BinaryFormat/DXContainerConstants.def" | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
// Values allowed here: | ||
// https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_texture_address_mode#syntax | ||
static bool verifyAddress(uint32_t Address) { | ||
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
switch (Address) { | ||
#define TEXTURE_ADDRESS_MODE(Num, Val) \ | ||
case llvm::to_underlying(dxbc::TextureAddressMode::Val): | ||
#include "llvm/BinaryFormat/DXContainerConstants.def" | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
static bool verifyMipLODBias(float MipLODBias) { | ||
return MipLODBias >= -16.f && MipLODBias <= 15.99f; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this matches what DXC's validator does so it's probably "correct", but |
||
} | ||
|
||
static bool verifyMaxAnisotropy(uint32_t MaxAnisotropy) { | ||
return MaxAnisotropy <= 16u; | ||
} | ||
|
||
static bool verifyComparisonFunc(uint32_t ComparisonFunc) { | ||
switch (ComparisonFunc) { | ||
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#define COMPARISON_FUNCTION(Num, Val) \ | ||
case llvm::to_underlying(dxbc::SamplersComparisonFunction::Val): | ||
#include "llvm/BinaryFormat/DXContainerConstants.def" | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
static bool verifyBorderColor(uint32_t BorderColor) { | ||
switch (BorderColor) { | ||
#define STATIC_BORDER_COLOR(Num, Val) \ | ||
case llvm::to_underlying(dxbc::SamplersBorderColor::Val): | ||
#include "llvm/BinaryFormat/DXContainerConstants.def" | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
static bool verifyLOD(float LOD) { return !std::isnan(LOD); } | ||
|
||
static bool validate(LLVMContext *Ctx, const mcdxbc::RootSignatureDesc &RSD) { | ||
|
||
if (!verifyVersion(RSD.Version)) { | ||
|
@@ -463,6 +601,48 @@ static bool validate(LLVMContext *Ctx, const mcdxbc::RootSignatureDesc &RSD) { | |
} | ||
} | ||
|
||
for (const dxbc::RTS0::v1::StaticSampler &Sampler : RSD.StaticSamplers) { | ||
if (!verifySamplerFilter(Sampler.Filter)) | ||
return reportValueError(Ctx, "Filter", Sampler.Filter); | ||
|
||
if (!verifyAddress(Sampler.AddressU)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One thought that just came up. I don't think it is required for us to early exit on all these verifications. Maybe we can report all invalid errors in one go rather than exiting on the first one? Can you provide an argument for why or why not? I would assume reporting all errors (so long as they aren't too verbose) reduces the number of compile cycles for the user to correct their input. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've created a follow-up PR with this: #144465 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll take a look at the other PR and think about it there, but FWIW we don't expect to be getting here from a user's input per se - I would expect we only really get here from a root signature read from a binary format or from a frontend that's presumably already attempted to validate the input. |
||
return reportValueError(Ctx, "AddressU", Sampler.AddressU); | ||
|
||
if (!verifyAddress(Sampler.AddressV)) | ||
return reportValueError(Ctx, "AddressV", Sampler.AddressV); | ||
|
||
if (!verifyAddress(Sampler.AddressW)) | ||
return reportValueError(Ctx, "AddressW", Sampler.AddressW); | ||
|
||
if (!verifyMipLODBias(Sampler.MipLODBias)) | ||
return reportValueError(Ctx, "MipLODBias", Sampler.MipLODBias); | ||
|
||
if (!verifyMaxAnisotropy(Sampler.MaxAnisotropy)) | ||
return reportValueError(Ctx, "MaxAnisotropy", Sampler.MaxAnisotropy); | ||
|
||
if (!verifyComparisonFunc(Sampler.ComparisonFunc)) | ||
return reportValueError(Ctx, "ComparisonFunc", Sampler.ComparisonFunc); | ||
|
||
if (!verifyBorderColor(Sampler.BorderColor)) | ||
return reportValueError(Ctx, "BorderColor", Sampler.BorderColor); | ||
|
||
if (!verifyLOD(Sampler.MinLOD)) | ||
return reportValueError(Ctx, "MinLOD", Sampler.MinLOD); | ||
|
||
if (!verifyLOD(Sampler.MaxLOD)) | ||
return reportValueError(Ctx, "MaxLOD", Sampler.MaxLOD); | ||
|
||
if (!verifyRegisterValue(Sampler.ShaderRegister)) | ||
return reportValueError(Ctx, "ShaderRegister", Sampler.ShaderRegister); | ||
|
||
if (!verifyRegisterSpace(Sampler.RegisterSpace)) | ||
return reportValueError(Ctx, "RegisterSpace", Sampler.RegisterSpace); | ||
|
||
if (!dxbc::isValidShaderVisibility(Sampler.ShaderVisibility)) | ||
return reportValueError(Ctx, "ShaderVisibility", | ||
Sampler.ShaderVisibility); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
|
@@ -542,6 +722,9 @@ analyzeModule(Module &M) { | |
// offset will always equal to the header size. | ||
RSD.RootParameterOffset = sizeof(dxbc::RTS0::v1::RootSignatureHeader); | ||
|
||
// static sampler offset is calculated when writting dxcontainer. | ||
RSD.StaticSamplersOffset = 0u; | ||
|
||
if (parse(Ctx, RSD, RootElementListNode) || validate(Ctx, RSD)) { | ||
return RSDMap; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
; RUN: not opt -passes='print<dxil-root-signature>' %s -S -o - 2>&1 | FileCheck %s | ||
|
||
|
||
target triple = "dxil-unknown-shadermodel6.0-compute" | ||
|
||
; CHECK: error: Invalid value for AddressU: 666 | ||
; CHECK-NOT: Root Signature Definitions | ||
|
||
define void @main() #0 { | ||
entry: | ||
ret void | ||
} | ||
attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" } | ||
|
||
|
||
!dx.rootsignatures = !{!2} ; list of function/root signature pairs | ||
!2 = !{ ptr @main, !3, i32 2 } ; function, root signature | ||
!3 = !{ !5 } ; list of root signature elements | ||
!5 = !{ !"StaticSampler", i32 4, i32 666, i32 3, i32 5, float 0x3FF6CCCCC0000000, i32 9, i32 3, i32 2, float -1.280000e+02, float 1.280000e+02, i32 42, i32 0, i32 0 } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
; RUN: not opt -passes='print<dxil-root-signature>' %s -S -o - 2>&1 | FileCheck %s | ||
|
||
|
||
target triple = "dxil-unknown-shadermodel6.0-compute" | ||
|
||
; CHECK: error: Invalid value for AddressV: 666 | ||
; CHECK-NOT: Root Signature Definitions | ||
|
||
define void @main() #0 { | ||
entry: | ||
ret void | ||
} | ||
attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" } | ||
|
||
|
||
!dx.rootsignatures = !{!2} ; list of function/root signature pairs | ||
!2 = !{ ptr @main, !3, i32 2 } ; function, root signature | ||
!3 = !{ !5 } ; list of root signature elements | ||
!5 = !{ !"StaticSampler", i32 4, i32 2, i32 666, i32 5, float 0x3FF6CCCCC0000000, i32 9, i32 3, i32 2, float -1.280000e+02, float 1.280000e+02, i32 42, i32 0, i32 0 } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
; RUN: not opt -passes='print<dxil-root-signature>' %s -S -o - 2>&1 | FileCheck %s | ||
|
||
|
||
target triple = "dxil-unknown-shadermodel6.0-compute" | ||
|
||
; CHECK: error: Invalid value for AddressW: 666 | ||
; CHECK-NOT: Root Signature Definitions | ||
|
||
define void @main() #0 { | ||
entry: | ||
ret void | ||
} | ||
attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" } | ||
|
||
|
||
!dx.rootsignatures = !{!2} ; list of function/root signature pairs | ||
!2 = !{ ptr @main, !3, i32 2 } ; function, root signature | ||
!3 = !{ !5 } ; list of root signature elements | ||
!5 = !{ !"StaticSampler", i32 4, i32 2, i32 3, i32 666, float 0x3FF6CCCCC0000000, i32 9, i32 3, i32 2, float -1.280000e+02, float 1.280000e+02, i32 42, i32 0, i32 0 } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
; RUN: not opt -passes='print<dxil-root-signature>' %s -S -o - 2>&1 | FileCheck %s | ||
|
||
|
||
target triple = "dxil-unknown-shadermodel6.0-compute" | ||
|
||
; CHECK: error: Invalid value for BorderColor: 666 | ||
; CHECK-NOT: Root Signature Definitions | ||
|
||
define void @main() #0 { | ||
entry: | ||
ret void | ||
} | ||
attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" } | ||
|
||
|
||
!dx.rootsignatures = !{!2} ; list of function/root signature pairs | ||
!2 = !{ ptr @main, !3, i32 2 } ; function, root signature | ||
!3 = !{ !5 } ; list of root signature elements | ||
!5 = !{ !"StaticSampler", i32 4, i32 2, i32 3, i32 5, float 0x3FF6CCCCC0000000, i32 9, i32 3, i32 666, float -1.280000e+02, float 1.280000e+02, i32 42, i32 0, i32 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.
So when
NumSampler == 0
is required to be 0 right? Or is it just ignored? Or do we set it 0 to match DXC?Just want to double-check that is defined and correct.
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 set it to zero just to make sure we are not jumping into a random file location by mistake. I don't think DXC forces it to be zero. But if you have no ranges, the offset should be ignored.
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.
FWIW DXC writes the value where samplers would be unconditionally, so there might be some argument to match that just so that we're binary identical.
That said, you are correct that this offset should be ignored when there aren't any samplers, and I think that explicitly zeroing it makes that clearer, so I kind of like it as is.