Root Signature - Create
A root signature is like a function signature for shaders. It is used in two places. The first is when compiling shaders. It let the shader know what types of resources to expect and how they will be arranged in memory. The second part is when it is set on a commandlist so the driver know how to send the resources to the shader. For it to work the root signature needs to be the same in both places.
The root signature can be described in HLSL string and used when compiling the shaders. The string can then be compiled to with the rootsig_1_0 shader profile and loaded into the application. Another option is to create the root signature in the application by using D3D12_ROOT_SIGNATURE_DESC to set the settings and D3D12SerializeRootSignature() to serialize it. Then create the root signature with ID3D12Device::CreateRootSignature().
The root signature is created by adding root parameters to it. Each parameter added is assigned to a root signature slot and it start from 0 and go up. The slot is used later when assigning a value to the shader parameter from code. Each parameter is described by a D3D12_ROOT_PARAMETER struct. In it one specify the shader visibility, the shader register, the root parameter type and a few other values depending on the type.
This makes it possible to select what shader stage should get access to the root parameter. D3D12_SHADER_VISIBILITY_ALL send it to all shader stages.
To root parameters are assigned to virtual registers that are numbers.
Root Parameter Type
Here one select the type of root parameter and also what type of union with extra data one needs to fill in. There are three root parameter types to select from.
Root Constants : D3D12_ROOT_CONSTANTS
This is values that show up in the shader as one constant buffer. Here one set how many 32 bit values should be in the buffer and the register/space that should be used for it in HLSL.
Root Descriptor : D3D12_ROOT_DESCRIPTOR
Set the HLSL shader register and register space to use.
Descriptors tables : D3D12_ROOT_DESCRIPTOR_TABLE
The table is described by a number of D3D12_DESCRIPTOR_RANGE structures. Each range describe the type and number of descriptors. Each range has all the descriptors of the same type. The register/space is set for the first descriptor and the rest will get the registers in increasing order.
Root Signature - Limits
The max total size of all root root arguments is is 64 DWORDS. How much each root argument cost is listed in this table.
- Use as small a root argument as possible.
- Arrange parameters in order of highest to lowest frequency of change (first to last)
- Put CBVs in root constant or root descriptors if possible.
- Use flags to turn of visibility for stages not used.
- There might be a optimal slot size to stay below and possible also a maximum slot size.
- try to minimzie the number of unique root signatures
Pipeline state objects
Load and Commpile Shaders
The shaders can be loaded and compiled with D3DCompileFromFile() or use D3DCompile() to compile it only. DirectX 12 use HLSL as a shading language.
Create vertex input layout
To provide the GPU with vertex data one give it a vertex buffer. The vertex input layout tell the GPU what vertex attribute is where in the buffer so the GPU can extract them and give them to the shaders. To make a input layout fill in a array of D3D12_INPUT_ELEMENT_DESC.
SemanticName: The name of this attribute in the shader.
InputSlot: It is possible to read from more then one vertex buffer. Each vertex buffer is assigned to a slot (0-n) and this select what slot to read the attribute from.
AlignedByteOffset: How many bytes from the start of the vertex data this attribute starts. Use D3D12_APPEND_ALIGNED_ELEMENT to calculate this automaticly from the previous element.
Create Pipeline state objects
The PSO contains almost the entire GPU state so it all can be set in a single step. After a PSO is created it's immutable so if a paramter has to be different a new PSO must to be created. All values should be set explicity or verify that there is a sensible default value. A PSO is created by filling out a D3D12_GRAPHICS_PIPELINE_STATE_DESC and calling ID3D12Device::CreateGraphicsPipelineState().
Things that are in the Pipeline State Object
Here are the things that are set when creating a PSO and what they do.
Things Not in the Pipeline State Object
Not all things set in the PSO. These things are set on the CommandList when calling draw commands.
Only the number and format of render targets is set in the PSO. The specific render targets to use is set on the Commandlist.
A bundle is a command list created with the type D3D12_COMMAND_LIST_TYPE_BUNDLE. It can not be added directly to the command queue but it can be used from a direct command list with ID3D12GraphicsCommandList::ExecuteBundle(...). The bundle inherit all state from the parent command list except the pipeline state object and primitive topology. All the state the bundle set will affect the parent command list.