In my OpenGL ES 3.0 program I need to have two separate Uniform Buffer Objects (UBOs). With just one UBO, things work as expected. The code for that case looks as follows:
GLSL vertex shader:
version 300 es
layout (std140) uniform MatrixBlock
{
mat4 matrix[200];
};
C++ header file member variables:
GLint _matrixBlockLocation;
GLuint _matrixBuffer;
static constexpr GLuint _matrixBufferBindingPoint = 0;
glm::mat4 _matrixBufferContent[200];
C++ code to initialze the UBO:
_matrixBlockLocation = glGetUniformBlockIndex(_program, "MatrixBlock");
glGenBuffers(1, &_matrixBuffer);
glBindBuffer(GL_UNIFORM_BUFFER, _matrixBuffer);
glBufferData(GL_UNIFORM_BUFFER, 200 * sizeof(glm::mat4), _matrixBufferContent, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, _matrixBufferBindingPoint, _matrixBuffer);
glUniformBlockBinding(_program, _matrixBlockLocation, _matrixBufferBindingPoint);
To update the content of the UBO I modify the _matrixBufferContent array and then call
glBufferSubData(GL_UNIFORM_BUFFER, 0, 200 * sizeof(glm::mat4), _matrixBufferContent);
This works as I expect it. In the vertex shader I can access the matrices and the resulting image is as it should be.
The OpenGL ES 3.0 specification defines that the maximum available storage per UBO is 16K (GL_MAX_UNIFORM_BLOCK_SIZE). Because the size of my matrix array comes close to that limit I want to create a second UBO that stores additional data. But as soon as I add that second UBO I encounter problems. Here's the code to create the two UBOs:
GLSL vertex shader:
version 300 es
layout (std140) uniform MatrixBlock
{
mat4 matrix[200];
};
layout (std140) uniform HighlightingBlock
{
int highlighting[200];
};
C++ header file member variables:
GLint _matrixBlockLocation;
GLint _highlightingBlockLocation;
GLuint _uniformBuffers[2];
static constexpr GLuint _matrixBufferBindingPoint = 0;
static constexpr GLuint _highlightingBufferBindingPoint = 1;
glm::mat4 _matrixBufferContent[200];
int32_t _highlightingBufferContent[200];
C++ code to initialize both UBOs:
_matrixBlockLocation = glGetUniformBlockIndex(_program, "MatrixBlock");
_highlightingBlockLocation = glGetUniformBlockIndex(_program, "HighlightingBlock");
glGenBuffers(2, _uniformBuffers);
glBindBuffer(GL_UNIFORM_BUFFER, _uniformBuffers[0]);
glBufferData(GL_UNIFORM_BUFFER, 200 * sizeof(glm::mat4), _matrixBufferContent, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, _uniformBuffers[1]);
glBufferData(GL_UNIFORM_BUFFER, 200 * sizeof(int32_t), _highlightingBufferContent, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, _matrixBufferBindingPoint, _uniformBuffers[0]);
glBindBufferBase(GL_UNIFORM_BUFFER, _highlightingBufferBindingPoint, _uniformBuffers[1]);
glUniformBlockBinding(_program, _matrixBlockLocation, _matrixBufferBindingPoint);
glUniformBlockBinding(_program, _highlightingBlockLocation, _highlightingBufferBindingPoint);
To update the first UBO I still modify the _matrixBufferContent array but then call
glBindBuffer(GL_UNIFORM_BUFFER, _uniformBuffers[0]);
glBufferSubData(GL_UNIFORM_BUFFER, 0, 200 * sizeof(glm::mat4), _matrixBufferContent);
To update the second UBO I modify the content of the _highlightingBufferContent array and then call
glBindBuffer(GL_UNIFORM_BUFFER, _uniformBuffers[1]);
glBufferSubData(GL_UNIFORM_BUFFER, 0, 200 * sizeof(int32_t), _highlightingBufferContent);
From what I see, the first UBO still works as expected. But the data that I obtain in the vertex shader is not what I originally put into _highlightingBufferContent. If I run this code as WebGL 2.0 code I'm getting the following warning in Google Chrome:
GL_INVALID_OPERATION: It is undefined behaviour to use a uniform buffer that is too small.
In Firefox I'm getting the following:
WebGL warning: drawElementsInstanced: Buffer for uniform block is smaller than UNIFORM_BLOCK_DATA_SIZE.
So, somehow the second UBO is not properly mapped somehow. But I'm failing to see where things go wrong. How do I create two separate UBOs and use both of them in the same vertex shader?
Edit
Querying the value behind GL_UNIFORM_BLOCK_DATA_SIZE that is expected by OpenGL reveals that it needs to be 4 times bigger than it is now. Here's how I query the values:
GLint matrixBlock = 0;
GLint highlightingBlock = 0;
glGetActiveUniformBlockiv(_program, _matrixBlockLocation, GL_UNIFORM_BLOCK_DATA_SIZE, &matrixBlock);
glGetActiveUniformBlockiv(_program, _highlightingBlockLocation, GL_UNIFORM_BLOCK_DATA_SIZE, &highlightingBlock);
Essentially, this means that the buffer size must be
200 * sizeof(int32_t) * 4
and not just
200 * sizeof(int32_t)
However, it is not clear to me why that it. I'm putting 32-bit integers into that array which I'd expect to be 4 byte in size but they seem to be 16 bytes long somehow. Not sure yet what is going on.