Setting OpenGL uniform value from shader storage buffer - opengl

In OpenGL, I have one compute shader which writes output values into a shader storage buffer on the device.
Then another shader (fragment shader) reads that value and uses it.
So this happens all on the device, without synchronizing with the host.
Is there way to instead have the fragment shader receive the values as a uniform, except that the content of the uniform is not set by the host with glUniform(), but it takes the value that is on the device-side shader storage buffer? In a way similar to how glDrawIndirect() can take parameters from a device-side buffer, instead of from the host, avoiding pipeline stalling.
This would allow simplifying a program where the fragment shader will receive the value either as a constant set by the host, or dynamically from a previous shader, depending on configuration.

Uniforms can be aggregated into an interface block:
layout(binding = 0) uniform InBlock {
// ... your uniforms go here ...
} IN;
Then the compute-shader written buffer can be bound to that interface block binding point:
glBindBuffersBase(GL_UNIFORM_BUFFER, 0, buffer_id);
In fact this is the preferred way of doing things in general, rather than setting each uniform one-by-one.

Related

Does GL_SHADER_STORAGE_BUFFER locations collide with other shaders locations?

I have multiple glsl files that use shader storage bufffer. If I bind buffer bases with other shader files, but they have same locations in storage buffer, they seem to affect each other. Does this mean that I have to unbind it somehow? When I chose other locations for each files, they didn't seem to have impact to the code.
for example
first.vs
layout(std430, binding = 0) buffer texture_coordinate_layout
{
vec2 texture_coordinates[];
};
second.vs
layout(std430, binding = 0) buffer vertices_layout
{
vec2 vertices[];
};
when having two different shader programs, when I bind with each like so
first shader program
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_vertex_ssbo);
second shader program
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_texture_coordiante_ssbo);
Buffer bindings are part of context state, not the shader program. Index 0 in the context is index 0; it's not associated with any program directly.
The program only specifies which indexed binding point is used for that particular variable when the program gets used for rendering purposes. If you need to use a particular buffer object for a particular variable in a program, then before rendering, you need to ensure that the particular buffer is bound to the context at the index which the program will read. Always.

Opengl 3/4 : Can I bind the same buffer object to different targets

In my specific case, I'm trying to bind a vertex buffer object into a uniform buffer object.
For more details, in my opaque object rendering pipeline in deferred shading, I create a G buffer then render light volumes one point light at a time using a light vbo.
I then need all these lights as a ubo available for iteration in forward rendering for translucent objects.
Texture objects are directly and forever associated with the target type with which they are first used. This is not the case for buffer objects.
There is no such thing as a "vertex buffer object" or a "uniform buffer object" (ignore the name of the corresponding extensions). There are only "buffer objects", which can be used for various OpenGL operations, like providing arrays of vertex data, or the storage for uniform blocks, or any number of other things. It is 100% fine to use a buffer as a source for vertex data, then use the same buffer (and same portion of that buffer) as a source for uniform data.

Memory data flow for uniform variables?

When a texture (2D) is supplied to a shader as a 'uniform' input, it is first uploaded to OpenGL using glTexImage2D() and then using glUniform1i() it is associated to shader uniform.
eg code :
Texture data
glTexImage2D(): is used to transfer texture data to the server side.
glGetUniformLocation(): is used to access shader uniform handle.
glUniform1i(): associates the data pointed by texture unit to the shader 'uniform' input.
but when we pass matrix (eg matrix4x4) to a shader as a 'uniform' input, when don't use any specific function to upload it to OpenGL. We just used glUniform..() to associate the data with the shader input which we also used in the case of associating texture data that is already present in OpenGL memory.
Matrix data
glGetUniformLocation(): to access shader uniform handle
glUniformMatrix4fv(): to associate matrix data to the shader uniform input.
Where does the matrix data live in each step in the process of passing it to a shader as a uniform input?
Does matrix data always live on client side/ CPU accessible memory and fetched every frame by server side?
If it is uploaded to OpenGL:
which step/function call uploads the data?
where does the data live in OpenGL memory?
how its memory location is pointed?
associates the data pointed by texture unit to the shader 'uniform' input.
No, this sets the "value" of the uniform variable (uniforms are not "inputs") to be the texture unit index where the sampler can find the texture that this uniform represents. The value is just a value; it is no different from the way you set the value of any uniform variable.
It's just that in GLSL, you don't access the "value" of the sampler uniform variable; you use texture accessing functions on it instead.
If you change the texture bound to that texture unit before you render with the shader, then the shader will see the new texture. It is not directly associated with any texture; the shader is only associated with a texture unit index.
Where does the matrix data live in each step in the process of passing it to a shader as a uniform input?
There's only one step in the "process" of passing uniform values to shaders. You make a glUniform* call, and the "process" is complete; the particular uniform now has that particular value.
This is no different from setting pretty much any state in OpenGL. If you make a glTexParameter call, that performs the "process" of setting the state value you pass into the proper place in the OpenGL texture object.
Does matrix data always live on client side/ CPU accessible memory and fetched every frame by server side?
It lives wherever the implementation chooses to put it. However, all OpenGL functions are finished with the pointers you pass them by the time they return. So you don't have to worry about your CPU memory.

Bind an SSBO to a fragment shader

I have a an SSBO which stores vec4 colour values for each pixel on screen and is pre populated with values by a compute shader before the main loop.
I'm now trying to get this data onscreen which I guess involves using the fragment shader (Although if you know a better method for this I'm open to suggestions)
So I'm trying to get the buffer or at least the data in it to the fragment shader so that I can set the colour of each fragment to the corresponding value in the buffer but I cannot find any way of doing this?
I have been told that I can bind the SSBO to the fragment shader but I don't know how to do this? Other thoughts I had was somehow moving the data from the SSBO to a texture but I can't work that out either
UPDATE:
In response thokra's excellent answer and following comments here is the code to set up my buffer:
//Create the buffer
GLuint pixelBufferID;
glGenBuffers(1, &pixelBufferID);
//Bind it
glBindBuffer(GL_SHADER_STORAGE_BUFFER, pixelBufferID);
//Set the data of the buffer
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec4) * window.getNumberOfPixels, new vec4[window.getNumberOfPixels], GL_DYNAMIC_DRAW);
//Bind the buffer to the correct interface block number
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, pixelBufferID);
Then I call the compute shader and this part works, I check the data has been populated correctly. Then in my fragment shader, just as a test:
layout(std430, binding=0) buffer PixelBuffer
{
vec4 data[];
} pixelBuffer
void main()
{
gl_FragColor = pixelBuffer.data[660000];
}
What I've noticed is that it seems to take longer and longer the higher the index so at 660000 it doesn't actually crash its just taking an silly amount of time.
Storage buffers work quite similarly to uniform buffers. To get a sense of how those work I suggest something like this. The main differences are that storage buffer can hold substantially higher amounts of data and the you can randomly read from and write to them.
There are multiple angles of working this, but I'll start with the most basic one - the interface block inside your shader. I will only describe a subset of the possibilities when using interface blocks but it should be enough to get you started.
In contrast to "normal" variables, you cannot specify buffer variables in the global scope. You need to use an interface block (Section 4.3.9 - GLSL 4.40 Spec) as per Section 4.3.7 - GLSL 4.40 Spec:
The buffer qualifier can be used to declare interface blocks (section 4.3.9 “Interface Blocks”), which are then referred to as shader storage blocks. It is a compile-time error to declare buffer variables at global scope (outside a block).
Note that the above mentioned section differs slightly from the ARB extension.
So, to get access to stuff in your storage buffer you'll need to define a buffer interface block inside your fragment shader (or any other applicable stage):
layout (binding = 0) buffer BlockName
{
float values[]; // just as an example
};
Like with any other block without an instance name, you'll refer to the buffer storage as if values were at global scope, e.g.:
void main()
{
// ...
values[0] = 1.f;
// ...
}
On the application level the only thing you now need to know is that the buffer interface block BlockName has the binding 0 after the program has been successfully linked.
After creating a storage buffer object with your application, you first bind the buffer to the binding you specified for the corresponding interface block using
glBindBufferBase(GLenum target​, GLuint index​, GLuint buffer​);
for binding the complete buffer to the index or
glBindBufferRange(GLenum target​, GLuint index​, GLuint buffer​, GLintptr offset​, GLsizeiptr size​);
for binding a subset specified by an offset and a number of of the buffer to the index.
Note that index refers to the binding specified in your layout for the corresponding interface block.
And that's basically it. Be aware that there are certain limits for the storage buffer size, the number of binding points, maximum storage block sizes and so on. I refer you to the corresponding sections in the GL and GLSL specs.
Also, there is a minimal example in the ARB extension. Reading the issues sections of extension also often provides further insight into the exposed functionality and the rationale behind it. I advise you to read through it.
Leave a comment if you run into problems.

What's glUniformBlockBinding used for?

Assuming I have a shader program with a UniformBlock at index 0.
Binding the UniformBuffer the following is apparently enough to bind a UniformBuffer to the block:
glUseProgram(program);
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
I only have to use glUniformBlockBinding when I bind the buffer to a different index than used in the shader program.
//...
glBindBufferBase(GL_UNIFORM_BUFFER, 1, buffer)
glUniformBlockBinding(program, 0, 1); // bind uniform block 1 to index 0
Did I understand it right? Would I only have to use glUniformBlockBinding if I use use the buffer in different programs where the appropriate blocks have different indices?
Per-program active uniform block indices differ from global binding locations.
The general idea here is that assuming you use the proper layout, you can bind a uniform buffer to one location in GL and use it in multiple GLSL programs. But the mapping between each program's individual buffer block indices and GL's global binding points needs to be established by this command.
To put this in perspective, consider sampler uniforms.
Samplers have a uniform location the same as any other uniform, but that location actually says nothing about the texture image unit the sampler uses. You still bind your textures to GL_TEXTURE7 for instance instead of the location of the sampler uniform.
The only conceptual difference between samplers and uniform buffers in this respect is that you do not assign the binding location using glUniform1i (...) to set the index. There is a special command that does this for uniform buffers.
Beginning with GLSL 4.20 (and applied retroactively by GL_ARB_shading_language_420pack), you can also establish a uniform block's binding location explicitly from within the shader.
GLSL 4.20 (or the appropriate extension) allows you to write the following:
layout (std140, binding = 0) uniform MyUniformBlock
{
vec4 foo;
vec4 bar;
};
Done this way, you never have to determine the uniform block index for MyUniformBlock; this block will be bound to 0 at link-time.