How to prevent blinking vertices (primitives) while using persistenly mapped buffer?

0

I am writing a 2d renderer. In a real world example one draw call can handle 600 000 vertices.

When buffer is bigger than 600 000, the renderer execute another draw call and so on.

Problems occur when there are 2 or more draw calls. There are 2 cases I tested:

synchronization per draw call - primitives are drawn, but are blinking (only when draw calls count > 1, when there is 1 draw call it works without any problems). synchronization once per all draw calls - only the last draw call renders primitive Example code tests a basic case, 4 vertices per draw call

This is how the renderer looks like:

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;
};
  • the first case - quads are blinking:
int main() 
{
    //Create window, gl context, init gl, etc. etc.

    Renderer renderer;

    while(!quit) {
        glClear(GL_COLOR_BUFFER_BIT);

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

        //Pretend we are out of max vertex count range, allocate another draw call with render present;
        renderer.FillRect(70.0f, 70.0f, 50.0f, 50.0f, 0xff0000ff);
        renderer.RenderPresent();

        //swap windows
    }
}
  • the second case - only second draw call renders a quad: we need to comment out Lock() and Unlock() in RenderPresent(); and add Lock() and Unlock() in main loop.
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();
}

//---------------

    while(!quit) {
        glClear(GL_COLOR_BUFFER_BIT);

        renderer.Lock();

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

        //Pretend we are out of max vertex count range, allocate another draw call with render present;
        renderer.FillRect(70.0f, 70.0f, 50.0f, 50.0f, 0xff0000ff);
        renderer.RenderPresent();

        renderer.Unlock();

        //swap windows
    }

Why those problems occur? How to fix it?

c++
opengl
persistent-storage
asked on Stack Overflow Dec 2, 2019 by BrodaJarek3 • edited Dec 2, 2019 by Shout

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0