• Advertisement
Sign in to follow this  

C++ struct as value in array, what about cross platform padding?

This topic is 2160 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Certain functions, for example glBufferData takes a pointer to an array, for example with x, y, z, tex and so on. It's convenient to store these values in a statically typed struct, and I've seen several developers do this. But the problem is that a compiler can insert padding into a struct on an implementation basis. You could use pragma pack, but its compiler specific. How do you solve this in an efficient way?

I'm on my phone, so I apologize for any weirdness.

Share this post


Link to post
Share on other sites
Advertisement
As I see it your two best options are likely:
1. Simply don't worry about padding until it actually has become a bottleneck. Use sizeof and let it be. Might be a waste of time to worry about it.
2. If you know it's a problem use pragma pack or whatever your current compiler requires and don't worry about compiling on some different compiler until you actually need it. Add ifdef for specific compilers when you do.

Share this post


Link to post
Share on other sites
Solve what exactly?
That your struct layout might not match your idea GL vertex layout.
e.g. Given a vertex layout that has float x/y/z position, 8 bit tex-coords, and float RGB colour, we could write this structstruct Vertex {
float x, y, z;
unsigned char u, v;
// most compilers will insert padding here, e.g. "char _pad_[2];"
float r, g, b;
};
The problem is that because of the padding, our struct doesn't match the original description, so you can't give it to GL.

To solve this, either use pragma pack (it is fairly cross-platform, I use it often at work), or simply don't use structs like this ;)

Share this post


Link to post
Share on other sites
The most applicable example for this that I considered was also vertex packing for OpenGL.
In my case I store everything in an LSUINT8 * array (LSUINT8 = unsigned char).
I can then enforce my own alignment conditions and be sure they are always the same alignment on every platform.

OpenGL is going to accept any stride you give it, so if you are aware that certain platforms like certain alignments on your data types, and would otherwise adjust your structures accordingly, you can handle this type of alignment manually with little effort, but guaranteed results.
If you don’t handle these, you are still guarantee that your code will work anyway, even if it is a little slower.
If it were to crash due to floats not being 4-byte aligned (hello armv7 without floating-point library calls), at least the crash is easily traceable to its actual source. Without crashing, you would be left with jumbled OpenGL ES 2 graphics and cranking up your puzzler.


L. Spiro

Share this post


Link to post
Share on other sites

[quote name='Rene Z' timestamp='1332245371' post='4923588']Solve what exactly?
That your struct layout might not match your idea GL vertex layout.
e.g. Given a vertex layout that has float x/y/z position, 8 bit tex-coords, and float RGB colour, we could write this structstruct Vertex {
float x, y, z;
unsigned char u, v;
// most compilers will insert padding here, e.g. "char _pad_[2];"
float r, g, b;
};
The problem is that because of the padding, our struct doesn't match the original description, so you can't give it to GL.

To solve this, either use pragma pack (it is fairly cross-platform, I use it often at work), or simply don't use structs like this ;)
[/quote]
For the record, unless padding is inserted in the middle of the doublets/triplets, the padding between the texture coordinate and the color is no problem for OpenGL. That structure is, in practice, perfectly fine.

Share this post


Link to post
Share on other sites

For the record, unless padding is inserted in the middle of the doublets/triplets, the padding between the texture coordinate and the color is no problem for OpenGL. That structure is, in practice, perfectly fine.


How do you mean 'is no problem' for OpenGL? It doesn't know anything about your struct. Do you mean that you would use stride = sizeof(struct) and then offset according to what field you are interested in? Or something else? offsetof seems to be somewhat deprecated / frowned upon, and even so it might not be possible if the padding is in the wrong place?


OpenGL is going to accept any stride you give it, so if you are aware that certain platforms like certain alignments on your data types, and would otherwise adjust your structures accordingly, you can handle this type of alignment manually with little effort, but guaranteed results.


How do you mean, handle with little effort? How would you handle it?

Cheers :)

Share this post


Link to post
Share on other sites

That your struct layout might not match your idea GL vertex layout.
e.g. Given a vertex layout that has float x/y/z position, 8 bit tex-coords, and float RGB colour, we could write this structstruct Vertex {
float x, y, z;
unsigned char u, v;
// most compilers will insert padding here, e.g. "char _pad_[2];"
float r, g, b;
};
The problem is that because of the padding, our struct doesn't match the original description, so you can't give it to GL.

To solve this, either use pragma pack (it is fairly cross-platform, I use it often at work), or simply don't use structs like this ;)


Couldn't you solve this by adjusting the stride when calling glVertexPointer, glTexCoord, etc. using offsetof?

Share this post


Link to post
Share on other sites

[quote name='Brother Bob' timestamp='1332254622' post='4923630']
For the record, unless padding is inserted in the middle of the doublets/triplets, the padding between the texture coordinate and the color is no problem for OpenGL. That structure is, in practice, perfectly fine.


How do you mean 'is no problem' for OpenGL? It doesn't know anything about your struct. Do you mean that you would use stride = sizeof(struct) and then offset according to what field you are interested in? Or something else? offsetof seems to be somewhat deprecated / frowned upon, and even so it might not be possible if the padding is in the wrong place?
[/quote]
OpenGL doesn't know about your structure, but you know about it. No matter how much padding is inserted, the stride in an array of structures is always sizeof() the struct, and offsetof() can be used to get the offset. The stride and the offsets are all you need to properly use the structure in a vertex array.

Share this post


Link to post
Share on other sites
struct Vertex {
float x, y, PADDING, z;
unsigned char u, v;
float r, g, b;
};


The above is unlikely, but theoretically possible. It's as possible as the compiler inserting a pad between the semantic data (i.e you can fix it with offsetof). If this happens it won't work whatever you do using offsetof?

Share this post


Link to post
Share on other sites
What compiler are you using that will even consider inserting padding there? If you are worried about the compiler padding the structure there, then no, you cannot use the structure in a vertex array.

Share this post


Link to post
Share on other sites
How do you solve this in an efficient way?[/quote]

First you make a list of platforms you will support.
Next you choose a compiler for that platform, down to minor version. If compilers are subject to change, you add that to your upgrade plan and allocate time.
Finally, you make an automated integration test which includes compiler flags, project options, etc. that run through desired code examples to ensure each compiler does what you want.

For desktop, number of distinct configurations will be anywhere up to ~6 or so.

Then you don't change any of the above without realizing how much effort such a change brings. This helps you resist constantly tweaking the tools and upgrading for sake of upgrading and everything else.


In short, the very definition of engineering. You work only with known knowns and treat any change, no matter how well meant or which "best practice" it promises as either known unknown or unknown unknown. The latter two are realizing that even if you're aware of problems in theory, there be always dragons there. Such methodology also helps you keep focus, since it prevents over-engineering and the "just in case" or "maybe I'll need it later" or "it's more generic" type of thinking which doesn't really do any good.

Share this post


Link to post
Share on other sites
Couldn't you solve this by adjusting the stride when calling glVertexPointer, glTexCoord, etc. using offsetof?
Yes you could, but it's the wrong solution. You want your GPU-consumed data to be packed as tightly as possible, whereas this solution bloats your data simply for convenience of the programmer. Do you really want to slow down the processing of every single vertex, just to avoid writing two extra lines of C++?

First you decide on your vertex format considering the GPU, and then you create a C++ way of interacting with the format if required. If the second step requires you to go back and re-do the first step, then you're doing it wrong.
[font=courier new,courier,monospace]offsetof[/font] and [font=courier new,courier,monospace]sizeof[/font] should only be used to assert that your C++ structure layout matches your originally-decided-upon layout.

Share this post


Link to post
Share on other sites
In my structure of data sent to the GPU, I try to use the same data types with the same alignment, and furthermore, I use a structure with arrays inside. While this isn't always aesthetically preferred, it puts my mind at ease that one element will come after the other, with nothing in between, and it will be properly aligned. This way, I can use the offset of the first element, and it will work regardless of default alignment or padding rules, and by using the same types, it ensures that they will be similarly aligned.

If you really must use different types for space reasons, put structure members of the same size and alignment together in such a way that they equal the alignment and size requirements of the largest member. If you end up with an uneven count of bytes, then add the smaller ones that don't fit without padding to the end, and sort by largest to smallest, so that if there is any padding, it is at the end, after the members. With the padding past the data, you don't need to worry about it, and it might not exist at all.

Just don't do something silly and assume it will be padded, and then get an unaligned access fault, because the second element in the array of structures is unaligned due to the lack of padding. If you _must_ pad, no one is stopping you from adding more bytes to ensure that the size is a multiple of the alignment requirements. If the structure is unpadded, your padding should work out. If the structure gets padded, and it is to such a higher number than anticipated, then conditional compile to either pad more, or instruct the compiler to align to a certain value; if a compiler pads a structure, it's likely that the compiler supports you changing its padding and alignment.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement