W jaki sposób dobrać odpowiedni offset do glBindBufferRange() dla Shader Storage Buffer i std430?

0

Chce przełączać się między danymi przekazanymi do ssbo, tak, aby odpowiednie polecenie rysowania miało odpowiednie uniformy. Wiem, że do tego potrzebuje użyć glBindBufferRange() z odpowiednim parametrem offset. Przeczytałem, że offset musi być wielokrotnością GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT dla ubo, ale dla ssbo sytuacja może wyglądać inaczej, bo std430 naprawia niektóre rzeczy, z którymi std140 miał problem.

Ten pseudokod nie będzie zawierał vao, vbo, danych wierzchołka ani shadera wierzchołków, bo nie mają te rzeczy znaczenia w tym problemie.

Pierwsze czego spróbowałem to najprostszy sposób:

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);

    bool quit = false;
    while(!quit) {
        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;
}

mamy 3 uniformy, więc i 3 polecenia rysowania. Dla mojej wygody przyjmijmy, że rysujemy kwadraty.

fragment shader

#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);
}

W tym przykładzie dostaje błąd GL_INVALID_VALUE glBindBufferRange(), który na pewno pochodzi z argumentu offset, ponieważ w następnych "implementacjach", ten błąd znika, ale za to pojawia się inny problem.

Przechodząc dalej, skoro to nie działa to pomyślałem, że może użyję GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT i formuły, którą znalazłem w internecie

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

pokażę tylko zmienioną wersje glNamedBufferData() i glBindBufferRange(), bo tylko to się zmienia. Więc:


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

I ten sposób prawie zadziałał.

Z tego w jaki sposób ustawiłem zmienną Uniform::type, czyli
uniform[0].type == 0
uniform[1].type == 1
uniform[2].type == 0

shader powinien narysować 3 kwadraty o w kolorach

pierwsze polecenie rysowania 
vec4(0, 0, 0, 1);

drugie polecenie rysowania
vec4(1, 0, 0, 1);

trzecie polecenie rysowania
vec4(0, 0, 0, 1);

jednak kolory są pozamieniane kolejnością i jest o tak:

pierwsze polecenie rysowania 
vec4(1, 0, 0, 1);

drugie polecenie rysowania
vec4(0, 0, 0, 1);

trzecie polecenie rysowania
vec4(0, 0, 0, 1);

Czy to jest na pewno problem z offsetem w makro glBindBufferRange(), czy może tu chodzi o coś innego?
Co powinienem zmienić żeby poprawie używać ssbo?

0
    u.c1 = {255, 0, 255, 255 };

A nie {1, 0, 1, 1}?

glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &align);

ile dostajesz w align a ile wynosi sizeof(Uniform)?

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

To że sobie wyliczyłeś "wyalignowany" rozmiar, nie powoduje że dane w uniform.data() są wyrównane do tego rozmiaru, więc prawdopodobnie trafiasz w offsety które wcale nie są początkiem struktury.

1 użytkowników online, w tym zalogowanych: 0, gości: 1