0

I'm trying to create a fragment shader for a Vulcan application that takes an array of texture2Ds. I have this working however i need to set the size of the array at application run time rather than compile time.

I'm trying to do this using a push constant to push the number of textures into the shader to set the array size. But my GLSL compiler is complaining that the push constant is not a constant integer expression.

Here is my current shader, it works however produces an error message if provided less than 8 textures at runtime.

#version 450

layout(set = 0, binding = 1) uniform sampler samp;
layout(set = 0, binding = 2) uniform texture2D textures[8];

layout(location = 0) in flat uint fragTexID;

layout(location = 1) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

void main() {
    outColor = texture(sampler2D(textures[ fragTexID ], samp), fragTexCoord);
}

And now adding in push constants to set the size of the textures array

#version 450

layout(push_constant) uniform PushConstants {
    uint myUint;
} pc;

layout(set = 0, binding = 1) uniform sampler samp;
layout(set = 0, binding = 2) uniform texture2D textures[ pc.myUint ];

layout(location = 0) in flat uint fragTexID;

layout(location = 1) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

void main() {
    outColor = texture(sampler2D(textures[ fragTexID ], samp), fragTexCoord);
}

But when compiled this complains that "array size must be a constant integer expression", I assumed push constants would be constant, especially since it is defined as uniform.

I have also tried explicity stating my uint myUint as uniform uint myUint but get the same error.

Any idea what i'm doing wrong here, or if this approach won't work, is it possible to set the array size at runtime?

Thank-you :)

1
  • 1
    I believe you can have an array without a size specified at all (which will make it entirely dynamic, the shader doesn't actually care how large the array is, only which index it needs to linearly access). Pretty sure you'll need the nonuniform extension. The term to research is "bindless texturing", it's a pretty large topic. Commented Apr 3 at 15:21

1 Answer 1

2

Push constants are not compile time constants, they are "constants" that effectively piggy-back on commands sent to the GPU so that they don't have to be set via updating uniforms or global memory.

In that sense, you should think of push constants in the same category of memory as a uniform itself is. You can't set the size of a texture array from a uniform value, so you can't do that from a push constant either.

Basically you're trying to do this:

layout(set = 0, binding = 0)  uniform PushConstants {
    uint myUint;
} pc;

layout(set = 0, binding = 1) uniform sampler samp;
layout(set = 0, binding = 2) uniform texture2D textures[ pc.myUint ];

If you want a dynamically sized list of textures, you'll need to use an array of textures (not a TextureArray2D) which allows you to have a dynamically sized list of textures as a uniform, and to take advantage of that you'll need "Descriptor Indexing". This will allow your example to turn into:


layout(set = 0, binding = 1) uniform sampler samp;
layout(set = 0, binding = 2) uniform texture2D textures[];

Additionally, you can utilize specialization constants if you don't change the size but per user configuration or something, but will require pipeline re-creation on update.

layout(constant_id = 0) const int texture_size = 8; // = 8 is the default, will update when you update the constant. 
layout(set = 0, binding = 1) uniform sampler samp;
layout(set = 0, binding = 2) uniform texture2D textures[texture_size];
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.