glDrawElemets медленный

Пишу игру на подобии minecraft (воксельный движок) на основе OpenGL и SDL2.

Столкнулся с такой проблемой: Рендеринг чанков очень медленный (пробуя разные методы отрисовки, получал от 9% до 50% в диспечере задач за ОДИН чанк). Вот некоторые куски кода:

Макросы, указывающие параметры чанков (длину по осям x,y,z):

#define CMD_CHUNK_COUNT_X 16
#define CMD_CHUNK_COUNT_Y 256
#define CMD_CHUNK_COUNT_Z 16

#define CMD_CHUNK_COUNT_ALL CMD_CHUNK_COUNT_X*CMD_CHUNK_COUNT_Y*CMD_CHUNK_COUNT_Z

Заполнение буфера вершин в структуре данных CMD_Mesh:

void CMD_RegenerateChunkMesh(CMD_Mesh* dst, CMD_Chunk* chunk)
{
    CMD_ChunckVertex* vertecies = (CMD_ChunckVertex*)malloc(
        CMD_CHUNK_COUNT_ALL * 24 * sizeof(CMD_ChunckVertex)); // max size
    memset(vertecies, 0, CMD_CHUNK_COUNT_ALL * 24 * sizeof(CMD_ChunckVertex));

    for(uint32_t x = 0; x < CMD_CHUNK_COUNT_X; x++)
    for(uint32_t y = 0; y < CMD_CHUNK_COUNT_Y; y++)
    for(uint32_t z = 0; z < CMD_CHUNK_COUNT_Z; z++)
    {
        uint32_t offset = (x*CMD_CHUNK_COUNT_Y*CMD_CHUNK_COUNT_Z + y*CMD_CHUNK_COUNT_Z +z)*24;
        vec3 currentPos = {x,y,z};
        vec3 opx = {1,0,0};
        vec3 onx = {-1,0,0};
        vec3 opy = {0,1,0};
        vec3 ony = {0,-1,0};
        vec3 opz = {0,0,1};
        vec3 onz = {0,0,-1}; 
        vec3* direcions[6] = {opx, onx, opy, ony, opz, onz};
        for(uint32_t i = 0; i < 6; i++){
            if(CMD_DrawSide(chunk, currentPos, direcions[i]))
            {
                for(uint32_t j = 0; j < 4; j++){
                vertecies[offset+j+i*4] = CMD_MapChunkVertexData(
                CMD_VOXEL_VERTECIES[j*4+0+i*4*4] +x,
                CMD_VOXEL_VERTECIES[j*4+1+i*4*4] +y,
                CMD_VOXEL_VERTECIES[j*4+2+i*4*4] +z,
                CMD_VOXEL_VERTECIES[j*4+3+i*4*4], 
                2
                );
                }
            }
        }
        
    }
    dst->ibo = CMD_ChunkIBO;
    GPI_BindBuffer(&dst->vbo);
        glBufferSubData(dst->vbo.TYPE, 0, CMD_CHUNK_COUNT_ALL * 24 * sizeof(CMD_ChunckVertex), vertecies);
    free(vertecies);
}

CMD_Mesh хранит в себе VBO, IBO (ссылку на него ибо IBO глобальное для всех чанков), VAO. Функция CMD_MapChunkVertexData сжимает данные, дабы одна вершина весила всего 8 байт (2 uint32_t) Сама процедура добавляет в псевдо 3D массив вершины, с offset-ом.

И собственно сам виновник торжества - отрисовка:

void CMD_RenderChunkMesh(CMD_Mesh* mesh, vec3 pos)
{
    GPI_BindVertexArray(&mesh->vao);
    GPI_BindVertexArrayAttribs(&mesh->vao);
    GPI_BindBuffer(&mesh->vbo);
    GPI_BindBuffer(&mesh->ibo);

    GPI_BindShader(&CMD_ChunkShader);
    int32_t loc = GPI_GetUniformLocation(&CMD_ChunkShader, "u_ChunkPosition");
    if(loc != -1)
        glUniform3f(loc, pos[0], pos[1], pos[2]);
    glDrawElements(
        GL_TRIANGLES,
        CMD_CHUNK_COUNT_ALL * 36, 
        GL_UNSIGNED_INT, 
        NULL);
}

Рендерю я с интегрированной видеокарты Intel HD Graphics 630, но пробовал и со своей основной GTX 1050 Ti, но на моё огромное удивление, особой разницы не нашел.

Есть ли более эффективный метод отрисовки?


Ответы (1 шт):

Автор решения: TheCPPNoob

Решению - быть! Всё дело в том, что вершины из VBO раскиданы под неким смещением а не в куче.. Было это сделано намеренно ибо так проще убирать блоки. Но проблема в том, что glDrawElements рисует ВЕСЬ vbo, что невероятно бьет по оптимизации (т.е количество рисуемых индексов = CMD_CHUNK_COUNT_ALL * 36).

Что же нужно делать? Использовать массив как массив и держать каждую вершину подряд.

НО есть у такого подхода проблема: игры на подобии minecraft дают игроку возможность редактировать мир, поэтому необходимо хранить массив вершин чанка в оперативной памяти и в нужный момент пушить данные с оперативной памяти в видеопамять (да, это дополнительный расход памяти). Но все не так плохо как кажется, ибо одна вершина занимает всего 8 байт.

→ Ссылка