mercredi 27 janvier 2021

OpenGL 4.6, C++ drawing from different parts of glBufferStorage using glDrawArrays

Context: I am currently working on a 2D engine in Opengl 4.6 and C++. Part of the project is a large batch of sprites getting rendered in different batches, my current approach is using a combination of glBufferData and glBufferSubData to accomplish this. Recently been working on implementing something similar using persistent data mapping with glBufferStorage. However I have only been able to draw the first batch all consecutive batches don't get drawn and am a bit floored as to the reason why.

Code:

Renderer initialization

struct Renderer
    {
        Renderer(std::vector<GLuint> channel_sizes, GLuint indices_size, std::vector<GLuint> attribute_sizes)
            : current_material(nullptr), indices_size(indices_size), attribute_size(0), quad_count(0)
        {
            for (auto att : attribute_sizes)
                attribute_size += att;

            vertex_size = attribute_size * indices_size;

            auto max_sprites = 0;

            for (auto size : channel_sizes)
            {
                channels.push_back(max_sprites * vertex_size);
                max_sprites += size;
            }
            channels.push_back(max_sprites * vertex_size);

            this->channels = channels;
            this->curr_channel = this->channels.begin();
            this->next_channel = this->curr_channel + 1;

            glGenVertexArrays(1, &vao);
            glGenBuffers(1, &vbo);

            // Memory mapping flags
            GLbitfield fMap = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;

            // Buffer creation flags
            GLbitfield fCreate = fMap | GL_DYNAMIC_STORAGE_BIT;

            glBindVertexArray(vao);
            glBindBuffer(GL_ARRAY_BUFFER, vbo);

            // Initialize and allocate buffer object
            glBufferStorage(GL_ARRAY_BUFFER, (GLsizeiptr)(max_sprites * vertex_size), nullptr, fCreate);

            // Map from gpu to local 
            buffer = (float*)glMapBufferRange(GL_ARRAY_BUFFER, 0, (GLsizeiptr)(max_sprites * vertex_size), fMap);
            buffer_curr = buffer;

            auto stride = 0ull;
            for (auto i = 0; i < attribute_sizes.size(); ++i)
            {
                glEnableVertexAttribArray(i);
                glVertexAttribPointer(i, attribute_sizes[i], GL_FLOAT, GL_FALSE, attribute_size * sizeof(float), (GLvoid*)stride);
                stride += attribute_sizes[i] * sizeof(float);
            }
        }

        ~Renderer()
        {
            // delete allocated buffers
            glDeleteBuffers(1, &vbo);
            glDeleteBuffers(1, &vao);
        }

        GLuint max_sprites,
            indices_size,
            vertex_size,
            attribute_size,
            quad_count;

        std::vector<GLuint> channels;
        std::vector<GLuint>::iterator curr_channel;
        std::vector<GLuint>::iterator next_channel;

        float* buffer;
        float* buffer_curr;

        GLuint vbo;
        GLuint vao;

        Material* current_material;
    };
void flush(Renderer* renderer)
{
    if (!renderer->quad_count) return;

    if (!renderer->current_material)
    {
        renderer->quad_count = 0;
        renderer->buffer_curr = renderer->buffer;
        return;
    }

    // bind texture
    renderer->current_material->mat->bind(renderer->current_material->texture);

    // set uniforms and use shader
    renderer->current_material->mat->compile(renderer->current_material->shader);

    auto total_draws = (GLsizei)(renderer->quad_count * renderer->indices_size);
    auto first_pos = *renderer->curr_channel / renderer->attribute_size;

    // draw triangles
    glDrawArrays(GL_TRIANGLES, first_pos, total_draws);

    renderer->curr_channel++;
    renderer->buffer_curr = &renderer->buffer[*renderer->curr_channel];
    renderer->next_channel++;
    renderer->quad_count = 0;
}
void draw(Renderer* renderer, Src* src, Dest* dest, Material* material)
{
    if (renderer->next_channel == renderer->channels.end())
    {
        std::cerr << "not enough space to render batch: " << material->batch_id << std::endl;
        return;
    }

    if ((renderer->quad_count * renderer->vertex_size >= *renderer->next_channel || !renderer->current_material) || material->hash != renderer->current_material->hash)
    {
        pflush(renderer);
        renderer->current_material = material;
    }

    auto& buffer = renderer->buffer_curr;

    // first triangle
    *buffer++ = dest->dest.x;
    *buffer++ = dest->dest.w;
    *buffer++ = src->src.x;
    *buffer++ = src->src.w;

    *buffer++ = dest->dest.z;
    *buffer++ = dest->dest.y;
    *buffer++ = src->src.z;
    *buffer++ = src->src.y;

    *buffer++ = dest->dest.x;
    *buffer++ = dest->dest.y;
    *buffer++ = src->src.x;
    *buffer++ = src->src.y;

    // second triangle
    *buffer++ = dest->dest.x;
    *buffer++ = dest->dest.w;
    *buffer++ = src->src.x;
    *buffer++ = src->src.w;

    *buffer++ = dest->dest.z;
    *buffer++ = dest->dest.w;
    *buffer++ = src->src.z;
    *buffer++ = src->src.w;

    *buffer++ = dest->dest.z;
    *buffer++ = dest->dest.y;
    *buffer++ = src->src.z;
    *buffer++ = src->src.y;

    ++renderer->quad_count;
}
/*
    dest for sprites. with layer
eg:

  p1         
    *_______
    |       |
    |       |
    |       |
     -------*
             p2

    dest = {p1.x, p1.y, p2.x, p2.y}

*/
struct Dest
    {
        Dest(glm::vec4 dest)
            : dest(dest)
        {}

        Dest()
            : dest()
        {}

        glm::vec4 dest;
    };

struct Src : Component
    {
        Src()
            : src(0.0f)
        {}

        Src(glm::vec4 src)
            : src(src)
        {}

        glm::vec4 src;
    };

struct Material
    {
        Material(GLint tex_unit)
            : image(tex_unit)
        {}

        GLint image;

        void compile(Shader* shader) override
        {
            glUseProgram(shader->id);
            glUniform1i(glGetUniformLocation(shader->id, "image"), image);
        }

        void bind(Texture* tex)
        {
            glActiveTexture(GL_TEXTURE0 + image);
            glBindTexture(GL_TEXTURE_2D, tex->id);
        }
    };

main function (renders first 64 and not the next 64)

int main()
{
    // init gl/glad/glfw/etc...
    
    // load some textures and shaders...

    auto Texture1 = new Texture("tex src");
    auto Shader1 = new Shader("...vertex shader", "...fragment shader" )

    auto Texture2 = new Texture("tex src");
    auto Shader2 = new Shader("...vertex shader", "...fragment shader" )

    auto projection = glm::ortho(0.0f, static_cast<GLfloat>(800),
        static_cast<GLfloat>(600), 0.0f, -1.0f, 1.0f);
    
    glUniformMatrix4fv(glGetUniformLocation(Shader1->id, "projection"), 1, GL_FALSE, projection);
    glUniformMatrix4fv(glGetUniformLocation(Shader2->id, "projection"), 1, GL_FALSE, projection);   

    auto material1 = new Material(0);
    auto material2 = new Material(1);

    auto src = new Src({0,0,1,1});

    auto Dest dests[] = new Dest()[128];

    for(auto i = 0; i < 128; ++i)
    {
        auto x = (float)(std::rand() % 800) / 800.0f;
        auto y = (float)(std::rand() % 600) / 600.0f; 
        dests[i] = new Dest(x,y, x + 32, y + 32) 
    }
    auto renderer = new Renderer({128, 128},  6u, { 2u, 2u })
    
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    renderer->current_material = nullptr;
    renderer->quad_count = 0;
    renderer->buffer_curr = renderer_->buffer;
    renderer->curr_channel = renderer_->channels.begin();
    renderer->next_channel = renderer_->curr_channel + 1;
    
    for (auto i = 0; i < 64; ++i)
        draw(renderer, src, dest[i], material1);
    for (auto i = 64; i < 128; ++i)
        draw(renderer, src, dest[i], material2);
    flush(renderer);

    std::cin.ignore();
}

Aucun commentaire:

Enregistrer un commentaire