W jaki sposób zapobiec miganiu wierzchołków (geometrii) podczas używania trwałego mapowania bufora?

0

Piszę system renderowania grafiki 2D. Jedno polecenie rysowania może narysować 600 000 wierzchołków. Kiedy ta liczba zostanie przekroczona, następne wierzchołki są rysowane przed drugie polecenie rysowania (które właśnie dodaliśmy).

Działa to sprawnie z glBufferSubData.

Chciałbym jednak użyć do tego bufora, który jest trwale zmapowany.
Aktualnie mam 2 przypadki, które mają swoje problemy:

  • synchronizacja na każde polecenie rysowania - geometria się renderuje, ale mruga (tylko gdy jest więcej niż 1 polecenie rysowania. Jeśli jest tylko 1 polecenie, wszystko działa jak natura chciała),
  • synchronizacja raz na wszystkie polecenie rysowania - tylko geometria z ostatniego polecenia rysowania jest renderowana.

Minimalny przykład pokazuje bardzo prosty przypadek, 4 wierzchołki na 1 polecenie rysowania

Wygląda to tak:

struct Vertex2D
{
    Vertex2D() = default;
    Vertex2D(float x, float y, uint32 color)
    {
        position = { x, y };
        this->color = color;
    }

    Vector2<float> position;
    uint32 color;
};

struct Range
{
    uint begin = 0;

    GLsync sync = 0;
};

struct Renderer
{
    Renderer() = default;
    ~Renderer()
    {
        glUnmapNamedBuffer(vbo);
        glDeleteBuffers(1, &vbo);

        glUnmapNamedBuffer(ebo);
        glDeleteBuffers(1, &ebo);

        glDeleteVertexArrays(1, &vao);
    }

    void Create()
    {
        shader.Create("shader.glsl");

        vertices.resize(MaxVertexCount);
        elements.resize(MaxElementCount);

        auto mapBit = GL_MAP_WRITE_BIT | GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT;

        glCreateVertexArrays(1, &vao);
        glBindVertexArray(vao);

        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glNamedBufferStorage(vbo, BufferCount * MaxVertexCount * sizeof(Vertex2D), nullptr, mapBit);

        mappedVertex = (Vertex2D*)glMapNamedBufferRange(vbo, NULL, BufferCount * MaxVertexCount * sizeof(Vertex2D), mapBit);

        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);

        glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(Vertex2D), (const void*)offsetof(Vertex2D, position));
        glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex2D), (const void*)offsetof(Vertex2D, color));

        glGenBuffers(1, &ebo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        glNamedBufferStorage(ebo, BufferCount * MaxElementCount * sizeof(uint32), nullptr, mapBit);

        mappedElements = (uint32*)glMapNamedBufferRange(ebo, NULL, BufferCount * MaxElementCount * sizeof(uint32), mapBit);

        vertexRanges[0].begin = 0;
        vertexRanges[1].begin = MaxVertexCount;
        vertexRanges[2].begin = MaxVertexCount * 2;

        elementsRanges[0].begin = 0;
        elementsRanges[1].begin = MaxElementCount;
        elementsRanges[2].begin = MaxElementCount * 2;
    }

    void LockBuffer(GLsync& syncObject)
    {
        if (syncObject) {
            glDeleteSync(syncObject);
        }

        syncObject = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
    }

    void WaitBuffer(GLsync& syncObject)
    {
        if (syncObject) {
            while (true) {
                auto waitReturn = glClientWaitSync(syncObject, GL_SYNC_FLUSH_COMMANDS_BIT, 1);
                if (waitReturn == GL_ALREADY_SIGNALED || waitReturn == GL_CONDITION_SATISFIED)
                    return;
            }
        }
    }

    void FillRect(float x, float y, float w, float h, uint32 color)
    {
        vertices[0] = Vertex2D(x, y, color);
        vertices[1] = Vertex2D(x, y + h, color);
        vertices[2] = Vertex2D(x + w, y + h, color);
        vertices[3] = Vertex2D(x + w, y, color);

        elements[0] = 0;
        elements[1] = 1;
        elements[2] = 2;
        elements[3] = 2;
        elements[4] = 3;
        elements[5] = 0;
    }

    void Lock()
    {
        WaitBuffer(vertexRanges[rangeIndex].sync);
        WaitBuffer(elementsRanges[rangeIndex].sync);
    }

    void RenderPresent()
    {
        Lock();

        memcpy(&mappedVertex[vertexRanges[rangeIndex].begin], vertices.data(),  (sizeof(Vertex2D) * vertices.size()));
        memcpy(&mappedElements[elementsRanges[rangeIndex].begin], elements.data(),  (sizeof(MiEuint32) * elements.size()));

        glDrawElements(GL_TRIANGLES, elements.size(), GL_UNSIGNED_INT, nullptr);

        Unlock();
    }

    void Unlock()
    {
        LockBuffer(vertexRanges[rangeIndex].sync);
        LockBuffer(elementsRanges[rangeIndex].sync);

        rangeIndex = (rangeIndex + 1) % BufferCount;
    }

    enum
    {
        BufferCount = 3,
        MaxVertexCount = 4,
        MaxElementCount = (MaxVertexCount - 2) * 3
    };

    int rangeIndex = 0;
    Range vertexRanges[BufferCount];
    Range elementsRanges[BufferCount];

    Shader shader;

    GLuint vao;
    GLuint vbo;
    GLuint ebo;

    Vertex2D* mappedVertex = nullptr;
    uint32* mappedElements = nullptr;

    std::vector<Vertex2D> vertices;
    std::vector<uint32> elements;
};

Rozkładając to na podane wyżej przypadki:

  • synchronizacja osobno dla każdego polecenia rysowania - prostokąty migają
int main() 
{
    //stwórz window, gl context, init gl, itd. itp.

    Renderer renderer;

    while(!quit) {
        glClear(GL_COLOR_BUFFER_BIT);

        renderer.FillRect(10.0f, 70.0f, 50.0f, 50.0f, 0xffffffff);
        renderer.RenderPresent();

        //Udajemy, że nie ma miejsca już w buforze wierzchołków i wywołujemy drugie polecenie rysowania
        renderer.FillRect(70.0f, 70.0f, 50.0f, 50.0f, 0xff0000ff);
        renderer.RenderPresent();

        //swap windows
    }
}
  • drugi przypadek - sychronizacja raz dla wszystkich poleceń rysowania - tylko ostatnie polecenie rysowania renderuje prostokąty
    Musimy skomentować Lock() i Unlock() w metodzie RenderPresent()
void RenderPresent()
{
    //Lock();

    memcpy(&mappedVertex[vertexRanges[rangeIndex].begin], vertices.data(),  (sizeof(Vertex2D) * vertices.size()));
    memcpy(&mappedElements[elementsRanges[rangeIndex].begin], elements.data(),  (sizeof(MiEuint32) * elements.size()));

    glDrawElements(GL_TRIANGLES, elements.size(), GL_UNSIGNED_INT, nullptr);

    //Unlock();
}

I do maina dodać Renderer::Lock i Renderer::Unlock

while(!quit) {
        glClear(GL_COLOR_BUFFER_BIT);

        renderer.Lock();

        renderer.FillRect(10.0f, 70.0f, 50.0f, 50.0f, 0xffffffff);
        renderer.RenderPresent();

        //Udajemy, że nie ma miejsca już w buforze wierzchołków i wywołujemy drugie polecenie rysowania
        renderer.FillRect(70.0f, 70.0f, 50.0f, 50.0f, 0xff0000ff);
        renderer.RenderPresent();

        renderer.Unlock();

        //swap windows
    }

Dlaczego takie problemy się pojawiają? Jak je naprawić?

0

Z góry mówię, że nie analizowałem jakoś bardzo dokładnie podanego kodu. Mam pytanie - czy stosujesz 2 bufory - jeden do rysowania, drugi do pokazywania? Jeśli tak - kiedy przełączasz się między tymi buforami? Czy synchronizujesz się z pionową synchronizacją obrazu? Chodzi mi o tzw. VSYNC.

0

VSYNC.

jest wyłączony

czy stosujesz 2 bufory

stosuje 3

kiedy przełączasz się między tymi buforami?

Metoda Unlock()

 rangeIndex = (rangeIndex + 1) % BufferCount;
0
brodajarek napisał(a):

VSYNC.

jest wyłączony

W sensie - "jest wyłączony i nie mam na to żadnego wpływu"? Czy może jest jakaś możliwość podpięcia własnego handlera pod VSYNC-a?

czy stosujesz 2 bufory

stosuje 3

kiedy przełączasz się między tymi buforami?

Metoda Unlock()

 rangeIndex = (rangeIndex + 1) % BufferCount;

Proponowałbym ustawiać jakąś flagę w momencie kiedy zakończone jest generowanie pojedynczej ramki obrazu i sprawdzać tę flagę w handlerze VSYNC-a. Jeśli flaga ustawiona - wywołujesz Unlock() i kasujesz flagę.

0

W sensie - "jest wyłączony i nie mam na to żadnego wpływu"? Czy może jest jakaś możliwość podpięcia własnego handlera pod VSYNC-a?

W sensie jest wyłączony, w teorii mogę włączyć, bo SDL na to pozwala, w praktyce, u mnie ustawienie vsynca od sdla nic nie zmienia, a z moich widzimisię chciałbym żeby nie było żadnego ograniczenia klatkowego.

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