I found out that I can use uniform buffers to store multiple variables like this
#version 330 core
layout (location = 0) in vec3 aPos;
layout (std140) uniform Matrices
{
mat4 projection;
mat4 view;
};
uniform mat4 model;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
and treat this data like any other buffer
unsigned int uboMatrices
glGenBuffers(1, &uboMatrices);
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 * sizeof(glm::mat4));
However, I can't seem to find any examples that would allow me to treat such a buffer like an array. Essentially I would like to achieve a global random-access buffer like this
#version 330 core
layout (location = 0) in vec3 aPos;
layout (what here?) uniform float[] globalBuffer;
uniform mat4 model;
void main()
{
...
}
The long term plan is to later produce this array with OpenCL
uniform float[] globalBuffer; is not a uniform buffer. It is just an array of uniforms. You must set the uniforms with glUniform1fv.
A Uniform block would be:
layout (std140) uniform Foo
{
float[] globalBuffer;
}
Unfortunately, in this case each array element would be aligned to the size of vec4. See OpenGL 4.6 API Core Profile Specification; 7.6.2.2 Standard Uniform Block Layout:
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.
I recommend to use a Shader Storage Buffer Object
layout (std430) buffer Foo
{
float[] globalBuffer;
};
OpenGL 4.6 API Core Profile Specification; 7.6.2.2 Standard Uniform Block Layout:
Shader storage blocks (see section 7.8) also support the std140 layout qualifier, as well as a std430 qualifier not supported for uniform blocks. When using the std430 storage layout, shader storage blocks will be laid out in buffer storage identically to uniform and shader storage blocks using the std140 layout, except that the base alignment and stride of arrays of scalars and vectors in rule 4 and of structures in rule 9 are not rounded up a multiple of the base alignment of a vec4.
Related
I'm trying to bind a storage buffer of object structs containing multiple variables of different types so that I can access them in my vertex shader.
Struct:
struct Object
{
vec2 vertices[4];
};
layout(std140, set = 0, binding = 0) buffer ObjectBlock
{
Object objects[];
};
Using the code above, although I am able to compile the shader and get the Vulkan program running, the values from the array variable vertices are completely different from the values I inputted. I'm not sure why this is happening.
I was trying to make a simple texture (fragment) shader that would loop through the uniform array atextures[] which is implicitly defined. The following code returns the following error
Code:
#version 330 core
out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D atextures[];
uniform int textureLength;
void main()
{
for (int i=0; i<textureLength; i++){
FragColor = texture(atextures[i], TexCoord);
}
}
Error:
Indirect index into implicitly-sized array
However when I change the index from i to 0, the following compiles fine. Did I setup the for loop wrong? Or did I initialise the array correctly?
You are getting the error because you have code that violates the specification. See the (most recent) OpenGL Shading Language 4.60 Specification - 4.1.9. Arrays:
It is legal to declare an array without a size (unsized) and then later redeclare the same name as an array of the same type and specify a size, or index it only with constant integral expressions (implicitly sized).
What you try to do is not the create an implicitly-sized array, but an dynamically-sized array.
It is not possible to create uniform array with a variable size. A variable size is just possible for the bottommost variable in a Shader Storage Block.
Anyway you should prefer to use an sampler2DArray instead of an array of sampler2D. With a sampler array, you must use a separate texture unit for each element, and the array index must be a Dynamically uniform expression
The error I get with the following declaration of a buffer is:
Binding point for uniform block must be equal or greater than 0 and
less than: GL_MAX_UNIFORM_BUFFER_BINDINGS.
My GL_MAX_UNIFORM_BUFFER_BINDINGS is 90. I want 128 of the following structs:
layout(binding = 3, std140) uniform lightsBuffer
{
mat4 viewMatrix;
vec3 colour;
float padding;
}lights[128];
It seems I've created 128 of these structs each at different binding points. How do I have the array of 128 structs at binding 2?
Edit: Is it like this:
layout(binding = 3, std140) uniform lightsBuffer
{
struct Light
{
mat4 viewMatrix;
vec3 colour;
float padding;
}lights[128];
};
That's not a struct; it's a uniform block. You can tell because you didn't use the keyword struct ;) So what you're creating is an array of uniform blocks.
If you want an array within a uniform block, you can create that using standard syntax:
layout(...) uniform lightsBuffer
{
StructName lights[128];
};
Where StructName is a previously-defined struct.
Can I define the Light struct inside
No. Why would you want to?
I was asking myself a question about UBO and the way of accessing them in GLSL with uniform blocks.
Following the official documentation, if I want to design an array of lights, I will probably write :
layout(std140, binding = 0) uniform LightBlock
{
vec4 position;
vec4 direction;
vec4 color;
...
} lights[8];
Now I see a lot of examples, where the uniform block is written that way :
struct LightStruct
{
vec4 position;
vec4 direction;
vec4 color;
...
};
layout(std140, binding = 0) uniform LightBlock
{
LightStruct lights[8];
};
What is the difference between the two ways ?
I guess it could help to reduce the number of uniform variables in use within a shader, but I'm not sure.
The first
layout(std140, binding = 0) uniform LightBlock
{
vec4 position;
vec4 direction;
vec4 color;
...
} lights[8];
declares an array of UBO buffer blocks itself. That means, that you can bind a different buffer object for each index in your array, or a different buffer range. Note that in this example, you will consume the UBO binding indices from 0 to 7, the GLSL spec explicitly states:
If the binding identifier is used with a uniform or shader storage
block instanced as an array, the first element of the array takes the
specified block binding and each subsequent element takes the next
consecutive uniform block binding point.
This has a couple of implications:
you can only use a very limited array size, because the number of UBO binding point is limited
you must index those arrays only with a dynamically uniform expression
you can bind the same UBO and buffer range to some or all of the indivudal indices of your array (something you could not do with an array inside the block)
In summary, you seldom really want to use an array of uniform blocks. Especially for your light example, you would use the latter:
layout(std140, binding = 0) uniform LightBlock
{
LightStruct lights[8];
};
just declares one uniform block with an array in it. It means you have to provide one consecutive UBO buffer range for the array, so you consume only one of the precious UBO binding points, and you can have the array as big as the maximum UBO size of your implementation is.
What are the diffences between "Shader Storage Buffer Objects" (SSBO) and Image load store operations
When should one be used and not the other?
They both can have atomic operations and I assume they are stored in the same type of memory. And regardless if they are stored in the same type of memory, do they have the same performance characteristics?
edit: the original question was asking between SSBOs and Uniform buffer objects, it was meant to be between SSBO and Image load store.
The difference between shader storage buffer objects and image textures and why one would want to use them is that they can use interface blocks.
Images are just textures which mean only vec4's are in the data structure. Well not only vec4, it could have other formats, but the data structure would be many of one data type.
Where as, SSBO's are generic. They can use combinations of int's, float's, arrays of vec3's all in a single interface block.
So, SSBO's are much more flexible than just Image Texture's.
Your question is already answered more or less definitively at http://www.opengl.org/wiki/Shader_Storage_Buffer_Object. It says:
SSBOs are a lot like Uniform Buffer Objects. Shader storage blocks are
defined by Interface Block (GLSL)s in almost the same way as uniform
blocks. Buffer objects that store SSBOs are bound to SSBO binding
points, just as buffer objects for uniforms are bound to UBO binding
points. And so forth.
The major differences between them are:
SSBOs can be much larger. The smallest required UBO size is 16KB; the smallest required SSBO size is 16MB, and typical sizes will
be on the order of the size of GPU memory.
SSBOs are writable, even atomically; UBOs are uniform​s. SSBOs reads and writes use incoherent memory accesses, so they need the
appropriate barriers, just as Image Load Store operations.
SSBOs can have unbounded storage, up to the buffer range bound; UBOs must have a specific, fixed storage size. This means that you can
have an array of arbitrary length in an SSBO. The actual size of the
array, based on the range of the buffer bound, can be queried at
runtime in the shader using the length​ function on the unbounded
array variable.
As others have mentioned, SSBOs have much larger storage and supports atomic operations, the accepted answer also mentioned that SSBOs are generic in the sense that they allow users to combine different types. But personally, I just want to point out that I think this is usually BAD, it is not always ideal to use interface blocks or structs in SSBO. Here's an example:
Let's say you have a struct in C++ like this:
struct Foo {
glm::vec4 position;
glm::vec4 velocity;
glm::vec4 padding_and_range; // range is a float padded to a vec4
};
which corresponds to an SSBO buffer in glsl:
struct Foo {
vec4 position;
vec4 velocity;
vec4 padding_and_range; // range is a float padded to a vec4
};
layout(std430, binding = 0) readonly buffer SSBO {
Foo data[];
} foo;
Although the SSBO buffer is able to hold an array of struct Foo, notice that paddings must be taken into account as per the std430 memory layout, you have to padded your float range to a vec4, and then use foo.data[i].padding_and_range.w to access it. This is error-prone, let alone the waste of memory spaces, especially when your SSBO is large (to be used in a compute shader) and your Foo struct is complex (needs a lot of paddings). Apart from that, you often need to fill in the buffer data in a loop like this:
Foo* foos = reinterpret_cast<Foo*>(glMapNamedBufferRange(ssbo, offset, size, GL_MAP_READ_BIT));
for (int i = 0; i < n_foos; i++) {
Foo& foo = foos[i];
foo.position = glm::vec4(1.0f);
foo.velocity = glm::vec4(2.0f);
foo.padding_and_range = glm::vec4(glm::vec3(0.0f), 3.5f);
}
glUnmapNamedBuffer(ssbo);
instead of simply writing data to it in one go using glNamedBufferData or glNamedBufferSubData.
A better way of handling struct is to store each struct element into a separate SSBO, so that each SSBO buffer array is tightly packed and homogeneous. Even though the performance may not be any better, it helps keep your code clean and more readable. Rarher than using the struct, you would want to use:
layout(std430, binding = 0) buffer FooPosition {
vec4 position[];
};
layout(std430, binding = 1) buffer FooVelocity {
vec4 velocity[];
};
layout(std430, binding = 2) buffer FooRange {
float range[];
};