0
\$\begingroup\$

I have written a compute shader to convert a height map into a normal map.

The shader definition (glsl) starts as follows:

#[compute]
#version 460

layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;

layout(set = 0, binding = 0, std430) buffer Workload {
    float vertical_scale[];
} params;

layout(r8, binding = 1) restrict readonly uniform image2D heightMap;

layout(rgba8, binding = 2) restrict uniform image2D normal;

void main() {
    float height = params.vertical_scale[0];

The shader is working fine (generating the correct normal map), however the I need to create a float buffer with only one element in it to pass my vertical_scale parameter into the shader.

It's not causing any issue (works fine) to do this - it just looks ugly, is there a better way to pass the single float parameter to the compute shader?

Godot 4.2.2, Mac OSX 14.5 using Vulcan

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

While it won't make the shader look that much better, you could use push constants (tutorial example). But it may make things simpler on the application/host side since you wouldn't have to go through all the steps to create and update the buffer and you'll need one less binding. It also means that you have to set the push constant prior to the dispatch in the command buffer, which may be convenient if you want to change that parameter frequently.

Another approach would be to use specialization constants (sample). In this case, you'll have to recreate the pipeline each time you change that parameter, or make a pipeline for every value for that parameter if there are not too many. This wouldn't really be an intended use for specialization constants since there isn't any way for the shader compiler to improve the code it generates by knowing the value of the constant at compile time since it is probably some arbitrary floating-point value. I don't think that the extra complexity on the application side would be worth it.

I think a push constant is your best choice since push constants are intended for passing small amounts of data to a shader, as noted in the tutorial link. Later, if your shader becomes more complex and needs a larger amount of parameter data, then a UBO is better.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for the answer, I agree that push constants are probably the right solution for the problem. However as you stated it doesn't really clean up the Godot side of the code, since you still have to manually pack any values into a byte array/buffer using GDScript. I will probably have a play with push constants, as I suspect there might be a slight performance gain over using regular uniforms - however for my current use case I don't think it will even be measurable. \$\endgroup\$
    – DavidT
    Commented Aug 8 at 21:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .