[D3D12] Using SetGraphicsRoot*View functions

Started by
15 comments, last by Hodgman 7 years, 11 months ago

Hello!

I am feeling confused by MSDN documentation on the argument SetGraphicsRootShaderResourceView should accept

https://msdn.microsoft.com/en-us/library/windows/desktop/dn903913(v=vs.85).aspx.


void SetGraphicsRootShaderResourceView(
  [in] UINT                      RootParameterIndex,
  [in] D3D12_GPU_VIRTUAL_ADDRESS BufferLocation
);

According to MSDN BufferLocation is "The GPU virtual address of the constant buffer. D3D12_GPU_VIRTUAL_ADDRESS is a typedef'd alias of UINT64. "

Sounds like a copy-paste for SetGraphicsRootConstantBufferView description

https://msdn.microsoft.com/en-us/library/windows/desktop/dn903911(v=vs.85).aspx

After I had checked D3D12 source header file, I got even more puzzled.


virtual void STDMETHODCALLTYPE SetGraphicsRootConstantBufferView(
  _In_  UINT RootParameterIndex,
  _In_  D3D12_GPU_VIRTUAL_ADDRESS BufferLocation) = 0;

virtual void STDMETHODCALLTYPE SetGraphicsRootShaderResourceView( 
  _In_  UINT RootParameterIndex,
  _In_  D3D12_GPU_VIRTUAL_ADDRESS BufferLocation) = 0;

virtual void STDMETHODCALLTYPE SetGraphicsRootUnorderedAccessView( 
  _In_  UINT RootParameterIndex,
  _In_  D3D12_GPU_VIRTUAL_ADDRESS BufferLocation) = 0;

All of the three functions take D3D12_GPU_VIRTUAL_ADDRESS as an argument.

Does it really mean that D3D12 can bind CBV/SRV/UAV for the resource based only on its D3D12_GPU_VIRTUAL_ADDRESS without specifying corresponding CBV/SRV/UAV descriptor handle?

Advertisement

Hello!

I am feeling confused by MSDN documentation on the argument SetGraphicsRootShaderResourceView should accept

https://msdn.microsoft.com/en-us/library/windows/desktop/dn903913(v=vs.85).aspx.


void SetGraphicsRootShaderResourceView(
  [in] UINT                      RootParameterIndex,
  [in] D3D12_GPU_VIRTUAL_ADDRESS BufferLocation
);

According to MSDN BufferLocation is "The GPU virtual address of the constant buffer. D3D12_GPU_VIRTUAL_ADDRESS is a typedef'd alias of UINT64. "

Sounds like a copy-paste for SetGraphicsRootConstantBufferView description

https://msdn.microsoft.com/en-us/library/windows/desktop/dn903911(v=vs.85).aspx

After I had checked D3D12 source header file, I got even more puzzled.


virtual void STDMETHODCALLTYPE SetGraphicsRootConstantBufferView(
  _In_  UINT RootParameterIndex,
  _In_  D3D12_GPU_VIRTUAL_ADDRESS BufferLocation) = 0;

virtual void STDMETHODCALLTYPE SetGraphicsRootShaderResourceView( 
  _In_  UINT RootParameterIndex,
  _In_  D3D12_GPU_VIRTUAL_ADDRESS BufferLocation) = 0;

virtual void STDMETHODCALLTYPE SetGraphicsRootUnorderedAccessView( 
  _In_  UINT RootParameterIndex,
  _In_  D3D12_GPU_VIRTUAL_ADDRESS BufferLocation) = 0;

All of the three functions take D3D12_GPU_VIRTUAL_ADDRESS as an argument.

Does it really mean that D3D12 can bind CBV/SRV/UAV for the resource based only on its D3D12_GPU_VIRTUAL_ADDRESS without specifying corresponding CBV/SRV/UAV descriptor handle?

The corresponding CBV/SRV/UAV descriptor handler is represented through the GPU_VITIRAL_ADDRESS in a descriptor heap already. Additionally you just need to specify the corresponding first parameter which is the root parameter index you've defined in root signature.

The structure looks complex and not very easy to organize manually, as a result I've already encapsulated them into classes, otherwise I'd get mad dealing with each detail.

Yu Lie, thank you for the input

Still, this leaves me with the questions. I would assume that CBV/SRV/UAV has to be created explicitly for the resource using

- CreateConstantBufferView

- CreateShaderResourceView

- CreateUnorderedAccessView

Does D3D12_GPU_VIRTUAL_ADDRESS correspond to D3D12_CPU_DESCRIPTOR_HANDLE from the descriptor heap?

If so, should D3D12_CPU_DESCRIPTOR_HANDLE allow read access to CPU then?

Or is it D3D12_GPU_DESCRIPTOR_HANDLE it has to reference?

Thanks!

IIRC, these functions actually skip the need to create a view at all... which makes them slightly faster, and much more dangerous.
Normally a CBV has two members: BufferLocation and SizeInBytes.
When you use SetGraphicsRootConstantBufferView, it's equivalent to just setting the BufferLocation and leaving SizeInBytes as a magic value of "unknown".

This means that if you provide an address of a GPU allocation that's only 4 bytes large, but your shader is expecting to be able to read a 256 byte structure, then very bad things will happen -- you'll most likely crash the GPU hardware, etc...

Regarding SRV's, AFAIK this isn't usable for texture's - only for buffers. This is because textures really do require knowledge of the members in the SRV, such as width/height/format.
It's possible for a shader to read from a StructuredBuffer without knowing the full SRV details / only knowing the GPU address... but the same dangers apply: if you read out of bounds, you're dead.

Likewise, a shader can read from a RWStructuredBuffer without knowing the full UAV details (if you're careful).

Hodgman, 10x.

It looks really wierd starting with D3D10 where the obligatory notion of the view was introduced.

The documentation for SetGraphicsRootShaderResourceView is indeed wrong in the sense that it shouldn't mention constant buffers, I'll have the team fix that.

Hodgman is correct, a GPU_VIRTUAL_ADDRESS can be used any time a pointer is all that's necessary to read (or write) to a resource.

Constant Buffers can be used this way, but you lose the hardware's ability to return 0 for out-of-bounds access. I've almost never seen a title rely on this behaviour anyway, but if you need it, you'll have to use a CBV.

Textures can't use the pointer-only approach, Buffers can. However, only unformatted Buffers (StructuredBuffer, RWStructuredBuffer, ByteAddressBuffer, RWByteAddressBuffer) work this way since the format of the Buffer is explicitly declared in HLSL. Formatted Buffers such as Buffer<float2>, RWBuffer<uint4> do not work this way since there are a great many DXGI_FORMATs that can be used for these buffers. 'float2' doesn't necessarily mean "Two 32-bit IEEE floats", it could be R8G8_UNORM, R16G16_FLOAT, R16G16_UNORM etc. The 'Format' is stored in the SRV, so you can't use the 'Root' functions as there's no space to store a 64-bit address and a Format in only 64 bits! Same rules apply to UAVs as to SRVs.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

Thanks Adam for the detailed explanation!

IIRC, these functions actually skip the need to create a view at all... which makes them slightly faster, and much more dangerous.
Normally a CBV has two members: BufferLocation and SizeInBytes.
When you use SetGraphicsRootConstantBufferView, it's equivalent to just setting the BufferLocation and leaving SizeInBytes as a magic value of "unknown".

This means that if you provide an address of a GPU allocation that's only 4 bytes large, but your shader is expecting to be able to read a 256 byte structure, then very bad things will happen -- you'll most likely crash the GPU hardware, etc...

Regarding SRV's, AFAIK this isn't usable for texture's - only for buffers. This is because textures really do require knowledge of the members in the SRV, such as width/height/format.
It's possible for a shader to read from a StructuredBuffer without knowing the full SRV details / only knowing the GPU address... but the same dangers apply: if you read out of bounds, you're dead.

Likewise, a shader can read from a RWStructuredBuffer without knowing the full UAV details (if you're careful).

I reviewed the my own code and found that what I used in my code base is not SetGraphicsRootShaderResourceView() mentioned by _void_ but SetGraphicsRootDescriptorTable(), so I actually didn't know about the latter. Can we say, the direct address set by calling SetGraphicsRootShaderResourceView() will come along with the risk of crossing range but it won't happen if we specify it via descriptor table?

Correct, there is additional information stored in descriptors which should prevent out-of-bounds access from causing problems. Writes get discarded and reads return 0.

Correct, there is additional information stored in descriptors which should prevent out-of-bounds access from causing problems. Writes get discarded and reads return 0.

OK it's clear now, that's why the those functions are not mentioned in the SDK's descriptions, not a recommended way in D3D12.

This topic is closed to new replies.

Advertisement