2

I try to optimize a working compute shader. Its purpose is to create an image: find the good color (using a little palette), and call imageStore(image, ivec2, vec4).

  • The colors are indexed, in an array of uint, in an UniformBuffer.
  • One color in this UBO is packed inside one uint, as {0-255, 0-255, 0-255, 0-255}.

Here the code:

struct Entry
{
    *some other data*
    uint rgb;
};

layout(binding = 0) uniform SConfiguration
{
    Entry materials[MATERIAL_COUNT];
} configuration;

void main()
{
    Entry material = configuration.materials[currentMaterialId];

    float r = (material.rgb >> 16) / 255.;
    float g = ((material.rgb & G_MASK) >> 8) / 255.;
    float b = (material.rgb & B_MASK) / 255.;

    imageStore(outImage, ivec2(gl_GlobalInvocationID.xy), vec4(r, g, b, 0.0));
}

I would like to clean/optimize a bit, because this color conversion looks bad/useless in the shader (and should be precomputed). My question is:

  • Is it possible to directly pack a vec4(r, g, b, 0.0) inside the UBO, using 4 bytes (like a R8G8B8A8) ?
Ealrann
  • 368
  • 1
  • 15
  • Unrelated to the original question, but why do you need to use `imageStore()` here? It's often slower and higher power consumption than just writing out a pixel from a fragment shader, and will often disable optimizations such as framebuffer compression. Don't use it unless you *really* need to; for simple cases like this fragment shaders are nearly always a better solution. – solidpixel May 02 '19 at 11:16
  • I don't use any vertex/fragment/rasterization for the main board (just for the UI). Here some pictures: https://github.com/Ealrann/VSand/blob/master/README.md . So I thought that the imageStore() is the best option for me, but well, I'm open to extra opinions because I'm still pretty new in the rendering world. Do you think it's a good use case for imageStore? – Ealrann May 02 '19 at 12:29

2 Answers2

5

Is it possible to do it directly? No.

But GLSL does have a number of functions for packing/unpacking normalized values. In your case, you can pass the value as a single uint uniform, then use unpackUnorm4x8 to convert it to a vec4. So your code becomes:

    vec4 color = unpackUnorm4x8(material.rgb);

This is, of course, a memory-vs-performance tradeoff. So if memory isn't an issue, you should probably just pass a vec4 (never use vec3) directly.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
0

Is it possible to directly pack a vec4(r, g, b, 0.0) inside the UBO, using 4 bytes (like a R8G8B8A8) ?

There is no way to express this directly as 4 single byte values; there is no appropriate data type in the shader to allow you to do declare this as a byte type.

However, why do you think you need to? Just upload it as 4 floats - it's a uniform so it's not like you are replicating it thousands of times, so the additional size is unlikely to be a problem in practice.

solidpixel
  • 10,688
  • 1
  • 20
  • 33
  • In fact, while writing the question, I thought the same. I don't have too many materials, so space is not a problem, and this buffer is never updated. Thank you for the answer. – Ealrann May 02 '19 at 12:31
  • Clarified answer just to remove any ambiguity about the use of the term "pack" given that you can use unpack as per @Nicol Bolas answer above. – solidpixel May 03 '19 at 08:21