user11548627
user11548627

Reputation:

How to establish glBindBufferRange() offset with Shader Storage Buffer and std430?

I want to switch between ssbo data to draw things with different setup. To make it happen I need to use glBindBufferRange() with its suitable offset. I've read that the offset needs to be a multiple of GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT for ubo, but things may be changed with ssbo since using std430 instead of std140.

I tried to do this the easiest way

struct Color
{
    float r, g, b, a;
};
struct V2
{
   float x, y;
};
struct Uniform
{
    Color c1;
    Color c2;
    V2 v2;
    float r;
    float f;
    int t;
};

GLuint ssbo = 0;
std::vector<Uniform> uniform;

int main()
{
    //create window, context etc.

    glCreateBuffers(1, &ssbo);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);

    Uniform u;
    u.c1 = {255, 0, 255, 255 };
    u.c2 = {255, 0, 255, 255 };
    u.v2 = { 0.0f, 0.0f };
    u.r = 0.0f;
    u.f = 100.0f;
    u.t = 0;
    uniform.push_back(u);

    u.c1 = {255, 255, 0, 255 };
    u.c2 = {255, 255, 0, 255 };
    u.v2 = { 0.0f, 0.0f };
    u.r = 100.0f;
    u.f = 100.0f;
    u.t = 1;
    uniform.push_back(u);

    u.c1 = {255, 0, 0, 255 };
    u.c2 = {255, 0, 0, 255 };
    u.v2 = { 0.0f, 0.0f };
    u.r = 100.0f;
    u.f = 0.0f;
    u.t = 0;
    uniform.push_back(u);

    glNamedBufferData(ssbo, sizeof(Uniform) * uniform.size(), uniform.data(), GL_STREAM_DRAW);

    for(int i = 0; i < uniform.size(); ++i) {
        glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, ssbo, sizeof(Uniform) * i, sizeof(Uniform));

        glDrawArrays(...);
    }

    //swap buffer etc.

    return 0;
}
#version 460 core

layout(location = 0) out vec4 f_color;

layout(std430, binding = 1) buffer Unif
{
    vec4 c1; 
    vec4 c2; 
    vec2 v2;   
    float r;  
    float f; 
    int t;      
};

void main()
{       
    f_color = vec4(t, 0, 0, 1);
}

There is of course vao, vbo, vertex struct and so on, but they are not affect ssbo.

I got GL_INVALID_VALUE glBindBufferRange() error, though. And that must come from offset, because my next attempt transfers data, but with wrong order.

My next attept was to use GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT and a formula I found on the Internet

    int align = 4;
    glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &align);
    int ssboSize = sizeof(Uniform) + align - sizeof(Uniform) % align;

so just changing glNamedBufferData and glBindBufferRange it looks like this

 glNamedBufferData(ssbo, ssboSize * uniform.size(), uniform.data(), GL_STREAM_DRAW);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, ssbo, ssboSize * i, sizeof(Uniform));

and that way, it almost worked. As you can see, ts are

0;
1;
0;

so opengl should draw 3 shapes with colors -

vec4(0, 0, 0, 1); 
vec4(1, 0, 0, 1); 
vec4(0, 0, 0, 1); 

it draws them wrong order

vec4(1, 0, 0, 1); 
vec4(0, 0, 0, 1); 
vec4(0, 0, 0, 1);

How can I make it transfer data proper way?

Upvotes: 2

Views: 1141

Answers (2)

user11548627
user11548627

Reputation:

I ended up using struct alignas(128) Uniform. I guess my next goal is to not use hardcoded align.

Upvotes: 1

derhass
derhass

Reputation: 45332

The OpenGL spec (Version 4.6) states the following in section "6.1.1 Binding Buffer Objects to Indexed Target Points" regararding the error conditions for glBindBufferRange:

  • An INVALID_VALUE error is generated by BindBufferRange if buffer is non-zero and offset or size do not respectively satisfy the constraints described for those parameters for the specified target, as described in section 6.7.1.

Section 6.7.1 "Indexed Buffer Object Limits and Binding Queries" states for SSBOs:

  • starting offset: SHADER_STORAGE_BUFFER_START
  • offset restriction: multiple of value of SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT
  • binding size SHADER_STORAGE_BUFFER_SIZE

According to Table 23.64 "Implementation Dependent Aggregate Shader Limits":

256 [with the following footnote]: The value of SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT is the maximum allowed, not the minimum.

So if your offset is not a multiple of 256 (which it isn't), this code is simply not guaranteed to work at all. You can query for the actual restriction by the implementation you are running on and ajust your buffer contents accordingly, but you must be prepared that it is as high as 256 bytes.

Upvotes: 3

Related Questions