1
$\begingroup$

In HLSL, we have cbuffers and tbuffers.

MSDN's description for tbuffers is the following:

A texture buffer is a specialized buffer resource that is accessed like a texture. Texture access (as compared with buffer access) can have better performance for arbitrarily indexed data. You can bind up to 128 texture buffers per pipeline stage.

what is the difference to cbuffers? in cbuffers i can access the data arbitrarily, too. When should i use a tbuffer and when should i use a cbuffer?

$\endgroup$

2 Answers 2

1
$\begingroup$

Keep in mind, that I'm still learning and I have no professional capacity in terms of graphics programming. So you might want to continue research with provided resources.

t*#*: A register for a texture buffer (tbuffer).[1]

To start with - texture buffer is what you can see in hlsl code as: Texture2D texName : register(t0) or as Texture2D texName : register(t0, space0). If I remember correctly, spaces were added in DirectX 12, maybe they're available in DX 11.3, but that's different discussion. Back to my point - you're using texture buffer every time you're binding texture to your shader and sending texture data to GPU. Therefore, using PSSetShaderResources [2] will be most trivial usage of texture buffer, espacially in DX11 code. You can use constant buffer to store pixel data and then send it to GPU, but why should you bother, when Texture2D or TextureCube and other data structures are already in hlsl?


cbuffer (DX11/DX12) forces you to pack data in 32-bit groups [3]. You might have experienced it, if not, I encourage you to experiment a little. In the past I didn't follow this rules, having that packed like this, which causes problem with incorrect data in your shader:

cbuffer buf : register(b0)
{
    float3 a;
    // Here is where problem occurs
    float2 b;
    float3 c;
};

Correct packing rule would be:

cbuffer buf : register(b0)
{
    float3 a;
    float padding_a;
    float2 b;
    float2 padding_b;
    float3 c;
    float padding_c;
};

Of course, I am using worst possible example here, because correct data is 8 bytes and you're sending additional 4 bytes (whole hlsl vector) as padding. But you get an idea. It might be easier to sort data with bigger buffers, especially if you're using uber buffer in your hobby project.


Additional problem in DX12 is that cbuffer has to be 256-byte aligned [4], unless you're using root constants, which allows you to send not aligned data, but they have other limitation. I was only talking about them with other developer, so I don't really have sources except [5]. There was something about size of buffer and that root constants are working magic in AMD drivers, but it isn't something that I really need right now, as I am focusing on learning, not extreme optimization of code.


Shader Model 5 allows you to use new data structures [6]. I wasn't working with compute shaders, but for DXR hlsl code, I am using RTTexture2D as UAV. However, I am passing indices, vertices and RaytracingAccelerationStructure as texture buffers. I didn't see anyone to use those three as cbuffer. I guess it's possible, at least for indices and vertices, but I don't see a reason to do so:

RaytracingAccelerationStructure SceneBVH : register(t0);
ByteAddressBuffer indices : register(t1);
StructuredBuffer<Vertex> vertices : register(t2);

Indices are simply 16 or 32 bit index for accessing vertex data. I don't see a reason to hassle with creating cbuffer for single array and having to deal with padding and providing correct size of buffer. However, I am really interested if there is a reason to do it sometimes.

Same with vertices - in rasterization, you're using structure which don't have to follow packing rules e.g.:

struct VertexInputType
{
    float3 position : POSITION;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
    float2 uv : TEXCOORD0;
};

So for raytracing, I am just using struct with same data and I'm accesing it through structured buffer, regsitered as texture buffer:

struct Vertex
{
    float3 position;
    float3 normal;
    float3 tangent;
    float3 binormal;
    float2 uv;
};
StructuredBuffer<Vertex> vertices : register(t2);

Regarding RaytracingAccelerationStructure - it's built-in hlsl type, no need to hassle with creating cbuffer.


I'm sorry for speaking from DX12, DXR perspective, but that's something that I'm currently working on, so it's a simplest way to provide examples from that area.

[1] https://docs.microsoft.com/en-us/windows/win32/direct3dgetstarted/work-with-shaders-and-shader-resources#read-from-the-constant-buffers

[2] https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-pssetshaderresources

[3] https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules

[4] https://social.msdn.microsoft.com/Forums/sqlserver/en-US/78dec419-6827-40fc-a048-01c3cccb92ef/directx-10-11-and-12-constant-buffer-alignment?forum=vsga

[5] https://docs.microsoft.com/en-us/windows/win32/direct3d12/using-constants-directly-in-the-root-signature

[6] https://docs.microsoft.com/en-us/windows/win32/direct3d11/direct3d-11-advanced-stages-cs-resources

$\endgroup$
2
  • 1
    $\begingroup$ Does that mean i can bind an arbitrary ID3D11Buffer as a texture? $\endgroup$
    – user11739
    Sep 25, 2020 at 15:49
  • $\begingroup$ @Raildex I can see that ID3D11Buffer is child of ID3D11Resource. I was using resources it only for textures in DX11. However, keep in mind that you're not binding stuff in texture buffers always as textures. StructuredBuffer is accessed as an array so it's not used as texture. You have to keep in mind that tbuffer doesn't mean that it's a texture. It would be best read resources above to better understand that. $\endgroup$ Sep 26, 2020 at 19:27
0
$\begingroup$

tbuffer is certainly not known as well as cbuffer is, I just found out that it was a valid keyword actually. Source for verification:
https://github.com/microsoft/DirectXShaderCompiler/blob/50c0199c17b0b7530cbc0bdddadc3697eaece695/tools/clang/include/clang/Basic/TokenKinds.def
It's line 485 in that revision.

What I'm guessing, as said by @DirectX_Programmer this is surely an effort to make symmetric the language handling of constant buffers by respect to more recently introduced structured buffer. Which uses different packing and alignment rules; and also different physical hardware memory depending on vendors.

tbuffer SkinConstants : register(t0)
{
   float4x3 bone_matrices[15];
}

// close in concept to (but oddly differs in packing rules):
struct SkC
{
    float4x3 bone_matrices[15];
};
StructuredBuffer<SkC> g_skc : register(t1);

// note that similarly, since HLSL6 the template type ConstantBuffer<T> can be used symmetrically instead of cbuffer.

Here a little experiment to demonstrate packing differences:
https://shader-playground.timjones.io/0421fd2f000cd65b771f12b387680fc7

Using the same source code as above, enriched with a supplementary variable float4 center, this is the layout:

Resource bind info for g_skc
; {
;
;   struct hostlayout.struct.SkC
;   {
;
;       column_major float3x4 bone_matrices[15];      ; Offset:    0
;       float4 center;                                ; Offset:  720
;   
;   } $Element;                                       ; Offset:    0 Size:   736
;
; }
;
; tbuffer SkinConstants
; {
;
;   struct hostlayout.SkinConstants
;   {
;
;       column_major float3x4 bone_matrices[15];      ; Offset:    0
;       float4 center;                                ; Offset:  960
;   
;   } SkinConstants;                                  ; Offset:    0 Size:   976
;
; }
;
;
; Resource Bindings:
;
; Name                                 Type  Format         Dim      ID      HLSL Bind  Count
; ------------------------------ ---------- ------- ----------- ------- -------------- ------
; g_skc                             texture  struct         r/o      T0             t1     1
; SkinConstants                     texture     u32     tbuffer      T1             t0     1
;

It appears that the tbuffer language construct, packs exactly like cbuffer does. I suppose it's a helper facility.

We shall note that in this documentation (DX11?) https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-constants it is mentioned that constant buffers are limited to "4096 128-bits vector variables". Whereas texture buffers are not. I suspect this is not true in DX12 anymore.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.