Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
937 views
in Technique[技术] by (71.8m points)

opengl - Why can glBufferData buffer structs for UBO and SSBO when c++ does not specify struct layout

I was browsing this page on how to use Uniform Buffer Objects in openGL and saw the following struct:

struct shader_data_t
{
    float camera_position[4];
    float light_position[4];
    float light_diffuse[4];
} shader_data;

being buffered into an openGL Uniform Buffer Object using

GLuint ubo = 0;
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, sizeof(shader_data), &shader_data, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);

and being used in the shader as

...
layout (std140) uniform shader_data
{ 
  vec4 camera_position;
  vec4 light_position;
  vec4 light_diffuse;
};
...

However, I don't understand how openGl knows how to map the data in the struct to the uniforms when glBufferData converts the struct pointer and a void pointer, which should make openGL unaware of the memory layout of the struct. c++ struct layout is implementation defined, and while the author and other users of UBOs manually pad their c++ structures with dummy variables to match the standardized std140 layout in the shader, what prevents the c++ compiler from adding more padding and breaking the layout? is there a strong guarantee in the c++ standard that more padding will not be inserted or is this a "practically portable" deal?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

OpenGL defines, very clearly, what the byte layout of a std140 interface block is. All you have to do on the C++ side is provide data in accord with that layout. If you can define a struct that your compiler will match with std140, then you're fine. How do you do that?

You have to know the rules that your compiler uses to lay out types.

C++11 defines a concept called "standard layout types". If you follow certain rules, your types are standard layout. Now, this doesn't really mean much for knowing exactly how they are laid out in memory. The only things C++ tells you about standard layout types with regard to layout is that empty base classes are ignored (so long as it remains standard layout) and that the first NSDM will be at the very beginning of the class. That is, there will never be padding at the front.

The other thing the standard says is that NSDMs of the same access class will be allocated in order, with later ones having larger offsets than earlier ones. And since you're not allowed to have different NSDMs of different access classes in standard layout types, you can rely on them being laid out in the order specified.

But that's it, as far as the C++ standard is concerned. [class.mem]/13 states that implementations can add padding between members for various reasons.

Informally however, the rules of "standard layout types" give you a good guideline to know when such padding won't be added. Follow the rules of standard layout, and you can assume, for most systems, that your class's layout will be as tightly packed as the sizes and alignments of the types being used permit.

Do implementations have to play along? No. But there's really no reason to assume that they won't. And worst comes to worst, you can always check to see how implementations lay types out.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...