MP0
MP0

Reputation: 1033

What is the logic behind glGetActiveUniformBlockParam of GL_UNIFORM_BLOCK_DATA_SIZE?

My shader source is this one :

#version 330

uniform mat4 camera;

struct S {
  vec2 v;
  mat2 m;
};

layout(std140) uniform SS {
 S s[2];
};

in vec2 v;

void main () {
  gl_Position = camera * vec4(v.x + s[0].m[0][0], v.y, 0.0, 1.0);
}

When I ask how big is my uniform block, GL answers 96. But if I assume that a mat2 is 4 floats, a vec2 is 2 floats and I have two of them, and that a float is 4 bytes, then:

(4 + 2) * 2 * 4 = 48

I find 48 bytes… Why so much ?

Maybe it comes from alignement of my structs to 4 bytes. Let's add 2 bytes of padding to account for alignment, after my vec2:

(4 + 2 + 2) * 2 * 4 = 64

This is 64, not 96. So where am I wrong? In fact I'm quite sure my floats are 4 bytes, since I can retrieve them when they follow each other…

Upvotes: 2

Views: 239

Answers (1)

Andon M. Coleman
Andon M. Coleman

Reputation: 43369

Believe it or not, this is normal for the std140 layout. To understand why, you need to read up on the alignment rules outlined below:

OpenGL 4.4 Core Profile Specification - 7.6.2.2 Standard Uniform Block Layout - pp. 131

Rule (4):

If the member is an array of scalars or vectors, the base alignment and array stride are set to match the base alignment of a single array element, according to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The array may have padding at the end; the base offset of the member following the array is rounded up to the next multiple of the base alignment.

Rule (5):

If the member is a column-major matrix with C columns and R rows, the matrix is stored identically to an array of C column vectors with R components each, according to rule (4).


I have re-written your data structure to indicate how it looks from the perspective of std140 alignment:

struct S {
  vec2 v;
  vec2 v_padding;  // NEW (satisfies rule 4 -- mat2 has base alignment 4N)

  //mat2 m;        // ORIGINAL (re-written below for alignment -- see rule 5)

  vec2 m0;
  vec2 m0_padding; // NEW (satisfies rule 4 -- column vectors are aligned to 4N)
  vec2 m1;
  vec2 m1_padding; // NEW (satisfies rule 4 -- column vectors are aligned to 4N)
};
  • vec2 v -> 2N + 2N
  • mat2 m -> 2N + 2N + 2N + 2N

Total Struct Size (in machine units): 4N + 8N = 12N

Size (in bytes) for array: S s [2]:

  12N * 2 = 24N * 4/N = 96


Your biggest problem here is the use of a mat type.

Matrices are treated as arrays of vectors, which adds extra alignment oddities. In some cases you can get around these oddities by using row-major matrices, but since this is a square matrix that will not help any.

Instead of a mat2, I would suggest you pack your data into a vec4.

struct S {
  vec2 v; // 2N + 2N (padding)
  vec4 m; // 4N
};        // 8N

2N + 2N + 4N = 8N * 2 * 4/N = 64 bytes

You can write some extra code to unpack the vec4 into a mat2 variable if you really need a mat2 in your shader.

Upvotes: 3

Related Questions