Understanding UBO limitations in GLSL - opengl

I'm about to implement some functionality which will use uniform buffer objects, and I'm trying to understand the limitations of UBOs before doing so.
For example let's use these GL_MAX_* values and this simple fragment shader:
- GL_MAX_UNIFORM_BUFFER_BINDINGS -> 84
- GL_MAX_UNIFORM_BLOCK_SIZE -> 16384
- GL_MAX_VERTEX_UNIFORM_BLOCKS -> 14
#version 330 core
layout (location = 0) in vec3 aPos;
// UBO
layout (std140, binding = 0) uniform Matrices
{
mat4 projection;
mat4 view;
};
// Individual uniform variables
uniform mat4 model;
uniform vec3 camPos;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
Questions:
Do individual uniform variables each consume one of the GL_MAX_VERTEX_UNIFORM_BLOCKS, or is there a default uniform block where these variables are stored (I'm guessing the later)?
Is the GL_MAX_UNIFORM_BLOCK_SIZE the limit for all defined uniform blocks, or does each defined UBO have a max size of this parameter making in this example the max amount of uniform data allowed to be passed to the shader program 229,376 bytes (spread across multiple ubos)?
If my assumption in question 1 is correct where individually defined uniform variables are contained in a default uniform buffer object:
A.) does this default buffer also adhere to the 16384 byte limit, meaning that the combined size of all individually defined uniform variables must not exceed 16384 bytes?
B.) does this default buffer consume a uniform block, leaving max available (before defining any other ubos) 13?
Do individually defined uniform variables count toward the GL_MAX_UNIFORM_BUFFER_BINDINGS parameter, leaving 81 available binding locations in this example?

Uniforms not declared in a block do not count against any uniform block limits. Nor do uniform block limits apply to them; non-block uniforms have their own, separate limitations.

Related

How to instance draw with different transformations for multiple objects

Im having a little problem with glDrawArraysInstanced().
Right now Im trying to draw a chess board with pieces.
I have all the models loaded in properly.
Ive tried drawing pawns only with instance drawing and it worked. I would send an array with transformation vec3s to shader through a uniform and move throught the array with gl_InstanceID
That would be done with this for loop (individual draw call for each model):
for (auto& i : this->models) {
i->draw(this->shaders[0], count);
}
which eventually leads to:
glDrawArraysInstanced(GL_TRIANGLES, 0, vertices.size(), count);
where the vertex shader is:
#version 460
layout(location = 0) in vec3 vertex_pos;
layout(location = 1) in vec2 vertex_texcoord;
layout(location = 2) in vec3 vertex_normal;
out vec3 vs_pos;
out vec2 vs_texcoord;
out vec3 vs_normal;
flat out int InstanceID;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 offsets[16];
void main(void){
vec3 offset = offsets[gl_InstanceID]; //saving transformation in the offset
InstanceID = gl_InstanceID; //unimportant
vs_pos = vec4(modelMatrix * vec4(vertex_pos + offset, 1.f)).xyz; //using the offset
vs_texcoord = vec2(vertex_texcoord.x,1.f-vertex_texcoord.y);
vs_normal = mat3(transpose(inverse(modelMatrix))) * vertex_normal;
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(vertex_pos + offset,1.f); //using the offset
}
Now my problem is that I dont know how to draw multiple objects in this way and change their transformations since gl_InstanceID starts from 0 on each draw call and thus my array with transformations would be used again from the beggining (which would just draw next pieces on pawns positions).
Any help will be appreciated.
You've got two problems. Or rather, you have one problem, but the natural solution will create a second problem for you.
The natural solution to your problem is to use one of the base-instance rendering functions, like glDrawElementsInstancedBaseInstance. These allow you to specify a starting instance for your instanced rendering calls.
This will precipitate a second problem: gl_InstanceID does not respect the base instance. It will always be on the range [0, instancecount). Only instance arrays respect the base instance. So instead of using a uniform to provide your per-instance data, you must use instance array rendering. This means storing the per-instance data in a buffer object (which you should have done anyway) and accessing it via a VS input whose VAO specifies that the particular attribute is instanced.
This also has the advantage of not restricting your instance count to uniform limitations.
OpenGL 4.6/ARB_shader_draw_parameters allows access to the gl_BaseInstance vertex shader input, which provides the baseinstance value specified by the draw command. So if you don't want to/can't use instanced arrays (for example, the amount of per-instance data is too big for the attribute limitations), you will have to rely on that extension/4.6 functionality. Recent desktop GL drivers offer this functionality, so if your hardware is decently new, you should be able to use it.

GLSL Uniform layout binding and textures

I'm a bit confused on what would be the right way to bind the texture when uniforms are using the layout binding.
layout(binding = 0, std140) uniform uCommon
{
mat4 projectionMatrix;
mat4 viewMatrix;
};
layout(binding = 1, std140) uniform uModel
{
mat4 modelViewProjectionMatrix;
};
layout(binding = 3) uniform sampler2D uTexture;
To bind my first texture I should use "GL_TEXTURE0 + 3"?
glActiveTexture(GL_TEXTURE0 + 3);
glBindTexture(GL_TEXTURE_2D, textureId);
Is this the correct way?
EDIT: Or is sampler using a separate binding from other uniforms? Can I use:
layout(binding = 0) uniform sampler2D uTexture;
while still using
layout(binding = 0, std140) uniform uCommon
Uniform block binding indices have nothing to do with sampler binding locations. These are different things.
The integer-constant-expression, which is used to specify the binding point or unit has not to be unique across all usages of the keyword binding.
See OpenGL Shading Language 4.60 Specification; 4.4.5 Uniform and Shader Storage Block Layout Qualifiers; page 77
The binding identifier specifies the uniform buffer binding point corresponding to the uniform or shader storage block, which will be used to obtain the values of the member variables of the block.
See OpenGL Shading Language 4.60 Specification; 4.4.6 Opaque-Uniform Layout Qualifiers; page 79
Image and sampler types both take the uniform layout qualifier identifier for binding:
layout-qualifier-id :
binding = integer-constant-expression
The identifier binding specifies which unit will be bound.

Do operations on uniform variables get cached in GLSL?

Let's say I have a very simple GLSL vertex shader:
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
in vec3 position;
in vec3 color;
out vec3 vertexColor;
void main()
{
mat4 mvp = projection * view * model;
vertexColor = color;
gl_Position = mvp * vec4(position, 1.0);
}
Is the variable mvp recalculated for every vertex, or is it precalculated and stored until the the uniform variables it depends on change?
Could a particular implementation cache that value? Sure. Will they?
I would not assume that they would. Such caching would be difficult to implement, since it requires evaluating such expressions before draw calls, based on uniform state, then uploading those expressions to the code.
If you have some state that is computed from multiple uniform values, and you believe that such computation would be a performance problem, you are the one who should compute it.

Error loading compute shader

I'm implementing simple ray tracing with a compute shader.
But I'm stuck in linking the program object of the compute shader.
#version 440
struct triangle {
vec3 points[3];
};
struct sphere {
vec3 pos;
float r;
};
struct hitinfo {
vec2 lambda;
int idx;
};
layout(binding = 0, rgba32f) uniform image2D framebuffer;
// wrriten by compute shader
layout (local_size_x = 1, local_size_y = 1) in;
uniform triangle triangles[2500];
uniform sphere spheres[2500];
uniform int num_triangles;
uniform int num_spheres;
uniform vec3 eye;
uniform vec3 ray00;
uniform vec3 ray10;
uniform vec3 ray01;
uniform vec3 ray11;
Here is my compute shader code and I can get a "Out of resource" error.
I know the reason of this error is the size of triangles but I need that size.
Is there any way how a large number of triangles can be passed into the shader?
There is just a very limited amount of uniforms a shader can have. If you need more data then what fits in your uniforms, you can either use Uniform Buffer Objects or Shader Storage Buffer Objects to back up a uniform.
In this case, you will have to define a GLSL Interface Block and bind the buffer to that uniform. This means, that you only need one uniform in order to store a large number of similar elements.

Instancing using more values than uniforms can store

I am fairly new to OpenGL and trying to achieve instancing using uniform arrays. However the number of instances I am trying to invoke is larger than the MAX_UNIFORM_LOCATIONS limit:
QOpenGLShader::link: error: count of uniform locations > MAX_UNIFORM_LOCATIONS(262148 > 98304)error: Too many vertex shader default uniform block components
error: Too many vertex shader uniform components
What other ways are possible that will work with that large a number of objects? So far this is my shader code:
layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
out vec3 vert;
out vec3 vertNormal;
out vec3 color;
uniform mat4 projMatrix;
uniform mat4 camMatrix;
uniform mat4 worldMatrix;
uniform mat4 myMatrix;
uniform sampler2D sampler;
uniform vec3 positions[262144];
void main() {
vec3 t = vec3(positions[gl_InstanceID].x, positions[gl_InstanceID].y, positions[gl_InstanceID].z);
float val = 0;
mat4 wm = myMatrix * mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, t.z, 1) * worldMatrix;
color = vec3(0.4, 1.0, 0);
vert = vec3(wm * vertex);
vertNormal = mat3(transpose(inverse(wm))) * normal;
gl_Position = projMatrix * camMatrix * wm * vertex;
}
If it should matter, I am using QOpenGLExtraFunctions.
There are many alternatives for overcoming the limitations of uniform storage:
UBOs, for example; they usually have a larger storage capacity than non-block uniforms. Now in your case, that probably won't work, since storing 200,000 vec3svec4s will require more storage than most implementations allow UBOs to provide. What you need is unbounded storage.
Instanced Arrays
Instanced arrays use the instanced rendering mechanism to automatically fetch vertex attributes based on the instance index. This requires that your VAO setup work change a bit.
Your shader would look like this:
layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec3 position;
out vec3 vert;
out vec3 vertNormal;
out vec3 color;
uniform mat4 projMatrix;
uniform mat4 camMatrix;
uniform mat4 worldMatrix;
uniform mat4 myMatrix;
uniform sampler2D sampler;
void main() {
vec3 t = position;
/*etc*/
}
Here, the shader itself never uses gl_InstanceID. That happens automatically based on your VAO.
That setup code would have to include the following:
glBindBuffer(GL_ARRAY_BUFFER, buffer_containing_instance_data);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);
glVertexAttribDivisor(2, 1);
This code assumes that the instance data is at the start of the buffer and is 3 floats-per-value (tightly packed). Since you're using vertex attributes, you can use the usual vertex attribute compression techniques on them.
The last call, to glVertexAttribDivisor is what tells OpenGL that it will only move to the next value in the array once per instance, rather than based on the vertex's index.
Note that by using instanced arrays, you also gain the ability to use the baseInstance glDraw* calls. The baseInstance in OpenGL is only respected by instanced arrays; gl_InstanceID never is affected by it.
Buffer Textures
Buffer textures are linear, one-dimensional textures that get their data from a buffer object's storage.
Your shader logic would look like this:
layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
out vec3 vert;
out vec3 vertNormal;
out vec3 color;
uniform mat4 projMatrix;
uniform mat4 camMatrix;
uniform mat4 worldMatrix;
uniform mat4 myMatrix;
uniform sampler2D sampler;
uniform samplerBuffer positions;
void main() {
vec3 t = texelFetch(positions, gl_InstanceID).xyz;
/*etc*/
}
Buffer textures can only be accessed via the direct texel fetching functions like texelFetch.
Buffer textures in GL 4.x can use a few 3-channel formats, but earlier GL versions don't give you that option (not without an extension). So you may want to expand your data to a 4-channel value rather than 3 channel.
Another problem is that buffer textures do have a maximum size limitation. And the required minimum is only 64KB of size, so the instanced array method will probably be more reliable (since it has no size restriction). However, all non-Intel OpenGL implementations give a huge size for buffer textures.
SSBOs
Shader storage buffer objects are like UBOs, only you can both read and write to them. That latter tool isn't important for you. The main advantage here is that the minimum required OpenGL size for them is have a minimum required size of 16MB (and implementations generally return a size limit on the order of available video memory). So size limits aren't a problem.
Your shader code would look like this:
layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
out vec3 vert;
out vec3 vertNormal;
out vec3 color;
uniform mat4 projMatrix;
uniform mat4 camMatrix;
uniform mat4 worldMatrix;
uniform mat4 myMatrix;
uniform sampler2D sampler;
buffer PositionSSBO
{
vec4 positions[];
};
void main() {
vec3 t = positions[gl_InstanceID].xyz;
/*etc*/
}
Note that we explicitly use a vec4 here. That's because you should never use vec3 in a buffer-backed interface block (ie: UBO/SSBO).
In code, SSBOs work much like UBOs. You bind them for use with glBindBufferRange.