Simple features I'm missing in GLSL: enums and sizeof

Hi all,
I’ve been working on a game engine for a while, based on Vulkan and using GLSL compute shaders extensively.

I’ve been puzzled about two seemingly very simple features that I’m constantly missing in the code I’m writing:

  • Auto-incremented enum style constants. I’m doing a lot of comparing against lists of IDs or types that I would naturally implement as enums in C, and that I’d like to match with the values of my C-side enums that are being passed by value from C to the compute shaders and back.
    I’m working around this by creating constant integers with the values manually written in, but it is quite error prone and would seem like both a very common use case and simple to implement.
    I’ll probably add some simple C-side glsl pre-processing of my own to improve my workflow, but it seems like it would be a generally useful feature.

  • A sizeof operator would be tremendously useful in debugging alignment and padding issues. Ideally with an accompanying offsetof operator. Getting data in a uniform or buffer struct to line up properly from C to GLSL so its contents are equally usable on both ends is a recurring pain and I’m having to do a lot of blind trial and error. My structs have many members of various sizes, so I often mess up my padding on the corresponding C side, though I’m getting better at learning the rules (and using more and more vec4s even when not strictly needed…).
    Being able to return the struct size or member alignment seems like it would help a lot to cut through to the answer, rather than my current method of writing a debug value into a struct member from the compute shader and checking where it landed on the C side. I don’t see why this wouldn’t be trivial to implement.
    What I might do to help with this is parse the disassembled SPIR-V for clues on how struct member offsets differ from my matching C-side structs. But again, this seems so basic that I don’t understand why it isn’t already available.

I haven’t seen much or any discussion around these two features - how does everyone manage to pass properly structured data to their compute shaders and back? Am I just using compute for more than it is intended for? I must say, apart from those two details that I’m manually working around, things have been working pretty great, so I don’t see why not to use compute shaders in this way.

Curious to hear everyone’s thoughts and if you think these should be features in the language or not.

How do you plan to get that value back? Any such sizeof operator would be inside your shader, not in OpenGL’s interface to the program.

In any case, OpenGL has a healthy introspection API that allows you to query anything you want about a UBO/SSBO’s layout. At least that way, you’d get the information in the right place: in your C++ code, not your shader code.

Follow these rules:

  1. Stop using vec3. This includes 3-column matrices as well.

  2. Only use mat4 matrices.

  3. In C++, set the alignas of each vec2 type to 8 and each vec4 type to 16. Also, set mat4's alignment to 16.

  4. In C++, for any struct you intend to use in a UBO/SSBO, alignas it to 16.

  5. For std140 layouts, never use arrays of anything other than structs or vec4-equivalent types.

  6. In C++, for any array member of a struct, alignas it to 16.

There; done.

Thanks for your insights, Alfonse.

I’m actually using Vulkan rather than OpenGL, which means OpenGL introspection is not an option here. I don’t think there’s any direct equivalent in Vulkan, unfortunately.

My use case is mostly with compute shaders, which means I have an easy way of reading back values for debugging, and use that extensively already.

With that said, I actually found an alternate way of getting the expected padded size of Uniforms: by inspecting the program with RenderDoc. It does not quite give me member-level information, but is able to display the expected size of a UBO, which will greatly help.

Thanks. I do follow a lot of these rules indeed to simplify things where possible.
However, a lot of the data I’m passing is individual float / bool / int values, and those don’t play as nice with these concepts so I do have to group them together and apply manual padding on the C side, figuring out alignment as I go.
My engine is written in C rather than C++, so alignas doesn’t apply in my case. It’s great advice for more common use cases of course. I don’t mind doing things manually as long as I can somehow check for issues.

I realize that my use case is unusual and falls outside the bounds of most other applications, so I should not expect support for things that no one needs. I was just curious if others had ever encountered these situations.

Thanks for your time

Show me an example where you use alignment (see below for how to do this in C) in accord with my rules where you need manual packing. It shouldn’t be possible.

_Alignas exists in C11. Not available under MSVC, but all other C compilers support it.

Well, the manual packing is precisely because I’m not using alignas. It’s likely that this would help in a lot of cases.

My windows version is being compiled with MSVC, so that is unfortunately not an option in this case. However, maybe that’ll prompt me to upgrade to another compiler even on Windows.

In the end though, following those rules certainly helps - but my problems usually stem from forgetting to apply one of them. My latest misalignment happened because I created a float array in an std104 uniform struct and matched it up with a float array in C. It would have helped me realize my mistake faster if I had some kind of way of seeing how things are being laid out in GLSL memory. I did find the problem after some trial and error, so all is well now - until I make another change a few months from now and forget one or the other of the alignment rules.

I guess it’ll be up to me to be careful.