Skip to content
24 changes: 24 additions & 0 deletions llvm/include/llvm/BinaryFormat/DXContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ enum class DescriptorRangeFlags : uint32_t {

LLVM_ABI ArrayRef<EnumEntry<DescriptorRangeFlags>> getDescriptorRangeFlags();

#define STATIC_SAMPLER_FLAG(Num, Enum, Flag) Enum = Num,
enum class StaticSamplerFlags : uint32_t {
#include "DXContainerConstants.def"

LLVM_MARK_AS_BITMASK_ENUM(NonNormalizedCoordinates)
};

LLVM_ABI ArrayRef<EnumEntry<StaticSamplerFlags>> getStaticSamplerFlags();

#define ROOT_PARAMETER(Val, Enum) Enum = Val,
enum class RootParameterType : uint32_t {
#include "DXContainerConstants.def"
Expand Down Expand Up @@ -813,6 +822,21 @@ struct DescriptorRange {
}
};
} // namespace v2
namespace v3 {
struct StaticSampler : public v1::StaticSampler {
uint32_t Flags;

StaticSampler() = default;
explicit StaticSampler(v1::StaticSampler &Base)
: v1::StaticSampler(Base), Flags(0U) {}

void swapBytes() {
v1::StaticSampler::swapBytes();
sys::swapByteOrder(Flags);
}
};

} // namespace v3
} // namespace RTS0

// D3D_ROOT_SIGNATURE_VERSION
Expand Down
10 changes: 10 additions & 0 deletions llvm/include/llvm/BinaryFormat/DXContainerConstants.def
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ DESCRIPTOR_RANGE_FLAG(0x10000, DescriptorsStaticKeepingBufferBoundsChecks, DESCR
#undef DESCRIPTOR_RANGE_FLAG
#endif // DESCRIPTOR_RANGE_FLAG

// STATIC_SAMPLER_FLAG(flag value, name, flag).
#ifdef STATIC_SAMPLER_FLAG

STATIC_SAMPLER_FLAG(0x0, None, SAMPLER_FLAG_NONE)
STATIC_SAMPLER_FLAG(0x1, UintBorderColor, SAMPLER_FLAG_UINT_BORDER_COLOR)
STATIC_SAMPLER_FLAG(0x2, NonNormalizedCoordinates, SAMPLER_FLAG_NON_NORMALIZED_COORDINATES)

#undef STATIC_SAMPLER_FLAG
#endif // STATIC_SAMPLER_FLAG

#ifdef ROOT_PARAMETER

ROOT_PARAMETER(0, DescriptorTable)
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/MC/DXContainerRootSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ struct StaticSampler {
uint32_t ShaderRegister;
uint32_t RegisterSpace;
dxbc::ShaderVisibility ShaderVisibility;
uint32_t Flags;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit:

Suggested change
uint32_t Flags;
// Version 3 onwards:
uint32_t Flags;

};

struct RootParametersContainer {
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/ObjectYAML/DXContainerYAML.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ struct StaticSamplerYamlDesc {
uint32_t ShaderRegister;
uint32_t RegisterSpace;
dxbc::ShaderVisibility ShaderVisibility;

LLVM_ABI uint32_t getEncodedFlags() const;

#define STATIC_SAMPLER_FLAG(Num, Enum, Flag) bool Enum = false;
#include "llvm/BinaryFormat/DXContainerConstants.def"
};

struct RootSignatureYamlDesc {
Expand Down
9 changes: 9 additions & 0 deletions llvm/lib/BinaryFormat/DXContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ ArrayRef<EnumEntry<DescriptorRangeFlags>> dxbc::getDescriptorRangeFlags() {
return ArrayRef(DescriptorRangeFlagNames);
}

static const EnumEntry<StaticSamplerFlags> StaticSamplerFlagNames[] = {
#define STATIC_SAMPLER_FLAG(Val, Enum, Flag) {#Enum, StaticSamplerFlags::Enum},
#include "llvm/BinaryFormat/DXContainerConstants.def"
};

ArrayRef<EnumEntry<StaticSamplerFlags>> dxbc::getStaticSamplerFlags() {
return ArrayRef(StaticSamplerFlagNames);
}

#define SHADER_VISIBILITY(Val, Enum) {#Enum, ShaderVisibility::Enum},

static const EnumEntry<ShaderVisibility> ShaderVisibilityValues[] = {
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/Frontend/HLSL/RootSignatureValidations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ namespace rootsig {

bool verifyRootFlag(uint32_t Flags) { return (Flags & ~0xfff) == 0; }

bool verifyVersion(uint32_t Version) { return (Version == 1 || Version == 2); }
bool verifyVersion(uint32_t Version) {
return (Version == 1 || Version == 2 || Version == 3);
}

bool verifyRegisterValue(uint32_t RegisterValue) {
return RegisterValue != ~0U;
Expand Down
7 changes: 6 additions & 1 deletion llvm/lib/MC/DXContainerRootSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ static uint32_t rewriteOffsetToCurrentByte(raw_svector_ostream &Stream,
size_t RootSignatureDesc::getSize() const {
uint32_t StaticSamplersOffset = computeStaticSamplersOffset();
size_t StaticSamplersSize =
StaticSamplers.size() * sizeof(dxbc::RTS0::v1::StaticSampler);
(Version > 2 ? sizeof(dxbc::RTS0::v3::StaticSampler)
: sizeof(dxbc::RTS0::v1::StaticSampler)) *
Copy link
Contributor

@inbelic inbelic Sep 23, 2025

Choose a reason for hiding this comment

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

very nitpicky: it would follow the pattern elsewhere in this size computation to use an if-else chain, which would technically be easier to extend for any future version.

more nitpicky things: Maybe getting the stride and then multiplying would be good to match the implementation

StaticSamplers.size();

return size_t(StaticSamplersOffset) + StaticSamplersSize;
}
Expand Down Expand Up @@ -171,6 +173,9 @@ void RootSignatureDesc::write(raw_ostream &OS) const {
support::endian::write(BOS, S.ShaderRegister, llvm::endianness::little);
support::endian::write(BOS, S.RegisterSpace, llvm::endianness::little);
support::endian::write(BOS, S.ShaderVisibility, llvm::endianness::little);

if (Version > 2)
support::endian::write(BOS, S.Flags, llvm::endianness::little);
}
assert(Storage.size() == getSize());
OS.write(Storage.data(), Storage.size());
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/ObjectYAML/DXContainerEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ Error DXContainerWriter::writeParts(raw_ostream &OS) {
NewSampler.RegisterSpace = Param.RegisterSpace;
NewSampler.ShaderVisibility = Param.ShaderVisibility;

if (RS.Version > 2)
NewSampler.Flags = Param.getEncodedFlags();

RS.StaticSamplers.push_back(NewSampler);
}

Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/ObjectYAML/DXContainerYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,15 @@ uint32_t DXContainerYAML::DescriptorRangeYaml::getEncodedFlags() const {
return Flags;
}

uint32_t DXContainerYAML::StaticSamplerYamlDesc::getEncodedFlags() const {
uint64_t Flags = 0;
#define STATIC_SAMPLER_FLAG(Num, Enum, Flag) \
if (Enum) \
Flags |= (uint32_t)dxbc::StaticSamplerFlags::Enum;
#include "llvm/BinaryFormat/DXContainerConstants.def"
return Flags;
}

uint64_t DXContainerYAML::ShaderFeatureFlags::getEncodedFlags() {
uint64_t Flag = 0;
#define SHADER_FEATURE_FLAG(Num, DxilModuleNum, Val, Str) \
Expand Down Expand Up @@ -512,6 +521,9 @@ void MappingTraits<llvm::DXContainerYAML::StaticSamplerYamlDesc>::mapping(
IO.mapRequired("ShaderRegister", S.ShaderRegister);
IO.mapRequired("RegisterSpace", S.RegisterSpace);
IO.mapRequired("ShaderVisibility", S.ShaderVisibility);
#define STATIC_SAMPLER_FLAG(Num, Enum, Flag) \
IO.mapOptional(#Flag, S.Enum, false);
#include "llvm/BinaryFormat/DXContainerConstants.def"
}

void MappingTraits<DXContainerYAML::Part>::mapping(IO &IO,
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we also add an equivalent test for invalid sampler flag values?

edit: I guess we haven't added that validation in this pr yet

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
; 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 Version: 4
; 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, !3, !4, !5} ; list of function/root signature pairs
!2 = !{ ptr @main, !6, i32 1 } ; function, root signature
!3 = !{ ptr @main, !6, i32 4 } ; function, root signature
!4 = !{ ptr @main, !6, i32 2 } ; function, root signature
!5 = !{ ptr @main, !6, i32 3 } ; function, root signature
!6 = !{ } ; list of root signature elements
61 changes: 61 additions & 0 deletions llvm/unittests/ObjectYAML/DXContainerYAMLTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,3 +526,64 @@ TEST(RootSignature, ParseStaticSamplers) {
EXPECT_EQ(Storage.size(), 144u);
EXPECT_TRUE(memcmp(Buffer, Storage.data(), 144u) == 0);
}

TEST(RootSignature, ParseStaticSamplersV13) {
SmallString<160> Storage;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why 160 here? This is a specific enough number that it looks intentional, but I can't see it actually being large enough for anything interesting. If we're just using an arbitrary number because the data will never fit in the small size anyway we might as well match the sizes used in the rest of the file.


// First read a fully explicit yaml with all sizes and offsets provided
ASSERT_TRUE(convert(Storage, R"(--- !dxcontainer
Header:
Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
Version:
Major: 1
Minor: 0
PartCount: 1
PartOffsets: [ 60 ]
Parts:
- Name: RTS0
Size: 76
RootSignature:
Version: 3
NumRootParameters: 0
RootParametersOffset: 24
NumStaticSamplers: 1
StaticSamplersOffset: 24
Parameters: []
Samplers:
- Filter: MinLinearMagMipPoint
AddressU: Wrap
AddressV: Mirror
AddressW: MirrorOnce
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: A lot of these members are optional. It might be clearer what is being tested if they are removed

MipLODBias: 1.23
MaxAnisotropy: 20
ComparisonFunc: LessEqual
BorderColor: TransparentBlack
MinLOD: 4.56
MaxLOD: 8.90
ShaderRegister: 31
RegisterSpace: 32
ShaderVisibility: Mesh
SAMPLER_FLAG_UINT_BORDER_COLOR: true
AllowInputAssemblerInputLayout: true
DenyGeometryShaderRootAccess: true
)"));

uint8_t Buffer[] = {
0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x52, 0x54, 0x53, 0x30, 0x4c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0xa4, 0x70, 0x9d, 0x3f, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x85, 0xeb, 0x91, 0x40, 0x66, 0x66, 0x0e, 0x41,
0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00};

EXPECT_EQ(Storage.size(), 148U);
EXPECT_TRUE(memcmp(Buffer, Storage.data(), 148U) == 0);
}