Skip to content

[DirectX] Documenting Root Signature Binary representation #131011

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

Merged
merged 28 commits into from
Mar 20, 2025
Merged
Changes from 14 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e26ef18
Adding root constant documentation
joaosaffran Mar 3, 2025
4f3930a
Removing union, fix typos
joaosaffran Mar 3, 2025
8fae269
Wrapping text
joaosaffran Mar 3, 2025
7ad5d2b
Removing redundant byte offset reference
joaosaffran Mar 3, 2025
93116c0
Try fix test
joaosaffran Mar 3, 2025
e1d385a
Fix git error
joaosaffran Mar 3, 2025
b390cd2
Adding root descriptor subsection
joaosaffran Mar 4, 2025
46face1
Fix git error
joaosaffran Mar 4, 2025
6a260b3
Try fix test
joaosaffran Mar 4, 2025
82a7de3
Updating Root Descriptor documentation
joaosaffran Mar 4, 2025
b591fd8
Linking Direct X docs to details flags
joaosaffran Mar 4, 2025
583e29c
Detail RootDescriptorFlags enum
joaosaffran Mar 5, 2025
16e3642
Update DXContainer.rst
joaosaffran Mar 6, 2025
3da10bd
Addressing comments
joaosaffran Mar 11, 2025
73c645d
Addressing comments
joaosaffran Mar 11, 2025
fcabc0e
Address PR Comments
joaosaffran Mar 11, 2025
b13609d
Adding Static Sampler Documentation
joaosaffran Mar 12, 2025
4525033
Update DXContainer.rst
joaosaffran Mar 12, 2025
15babc8
Merge branch 'documentation/root-descriptors' into documentation/stat…
Mar 13, 2025
5303de8
make a single PR
Mar 13, 2025
8dc983a
Update DXContainer.rst
joaosaffran Mar 13, 2025
b013080
Update DXContainer.rst
joaosaffran Mar 14, 2025
0bcfa6b
Addressing comments
joaosaffran Mar 17, 2025
787c920
Addressing comments
joaosaffran Mar 19, 2025
351e6bb
Addressing some comments
joaosaffran Mar 19, 2025
32fd3de
Address PR Comments
joaosaffran Mar 19, 2025
7dcad52
Update DXContainer.rst
joaosaffran Mar 19, 2025
a9260e5
Update DXContainer.rst
joaosaffran Mar 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 258 additions & 1 deletion llvm/docs/DirectX/DXContainer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ FXC are marked with \*.
#. `PSV0`_ - Stores Pipeline State Validation data.
#. RDAT† - Stores Runtime Data.
#. RDEF\* - Stores resource definitions.
#. RTS0 - Stores compiled root signature.
#. `RTS0`_ - Stores compiled root signature.
#. `SFI0`_ - Stores shader feature flags.
Comment on lines +114 to 115
Copy link
Contributor

Choose a reason for hiding this comment

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

It's odd that the order here doesn't match the order of the sections later. Probably makes sense to insert the new content right before the SFI0 section rather than right after.

#. SHDR\* - Stores compiled DXBC bytecode.
#. SHEX\* - Stores compiled DXBC bytecode.
Expand Down Expand Up @@ -400,3 +400,260 @@ SFI0 Part
The SFI0 part encodes a 64-bit unsigned integer bitmask of the feature flags.
This denotes which optional features the shader requires. The flag values are
defined in `llvm/include/llvm/BinaryFormat/DXContainerConstants.def <https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/BinaryFormat/DXContainerConstants.def>`_.


Root Signature (RTS0) Part
--------------------------
.. _RTS0:

The Root Signature defines the interface between the shader and the pipeline,
specifying which resources are bound to the shader and how they are accessed.
This structure serves as a contract between the application and the GPU,
establishing a layout for resource binding that both the shader compiler and
the runtime can understand.
Copy link
Contributor

Choose a reason for hiding this comment

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

These two sentences feel very redundant with one another, and neither really has a lot of content. The first says this defines an interface and the second literally defines what an interface is. Something like "The root signature part describes how resources are bound to the shader pipeline" is both more direct and clearer.


The Root Signature consists of a header followed by a collection of root parameters
and static samplers. The structure uses a versioned design with offset-based references
to allow for flexible serialization and deserialization.

Root Signature Header
~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: c

struct RootSignatureHeader {
uint32_t Version;
uint32_t NumParameters;
uint32_t ParametersOffset;
uint32_t NumStaticSamplers;
uint32_t StaticSamplerOffset;
uint32_t Flags;
}


The `RootSignatureHeader` structure contains the top-level information about a root signature:

#. **Version**: Specifies the version of the root signature format. This allows for backward
compatibility as the format evolves.
#. **NumParameters**: The number of root parameters contained in this root signature.
#. **ParametersOffset**: Byte offset from the beginning of RST0 section to the array of root
parameters header.
#. **NumStaticSamplers**: The number of static samplers defined in the root signature.
#. **StaticSamplerOffset**: Byte offset to the array of static samplers.
#. **Flags**: Bit flags that define global behaviors for the root signature, such as whether
to deny vertex shader access to certain resources.
Copy link
Contributor

Choose a reason for hiding this comment

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

It's probably fine, but I wonder if showing an example C structure followed by the list of fields is overly verbose here. What do you think of simply describing the header in a sentence and then illustrating with a C struct, like

The root signature header is 24 bytes long, consisting of six 32 bit values representing the version, number and offset of parameters, number and offset of static samplers, and a flags field for global behaviours:

.. code-block:: c
...


This header allows readers to navigate the binary representation of the root signature by
providing counts and offsets to locate each component within the serialized data.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is just defining a header - I don't think it's necessary


Root Parameter Header
~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: c

struct RootParameterHeader {
dxbc::RootParameterType ParameterType;
dxbc::ShaderVisibility ShaderVisibility;
Copy link
Contributor

Choose a reason for hiding this comment

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

We're describing a binary format, not C. An enum in C does not necessarily map to 32 bits, so we should really be explicit about the size of the data here. This is a 32-bit value describing the parameter type (from a binary format point of view), not an enum of type RootParameterType.

Also, as above, I think having a description of what the values in a root paramter header are before we bother showing the example C struct would make this easier to follow.

uint32_t ParameterOffset;
};


Each root parameter in the signature is preceded by a `RootParameterHeader` that describes
the parameter's basic attributes:

#. **ParameterType**: Enumeration indicating what type of parameter this is (e.g., descriptor
table, constants, CBV, SRV, UAV).
#. **ShaderVisibility**: Specifies which shader stages can access this parameter (e.g., all stages,
vertex shader only, pixel shader only).
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be helpful to explain the full set of parameter types and possible visibilities in the top level description of root parameters. Then this can simply refer back to that and say we describe the parameter type and visibility and we don't need the "e.g." for each of these.

#. **ParameterOffset**: Byte offset to the specific parameter data structure
for this entry.

The header uses a parameter type field rather than encoding the version of the parameter through
size, allowing for a more explicit representation of the parameter's nature.
Copy link
Contributor

Choose a reason for hiding this comment

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

I have no idea what this sentence means.


Root Parameters
~~~~~~~~~~~~~~~

The Root Parameters section contains structured definitions for each type of root parameter that can
be included in a root signature. Each structure corresponds to a specific parameter type as identified
by the ``ParameterType`` field in the ``RootParameterHeader``.

Root Constants
~~~~~~~~~~~~~~

.. code-block:: cpp

struct RootConstants {
uint32_t ShaderRegister;
uint32_t RegisterSpace;
uint32_t Num32BitValues;
};

The ``RootConstants`` structure represents inline root constants that are directly embedded in the root
signature and passed to the shader without requiring a constant buffer resource:

#. **ShaderRegister**: The shader register (b#) where these constants are bound.
#. **RegisterSpace**: The register space used for the binding.
#. **Num32BitValues**: The number of 32-bit values included in this constant buffer.

Root constants provide a fast way to pass small amounts of data directly to the shader without the overhead of creating and binding a constant buffer resource.

Root Descriptor
~~~~~~~~~~~~~~~

Root descriptors provide a direct mechanism for binding individual resources to shader stages in the Direct3D 12
rendering pipeline. They represent a critical interface for efficient resource management, allowing applications
to specify how shader stages access specific GPU resources.

.. code-block:: cpp

// Version 1.0 Root Descriptor
struct RootDescriptor_V1_0 {
uint32_t ShaderRegister;
uint32_t RegisterSpace;
};

// Version 1.1 Root Descriptor
struct RootDescriptor_V1_1 {
uint32_t ShaderRegister;
uint32_t RegisterSpace;
// New flags for Version 1.1
enum Flags {
None = 0x0,
DATA_STATIC = 0x1,
DATA_STATIC_WHILE_SET_AT_EXECUTE = 0x2,
DATA_VOLATILE = 0x4
};

// Bitfield of flags from the Flags enum
uint32_t Flags;
};

The Root Descriptor structure has evolved to support two versions, providing enhanced flexibility and
performance optimization capabilities.

Version 1.0 Root Descriptor
'''''''''''''''''''''''''''
The Version 1.0 RootDescriptor_V1_0 provides basic resource binding:

#. **ShaderRegister**: The shader register where the descriptor is bound.
#. **RegisterSpace**: The register space used for the binding.

Version 1.1 Root Descriptor
'''''''''''''''''''''''''''
The Version 1.1 RootDescriptor_V1_1 extends the base structure with the following additional fields:

#. **Flags**: Provides additional metadata about the descriptor's usage pattern.

Root Descriptor Table
~~~~~~~~~~~~~~~~~~~~~

Descriptor tables function as containers that hold references to descriptors in descriptor heaps.
They allow multiple descriptors to be bound to the pipeline through a single root signature parameter.

.. code-block:: cpp

struct DescriptorRange_V1_0 {
dxbc::DescriptorRangeType RangeType;
uint32_t NumDescriptors;
uint32_t BaseShaderRegister;
uint32_t RegisterSpace;
uint32_t OffsetInDescriptorsFromTableStart;
};

struct DescriptorRange_V1_1 {
dxbc::DescriptorRangeType RangeType;
uint32_t NumDescriptors;
uint32_t BaseShaderRegister;
uint32_t RegisterSpace;
uint32_t OffsetInDescriptorsFromTableStart;
// New flags for Version 1.1
enum Flags {
None = 0x0,
// Descriptors are static and known at root signature creation
DESCRIPTORS_STATIC = 0x1,
// Descriptors remain constant during command list execution
DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS = 0x2,
// Descriptors may change frequently
DESCRIPTORS_VOLATILE = 0x4
};

// Bitfield of flags from the Flags enum
uint32_t Flags;
};

struct RootDescriptorTable {
uint32_t NumDescriptorRanges;
uint32_t DescriptorRangesOffset;
};


Descriptor Range Version 1.0
''''''''''''''''''''''''''''
The Version 1.0 ``DescriptorRange_V1_0`` provides basic descriptor range definition:

#. **RangeType**: Type of descriptors (CBV, SRV, UAV, or Sampler)
#. **NumDescriptors**: Number of descriptors in the range
#. **BaseShaderRegister**: First shader register in the range
#. **RegisterSpace**: Register space for the range
#. **OffsetInDescriptorsFromTableStart**: Offset from the descriptor heap start

Descriptor Range Version 1.1
''''''''''''''''''''''''''''
The Version 1.1 DescriptorRange_V1_1 extends the base structure with performance optimization flags.

#. **Flags**: Provide additional information about the descriptors and enable further driver optimizations.
For details, check `Direct X documentation <https://learn.microsoft.com/en-us/windows/win32/direct3d12/root-signature-version-1-1#static-and-volatile-flags>`_.

Root Descriptor Table
'''''''''''''''''''''
RootDescriptorTable provides basic table structure:

#. **NumDescriptorRanges**: Number of descriptor ranges
#. **DescriptorRangesOffset**: Offset to descriptor range array

Static Samplers
~~~~~~~~~~~~~~~

Static samplers provide a way to define fixed sampler states within the root signature itself.

.. code-block:: cpp

struct StaticSamplerDesc {
FilterMode Filter;
TextureAddressMode AddressU;
TextureAddressMode AddressV;
TextureAddressMode AddressW;
float MipLODBias;
uint32_t MaxAnisotropy;
ComparisonFunc ComparisonFunc;
StaticBorderColor BorderColor;
float MinLOD;
float MaxLOD;
uint32_t ShaderRegister;
uint32_t RegisterSpace;
ShaderVisibility ShaderVisibility;
};


Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

The StaticSamplerDesc structure defines all properties of a static sampler:
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it would make sense to remove all of these lines and instead just link to the top of the https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_static_sampler_desc page?

Although, that link could break one day.

Copy link
Contributor Author

@joaosaffran joaosaffran Mar 13, 2025

Choose a reason for hiding this comment

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

The intention here is to document the binary representation of root signatures inside dxcontainer, one of such parts is static samplers. Although, DX12 representation and the compiler representation are the same, that might not be the case in the future. For example, if we decide to change this representation or if DX12 decide to change theirs representation. Therefore, I think this duplication might be okay IMHO.

Copy link
Contributor

Choose a reason for hiding this comment

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

Does that mean that this struct will be defined in .../BinaryFormat/DXContainer.h ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. DXC has similar structs as well

Copy link
Contributor

Choose a reason for hiding this comment

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

This references a lot of types that aren't defined in this document. As a description of a binary format it really needs to discuss the sizes and layout of things more so than what the things are for.


#. Filter: The filtering mode (e.g., point, linear, anisotropic) used for texture sampling.
For details, check `Static Sampler Fileters definition. <https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_filter#syntax>`_.
#. AddressU: The addressing mode for the U texture coordinate.
For details, check `Texture address mode definition. <https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_texture_address_mode>`_.
#. AddressV: The addressing mode for the V texture coordinate.
#. AddressW: The addressing mode for the W texture coordinate.
#. MipLODBias: Bias value applied to mipmap level of detail calculations.
#. MaxAnisotropy: Maximum anisotropy level when using anisotropic filtering.
#. ComparisonFunc: Comparison function used for comparison samplers.
For details, check `Comparison Function definition. <https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_comparison_func>`_.
#. BorderColor: Predefined border color used when address mode is set to border.
For details, check `Static border color <https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_static_border_color>`_.
#. MinLOD: Minimum level of detail to use for sampling.
#. MaxLOD: Maximum level of detail to use for sampling.
#. ShaderRegister: The shader sampler register (s#) where this sampler is bound.
#. RegisterSpace: The register space used for the binding.
#. ShaderVisibility: Specifies which shader stages can access this sampler.
For details, check `Sahder Visibility definition. <https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_shader_visibility>`_.