0

I am currently learning DirectX 12 and trying to get a demo application running. I am currently stuck at creating a pipeline state object using a root signature. I am using dxc to compile my vertex shader:

./dxc -T vs_6_3 -E main -Fo "basic.vert.dxi" -D DXIL "basic.vert"

My shader looks like this:

#pragma pack_matrix(row_major)

struct VertexData 
{
    float4 Position : SV_POSITION;
    float4 Color : COLOR;
}; 

struct VertexInput
{
    float3 Position : POSITION;
    float4 Color : COLOR;
};

struct CameraData
{
    float4x4 ViewProjection;
};

ConstantBuffer<CameraData> camera : register(b0, space0);

VertexData main(in VertexInput input)
{
    VertexData vertex;
    
    vertex.Position = mul(float4(input.Position, 1.0), camera.ViewProjection);
    vertex.Color = input.Color;
 
    return vertex;
}

Now I want to define a root signature for my shader. The definition looks something like this:

CD3DX12_DESCRIPTOR_RANGE1 descriptorRange;
descriptorRange.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND);

CD3DX12_ROOT_PARAMETER1 rootParameter;
rootParameter.InitAsDescriptorTable(1, &descriptorRange, D3D12_SHADER_VISIBILITY_VERTEX);

ComPtr<ID3DBlob> signature, error;
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init_1_1(1, &rootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
::D3D12SerializeVersionedRootSignature(&rootSignatureDesc, &signature, &error);

ComPtr<ID3D12RootSignature> rootSignature;
device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&rootSignature));

Finally, I pass the root signature along other state variables to the pipeline state object:

D3D12_GRAPHICS_PIPELINE_STATE_DESC pipelineStateDescription = {};
// ...
pipelineStateDescription.pRootSignature = rootSignature.Get();

ComPtr<ID3D12PipelineState> pipelineState;
device->CreateGraphicsPipelineState(&pipelineStateDescription, IID_PPV_ARGS(&pipelineState));

However, no matter what I do, the device keeps on complaining about the root signature not matching the vertex shader:

D3D12 ERROR: ID3D12Device::CreateGraphicsPipelineState: Root Signature doesn't match Vertex Shader: Shader CBV descriptor range (BaseShaderRegister=0, NumDescriptors=1, RegisterSpace=0) is not fully bound in root signature [ STATE_CREATION ERROR #688: CREATEGRAPHICSPIPELINESTATE_VS_ROOT_SIGNATURE_MISMATCH]

D3D12: BREAK enabled for the previous message, which was: [ ERROR STATE_CREATION #688: CREATEGRAPHICSPIPELINESTATE_VS_ROOT_SIGNATURE_MISMATCH ]

I am confused about what this error is trying to tell me, since I clearly have a constant buffer bound to register(b0, space0). Or does it mean that I have to allocate a descriptor from a heap before creating the pipeline state object?

I also tried defining a root signature within the shader itself:

#define ShaderRootSignature \
    "RootFlags( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT ), " \
              "DescriptorTable( CBV(b0, space = 0, numDescriptors = 1, flags = DATA_STATIC ) ), "

... and compiling it using [RootSignature(ShaderRootSignature)], or specifying -rootsig-define "ShaderRootSignature" for dxc. Then I tried loading the signature as suggested here, however both approaches fail, since the root signature could not be read from the shader bytecode.

Any clarification on how to interpret the error message would be much appreciated, since I really do not know what the binding in a root signature means in this context. Thanks in advance!

Carsten
  • 11,287
  • 7
  • 39
  • 62
  • The message says that your root signature doesn't contain CBV at register(b0,space0), nothing with heap descriptors or something like that. However, I don't see any mistakes in your root signature creation. Can you add more parts of your code somewhere like pastebin? You should also check return values of device->Create* functions. – mateeeeeee Jun 02 '21 at 21:05
  • @zezanjee Thanks for the comment. At least I know my shader is correct. I will try to prepare a MRE. I do actually check each HRESULT, and every call and everything is fine, until I create the pipeline. I suppose it will return `E_INVALIDARG`, however the debug layer kicks in before. Do I have to load the shader into a blob? Because I am currently storing a simple byte array, containing the whole shader file. – Carsten Jun 03 '21 at 05:50
  • @zezanjee: I was partially able to fix this issue, by defining the root signature inside the shader and using the attribute `[RootSignature(ShaderRootSignature)]` together with the `-extractrootsignature` switch. I then load the root signature from the shader byte code and use it to initialize the PSO. If I use the manually-created root signature, however, I receive the same error. I tried to deserialize the root signature from the bytecode to compare both signatures and it seems like `CD3DX12_DESCRIPTOR_RANGE1` replaces the `D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND` with `8` for some reason. – Carsten Jun 03 '21 at 09:52
  • Offset is important when you are setting your root parameters at runtime so I don't believe it's connected to your original problem. MRE would be nice. Regarding offset, you are the one controlling the offset even when writing HLSL root signature. The default value is D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, so if you didn't explicitly set your offset to 8 in shader, I don't know what could be the cause for that. – mateeeeeee Jun 03 '21 at 11:46
  • @zezanjee Thanks for your help. I figured out the problem, finally. Coming from Vulkan, I thought it is possible to set the shader visibility in form of a bit mask, however, it turns out that you can only set it to a single shader or `D3D12_SHADER_VISIBILITY_ALL` in DX12. I've specified `D3D12_SHADER_VISIBILITY_VERTEX | D3D12_SHADER_VISIBILITY_PIXEL`, which resulted in `D3D12_SHADER_VISIBILITY_PIXEL` and caused the CBV not to be visible to the vertex shader. ‍♂️ Thanks for the explanation though, it pointed me away from looking into the shader code for possible errors. – Carsten Jun 04 '21 at 07:34
  • 1
    I remember making the same mistake about bitwising those flags :) Too bad you removed a bit too much from your example but I am glad you finally got it working. – mateeeeeee Jun 04 '21 at 12:03

1 Answers1

0

Long story short: shader visibility in DX12 is not a bit field, like in Vulkan, so setting the visibility to D3D12_SHADER_VISIBILITY_VERTEX | D3D12_SHADER_VISIBILITY_PIXEL results in the parameter only being visible to the pixel shader. Setting it to D3D12_SHADER_VISIBILITY_ALL solved my problem.

Carsten
  • 11,287
  • 7
  • 39
  • 62