Vulkan sampler buffer

Hello, I am currently learning Vulkan coming over from OpenGL and I want to implement a system similar to the opengl bindless textures feature where you can store an array of samplers in a shader storage buffer and the samplers can be accessed in the shader from the buffer. The closest thing I have found that is similar in Vulkan is using multiple uniform sampler descriptors on the same binding however is there a way to specify the number of samplers dynamically in the buffer?

Not sure if this is valid glsl but something like this

#version 460 

layout(location = 0) out vec4 OutColor;

layout(location = 0) in vec3 FragColor;
layout(location = 1) in vec2 TexCoord;
layout(location = 2) in float TexID;

layout(binding = 1) uniform sampler2D Samplers[]; 


void main() 
{
   int ID = int(TexID + 0.5);
   OutColor = texture(Samplers[ID], TexCoord) * vec4(FragColor, 1.0);
}

I believe that an unsized array like this will work (the descriptor set layout will contain the actual size), but I haven’t looked into the specification to make sure.

But be aware that a lot of hardware requires that the index into the array of textures must be dynamically uniform. And unless you have ensured that TexID is dynamically uniform (basically, every fragment in the entire draw call gets the same value), you will violate this requirement. You can query whether a particular piece of hardware requires dynamically uniform indices or not with the shaderSampledImageArrayNonUniformIndexing and shaderSampledImageArrayDynamicIndexing features.

That seems fine but the problem is defining it in the descriptor set layout is pretty restricting as its only set when the pipeline is being created. if the textures increase past the size i will have to recreate the whole pipeline with a larger size. is this normal in renderers? Is it just better off allocating a large size so something like that won’t happen, but a lot of extra space will be used for no reason.

And unless you have ensured that TexID is dynamically uniform (basically, every fragment in the entire draw call gets the same value),

Texture ID is instance specific so this shouldn’t be an issue.

I also realised another problem is that when a texture changes/is added the descriptor set will need to be updated as well and I am not sure of the overhead of this.

The extra space in that array is generally on the order of (64-bit) pointers of size per entry. That is, each entry might be anywhere from 1 pointer to maybe 16 pointers in size. So I would hesitate to categorize this as “a lot of extra space”.

That being said, there are queriable hardware limits to how many samplers you can have, even in arrays.

With descriptor indexing, the overhead is pretty minimal. As long as you were not actually accessing that array slot in a shader, you can just modify that array slot without any kind of synchronization.

The path you’re on is pretty commonly used in Vulkan applications.

1 Like

Sounds good too me thanks for your help, just one final thing i wanted to ask when recreating descriptors to a larger size would it be good to wait for the graphics queue to finish before recreating or is this something i can just do without synchronization?

What do you mean by “recreating descriptors to a larger size” here? If you have, for some reason, blown past your array size and need to make a larger one, you have to rebuild your pipelines and everything else. You’re not replacing anything, you’re making new stuff. So that’s not a synchronization thing.

And if you’re just adding new entries to the end of the array (or modifying unused entries in the middle), that doesn’t need synchronization either so long as descriptor indexing is enabled. And that doesn’t need “recreating” anything.

I see right thanks for your help, I am still new to vulkan and descriptors has been one of the hardest things to get my head around but i am understanding it now.