OpenGL 4.5 - Shader storage buffer objects layout
Asked Answered
C

0

6

I'm trying my hand at shader storage buffer objects (aka Buffer Blocks) and there are a couple of things I don't fully grasp. What I'm trying to do is to store the (simplified) data of an indeterminate number of lights n in them, so my shader can iterate through them and perform calculations.

Let me start by saying that I get the correct results, and no errors from OpenGL. However, it bothers me not to know why it is working.

So, in my shader, I got the following:

struct PointLight {
  vec3 pos;
  float intensity;
};

layout (std430, binding = 0) buffer PointLights {
  PointLight pointLights[];
};

void main() {
  PointLight light;
  for (int i = 0; i < pointLights.length(); i++) {
    light = pointLights[i];
    // etc
  }
}

and in my application:

struct PointLightData {
  glm::vec3 pos;
  float intensity;
};

class PointLight {
  //  ...
  PointLightData data;
  // ...
};

std::vector<PointLight*> pointLights;

glGenBuffers(1, &BBO);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, BBO);
glNamedBufferStorage(BBO, n * sizeof(PointLightData), NULL, GL_DYNAMIC_STORAGE_BIT);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, BBO);

...

for (unsigned int i = 0; i < pointLights.size(); i++) {
  glNamedBufferSubData(BBO, i * sizeof(PointLightData), sizeof(PointLightData), &(pointLights[i]->data));
}

In this last loop I'm storing a PointLightData struct with an offset equal to its size times the number of them I've already stored (so offset 0 for the first one).

So, as I said, everything seems correct. Binding points are correctly set to the zeroeth, I have enough memory allocated for my objects, etc. The graphical results are OK.

Now to my questions. I am using std430 as the layout - in fact, if I change it to std140 as I originally did it breaks. Why is that? My hypothesis is that the layout generated by std430 for the shader's PointLights buffer block happily matches that generated by the compiler for my application's PointLightData struct (as you can see in that loop I'm blindingly storing one after the other). Do you think that's the case?

Now, assuming I'm correct in that assumption, the obvious solution would be to do the mapping for the sizes and offsets myself, querying opengl with glGetUniformIndices and glGetActiveUniformsiv (the latter called with GL_UNIFORM_SIZE and GL_UNIFORM_OFFSET), but I got the sneaking suspicion that these two guys only work with Uniform Blocks and not Buffer Blocks like I'm trying to do. At least, when I do the following OpenGL throws a tantrum, gives me back a 1281 error and returns a very weird number as the indices (something like 3432898282 or whatever):

const char * names[2] = { 
  "pos", "intensity"
};

GLuint indices[2];
GLint size[2];
GLint offset[2];

glGetUniformIndices(shaderProgram->id, 2, names, indices);
glGetActiveUniformsiv(shaderProgram->id, 2, indices, GL_UNIFORM_SIZE, size);
glGetActiveUniformsiv(shaderProgram->id, 2, indices, GL_UNIFORM_OFFSET, offset);

Am I correct in saying that glGetUniformIndices and glGetActiveUniformsiv do not apply to buffer blocks?

If they do not, or the fact that it's working is like I imagine just a coincidence, how could I do the mapping manually? I checked appendix H of the programming guide and the wording for array of structures is somewhat confusing. If I can't query OpenGL for sizes/offsets for what I'm tryind to do, I guess I could compute them manually (cumbersome as it is) but I'd appreciate some help in there, too.

Chloe answered 26/7, 2017 at 4:56 Comment(6)
Are you sure it breaks with std140 for this exact definition of PointLight structure? In this particular case, there should not be a difference between std140 and std430. Now, if you remove the intensity member for instance, there would be a difference.Grope
well spotted! it does indeed work, so what I said was hasty. What are the sizes and offsets that you came up with for the structure? By my counting, if I get this right, size is 16 bytes (3*4 + 1 *4) and alignment is 12 bytes (3*4, i.e., the size of the vec3). Is that correct?Chloe
Having re-read the relevant portion of the spec, it looks like there won't be a difference between std140 and std430 even if you remove the intensity member. The alignment of struct PointLight is still 16 bytes, enforced by rule 3, that says the alignment of vec3 is the same as that of ver4.Grope
Based on some experiments I just did I agree that the alignment must be 16 bytes, but I don't get why. Reading rule 3 it is states this is true only when the member is not part of an array or nested structure. So I refer to rule 7, for arrays of structures (which is this case), and there it says, structure alignment is the same as the alignment for the biggest structure member, where 3-component vectors *are not* rounded up to the size of 4 component vectors. Shouldn't the alignment be 12, then? I am confused about this.Chloe
snap, I think I get it. They are not rounded up in size, but they definitely are in alignment. That's why it's 16, that vec3 takes up 12 bytes (according to the exception in rule 7) but is aligned on 16, as per rule 3.Chloe
Your understanding is correct, but it's true for both std140 and std430. The difference is that with std140, array elements get rounded up to 16 bytes just because, while with std430 it only happens if that array element contains a member with such an alignment.Grope

© 2022 - 2024 — McMap. All rights reserved.