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.

Carlos Romero
  • 698
  • 8
  • 18
  • 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. – Joseph Artsimovich Jul 26 '17 at 14:57
  • 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? – Carlos Romero Jul 26 '17 at 15:03
  • 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. – Joseph Artsimovich Jul 26 '17 at 15:24
  • 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. – Carlos Romero Jul 26 '17 at 15:29
  • 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. – Carlos Romero Jul 26 '17 at 15:39
  • 2
    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. – Joseph Artsimovich Jul 26 '17 at 15:55

0 Answers0