I would like to know which resource type is the best to use in order to hold as many elements as possible for data that will be static and not change for the duration of a draw call. The only difference I can tell between a constant buffer and a read-only structured buffer is the fact that constant buffer data has to be 256 bytes aligned inside of its ID3D12Resource
object.
lights.hlsl
#define as_many_as_possible 1000
struct light
{
float3 position;
float falloff_start;
float3 direction;
float falloff_end;
float3 color;
float3 strenght;
};
struct light_data
{
light lights [as_many_as_possible];
};
ConstantBuffer<light_data> cb_lights : register(b0);
// Versus
StructuredBuffer<light> sb_lights : register(s0);
If my goal is to hold data for as many lights as possible, which one is better?
There are more differences between constant and structured buffers.
In a constant buffer, Only 64k of data can be visible at the same time, so you can't have 1mb of data and have it visible at once in your shader, whereas it is possible on structured buffers.
Constant buffers have more complex alignment rules than structured buffers, you example actually fits it pretty well:
In case of a structured buffer, your light struct has a size of:
struct light
{
float3 position; /12
float falloff_start; /4 -> 16
float3 direction; /12 -> 28
float falloff_end; /4 -> 32
float3 color; /12 -> 44
float3 strenght; /12 -> 56
};
So your data will be interpreted as an array of 56 bytes structs.
However, constant buffer struct alignment requires 16 byte, so your struct will be interpreted as :
struct light
{
float3 position; /12
float falloff_start; /4 -> 16
float3 direction; /12 -> 28 (not 16 byte boundary crossing)
float falloff_end; /4 -> 32
float3 color; /12 -> 44 (no 16 byte boundary crossing)
float pad; /4 -> 48 (float3 would cross 16 boundary)
float3 strenght; /12 -> 60
float pad2; /4 ->64 (next float3 would cross 16 boundary, which is the next position in the array, there is no end padding for the last element of the array however)
So your lights will be 64 bytes (which needs to match your cpu struct, otherwise your data will not match).
On some hardware, because of those limitations, read access can be much more optimized in case of constant buffers vs structured buffers. This depends on many factors (read frequency in your shader for example), so you need to profile to see what would be the difference in your use case.
Also some vendors (like NVidia) recommend that structs should be aligned to a 16 byte boundary for performance reasons (in the case of Structured buffers), so in your case struct would be :
struct light
{
float3 position; /12
float falloff_start; /4 -> 16
float3 direction; /12 -> 28
float falloff_end; /4 -> 32
float3 color; /12 -> 44
float3 strenght; /12 -> 56
float2 _pad;
};