glMultiDrawIndirect и DrawElementsIndirectCommands не отображают все объекты - PullRequest
0 голосов
/ 05 апреля 2019

Я, кажется, использую glMultiDrawIndirect (MDI) и / или DrawElementsIndirectCommand (DEIC) неправильно, поскольку я не могу правильно отобразить все объекты. Метод, пытающийся повторно использовать команды рисования для похожих объектов и текстур ('instancing' ... ish), рисует все объекты во всех местах. Метод 'debug' просто использует 1 команду рисования на объект (два треугольника), но на выходе всегда слишком мало объектов, причем местоположение первого объекта никогда не используется ни для какого объекта.

Вот что происходит при неудачной попытке создания экземпляра: enter image description here

Вот что происходит при использовании метода отладки одного объекта (двух треугольников) для каждого DEIC: debug render method

Цель состоит в том, чтобы правильно использовать instanceCount в DEIC, чтобы позволить чему-то приближаться к экземпляру, в то же время рисуя правильное количество объектов в правильных местах . Мои приключения в google-raiding предполагают, что поле baseInstance DEIC может использоваться в качестве drawID, если DEIC хранятся в буфере. Если это невозможно или я сильно неправильно понимаю использование, пожалуйста, позвоните мне и дайте мне знать! Я попытался включить наименьшее количество применимого кода, чтобы избежать поста в 10000 слов.

Ниже я создаю объекты «рисования пути», которые представляют собой набор идентификаторов буферов и векторов, которые должны быть загружены в буферы (на основе многочисленных переменных, не связанных с этим вопросом).

// VAO
glGenVertexArrays(1, &p->vertexArrayObject);
glBindVertexArray(p->vertexArrayObject);

// vertices
glCreateBuffers(1, &p->vertexBuffer);
glBindVertexBuffer(bindingIndex, p->vertexBuffer, 0, sizeof(Vertex));
glEnableVertexArrayAttrib(p->vertexArrayObject, 0);
glEnableVertexArrayAttrib(p->vertexArrayObject, 1);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, offsetof(Vertex, position));
glVertexAttribBinding(0, bindingIndex);
glVertexAttribFormat(1, 2, GL_FLOAT, GL_TRUE, offsetof(Vertex, uv));
glVertexAttribBinding(1, bindingIndex);

if(p->pathType == DrawPathType::FAST)
{
    glNamedBufferStorage(p->vertexBuffer, p->rbVertices.bufferSize, nullptr, m_persistentCreateFlags);
    p->rbVertices.ptr = (Vertex*)glMapNamedBufferRange(p->vertexBuffer, 0, p->rbVertices.bufferSize, m_persistentMapFlags);
    p->rbVertices.bufferFragment = p->rbVertices.bufferSize / 3;
}

// indices
glCreateBuffers(1, &p->indexBuffer);
glVertexArrayElementBuffer(p->vertexArrayObject, p->indexBuffer);

// draw commands
//      glCreateBuffers(1, &p->drawCmdBuffer);
//      glBindBuffer(GL_DRAW_INDIRECT_BUFFER, p->drawCmdBuffer);
//      glNamedBufferStorage(p->drawCmdBuffer, p->rbCommands.bufferSize, nullptr, m_persistentCreateFlags);
//      p->rbCommands.ptr = (DEIC*)glMapNamedBufferRange(p->drawCmdBuffer, 0, p->rbCommands.bufferSize, m_persistentMapFlags);
//      p->rbCommands.bufferFragment = p->rbCommands.bufferSize / 3;

// unsure how this works
//      glEnableVertexArrayAttrib(p->vertexArrayObject, 2);
//      glVertexAttribIFormat(2, 1, GL_UNSIGNED_INT, offsetof(DrawElementsIndirectCommand, baseInstance));
//      glVertexAttribBinding(2, bindingIndex);
//      glVertexBindingDivisor(bindingIndex, 1);

// draw IDs
glCreateBuffers(1, &p->drawIDBuffer);
glBindBuffer(GL_ARRAY_BUFFER, p->drawIDBuffer);
glEnableVertexAttribArray(2);
glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, sizeof(GLuint), 0);
glVertexAttribDivisor(2, 1);

// transforms
glCreateBuffers(1, &p->transformBuffer);
if(p->pathType == DrawPathType::LONG || p->pathType == DrawPathType::FAST)
{
    glNamedBufferStorage(p->transformBuffer, p->rbTransforms.bufferSize, nullptr, m_persistentCreateFlags);
    p->rbTransforms.ptr = (glm::mat4*)glMapNamedBufferRange(p->transformBuffer, 0, p->rbTransforms.bufferSize, m_persistentMapFlags);
    p->rbTransforms.bufferFragment = p->rbTransforms.bufferSize / 3;
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, p->transformBuffer);
}

// texture addresses
glCreateBuffers(1, &p->textureAddressBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, p->textureAddressBuffer);

Это полезная часть функции renderPrep.

for(size_t i = 0; i < glyphs->size(); i++)
{
    auto it = glyphs->at(i);

    // ensure we have a valid texture address
    if(!it->textureAddress.defined())
    {
        Logger::getInstance().Log(Logs::CRIT, Logs::Drawing, "Renderer2D::drawPrep()", "Iteration [{}] of [{}] has a null texture address (0,0)!", i, glyphs->size());
        failed++;
    }
    else
    {
        offset = verts->size();
        for(int in = 0; in < QUAD_VERTS; in++) { indices->push_back(baseQuadIndices[in] + offset); }

        // creating our model space to world space matrix ('model' in "projection * view * model")
        glm::mat4 transRotate = glm::rotate(identMat, glm::radians(it->angle), glm::vec3(0.0f, 0.0f, 1.0f));
        transforms->push_back(transRotate);
        transforms->back() = glm::translate(transforms->back(), it->position);

        // push back modelspace vertices
        for(auto& v : it->vertices) { verts->push_back(v); }

        // update previous draw command or create a new one
        if(currentTex.exists() && currentTex == it->textureAddress)
        {
            // append previous draw command
            DEICs->back().vertexCount += QUAD_VERTS;
            DEICs->back().instanceCount += 1; // two triangles, thus two instances
        }
        else
        {
            // different texture, new draw command
            DEIC tmp = { QUAD_VERTS, 1, (inc * QUAD_VERTS), (inc * BASE_VERTS), inc };
            DEICs->push_back(tmp);
            currentTex = it->textureAddress;
        }

        /// \NOTE: Current issue is that the draw command is only drawing one object, in two iterations.
        ///     This is responsible for the blank second box
        /* DEIC tmp = { QUAD_VERTS, 1, (inc * QUAD_VERTS), (inc * BASE_VERTS), 0 };
        DEICs->push_back(tmp);
        texAddrs->push_back(it->textureAddress); */

        Logger::getInstance().Log(Logs::DEBUG, Logs::Drawing, "Renderer2D::drawPrep()",
            "\n\033[93mDEIC #{}\033[0m:\n\tvertCount\t\t{}\n\tinstCount\t\t{}\n\tfirstInd\t\t{}\n\tbaseVert\t\t{}\n\tbaseInst\t\t{}\n",
            DEICs->size(), DEICs->back().vertexCount, DEICs->back().instanceCount, DEICs->back().firstIndex, DEICs->back().baseVertex, DEICs->back().baseInstance);

        texAddrs->push_back(currentTex);
        p->drawIDs.push_back(inc);
        inc++;
    }
}

Этот фрагмент фактически отвечает за рендеринг.

int activeProgramID = 0; // currently used glsl program
glGetIntegerv(GL_CURRENT_PROGRAM, &activeProgramID);

// active passed glsl program id, or enable existing if not already enabled
if(glProgID > 0) { glUseProgram(glProgID); }
else if(activeProgramID == 0) { glUseProgram(m_prog->getProgramID()); }

// all clear, do it!
glBindVertexArray(p->vertexArrayObject);

// bind SSBOs, if applicable
if(p->transformBuffer) { glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, p->transformBuffer); }
if(p->textureAddressBuffer) { glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, p->textureAddressBuffer); }

// finally render
//if(p->drawCmdBuffer) { glBindBuffer(GL_DRAW_INDIRECT_BUFFER, p->drawCmdBuffer); glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0, p->drawCommands.size(), 0); }
//else { glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, p->drawCommands.data(), p->drawCommands.size(), 0); }
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, p->drawCommands.data(), p->drawCommands.size(), 0);

// update ring buffer(s), if applicable
if(p->rbCommands.ptr != nullptr) { m_bufferLockManager->lockRange(p->rbCommands.oldHead, p->rbCommands.bufferFragment); }
if(p->rbTransforms.ptr != nullptr) { m_bufferLockManager->lockRange(p->rbTransforms.oldHead, p->rbTransforms.bufferFragment); }
if(p->rbVertices.ptr != nullptr) { m_bufferLockManager->lockRange(p->rbVertices.oldHead, p->rbVertices.bufferFragment); }

// options specific to a "fast" draw path (if a fast draw path, glyphs are single use)
if(p->pathType == DrawPathType::FAST) { p->clear(true); }

// clean up
glBindVertexArray(0);

// change to previous glProgram
if(activeProgramID) { glUseProgram(activeProgramID); }
else { glUseProgram(0); }

РЕДАКТИРОВАТЬ # 1, 2019-04-05 11: 53a EST :

Сначала я забыл шейдеры! Извиняюсь за то, что пропустил это.

// --------- Vertex shader ------------
    // uniforms / shader_storage_buffer object
    layout(std140, binding = 0) buffer CB0 { mat4 Transforms[]; };

    // view & projection in one
    uniform mat4 ViewProjection;

    // input
    layout(location = 0) in vec3 In_v3Pos;
    layout(location = 1) in vec2 In_v2TexCoord;
    layout(location = 2) in uint In_uiDrawID;

    // output
    out DrawBlock
    {
        vec2 v2TexCoord;
        flat uint iDrawID;
    } Out;

    void main()
    {
        mat4 World = Transforms[In_uiDrawID + gl_InstanceID];
        vec4 worldPos = World * vec4(In_v3Pos, 1.0);
        gl_Position = ViewProjection * worldPos;
        Out.v2TexCoord = In_v2TexCoord;
        Out.iDrawID = In_uiDrawID;
    }

// --------- Fragment shader ------------
struct TexAddress
    {
        sampler2DArray arr;
        float slice;
    };

    layout (std430, binding = 1) buffer CB1 { TexAddress texAddress[]; };

    // input
    in DrawBlock
    {
        vec2 v2TexCoord;
        flat uint iDrawID;
    } In;

    // output
    layout(location = 0) out vec4 Out_v4Color;

    vec4 Texture(TexAddress addr, vec2 uv) { return texture(addr.arr, vec3(uv, addr.slice)); }

    void main()
    {
        int DrawID = int(In.iDrawID);
        Out_v4Color = vec4(Texture(texAddress[DrawID], In.v2TexCoord).xyz, 1.0f);
    }

Если я удалил блок drawIDs с использованием не-DSA и заменил его фрагментом ниже, он рисует белые треугольники, фокусирующиеся на центре экрана.

glCreateBuffers(1, &p->drawCmdBuffer);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, p->drawCmdBuffer);
glNamedBufferStorage(p->drawCmdBuffer, p->rbCommands.bufferSize, nullptr, m_persistentCreateFlags);
p->rbCommands.ptr = (DEIC*)glMapNamedBufferRange(p->drawCmdBuffer, 0, p->rbCommands.bufferSize, m_persistentMapFlags);
p->rbCommands.bufferFragment = p->rbCommands.bufferSize / 3;

glEnableVertexArrayAttrib(p->vertexArrayObject, 2);
glVertexAttribIFormat(2, 1, GL_UNSIGNED_INT, offsetof(DrawElementsIndirectCommand, baseInstance));
glVertexAttribBinding(2, bindingIndex);
glVertexBindingDivisor(2, 1);

Результат: outcome using DEIC baseInst


РЕДАКТИРОВАТЬ # 2 @ 2019-04-06 12: 09p EST : Создана сущность на github с полным заголовком / источником для средства визуализации. Ссылка: https://gist.github.com/bbilyeu/bbf74ef4eaf979b5d2b4f2c2a9dcce48

1 Ответ

1 голос
/ 06 апреля 2019

Количество элементов, экземпляров и команд рисования различно и относится к разным вещам. В частности, использование любой команды multi-draw не обязательно означает, что вы выполняете инстансинг.

В следующем коде для вызова двух команд рисования используется один glMultiDrawArraysIndirect вызов:

  1. Визуализация трех экземпляров объекта "quad"
  2. Визуализация пяти экземпляров объекта "треугольник"

Данные

struct Vert {
    vec2f position;
};

struct Inst {
    box2f position;
    vec4f color;
};

static const Vert verts[] = {
    // quad
    { { 0, 0 } },
    { { 1, 0 } },
    { { 0, 1 } },
    { { 1, 1 } },

    // triangle
    { { 0, 0 } },
    { { 1, 0 } },
    { { 0.5, 1 } },
};

static const Inst insts[] = {
    // three quads
    { { -0.8, -0.8, -0.6, -0.6 }, { 1, 0, 0, 1 } },
    { { -0.1, -0.8, 0.1, -0.6 }, { 0, 1, 0, 1 } },
    { { 0.6, -0.8, 0.8, -0.6 }, { 0, 0, 1, 1 } },

    // five triangles
    { { -0.8, 0.6, -0.6, 0.8 }, { 1, 1, 0, 1 } },
    { { -0.4, 0.6, -0.2, 0.8 }, { 0.1, 0.1, 0.1, 1 } },
    { { -0.1, 0.6, 0.1, 0.8 }, { 0, 1, 1, 1 } },
    { { 0.2, 0.6, 0.4, 0.8 }, { 0.1, 0.1, 0.1, 1 } },
    { { 0.6, 0.6, 0.8, 0.8 }, { 1, 0, 1, 1 } },
};

static const DrawArraysIndirectCommandSN cmds[] = {
    // quads: 4 vertices, 3 instances, first vertex=0, first instance=0
    { 4, 3, 0, 0 },

    // triangles: 3 vertices, 5 instances, first vertex=4, first instance=3
    { 3, 5, 4, 3 },
};

Код инициализации:

GLuint buf[3]; // vertex, instance, draw-command buffers
glCreateBuffers(3, buf);
glNamedBufferStorage(buf[0], sizeof(verts), verts, 0);
glNamedBufferStorage(buf[1], sizeof(insts), insts, 0);
glNamedBufferStorage(buf[2], sizeof(cmds), cmds, 0);

GLuint va;
glCreateVertexArrays(1, &va);
glVertexArrayVertexBuffer(va, 0, buf[0], 0, sizeof(Vert));
glVertexArrayVertexBuffer(va, 1, buf[1], 0, sizeof(Inst));
glVertexArrayBindingDivisor(va, 1, 1);
glVertexArrayAttribBinding(va, 0, 0);
glEnableVertexArrayAttrib(va, 0);
glVertexArrayAttribFormat(va, 0, 2, GL_FLOAT, 0, offsetof(Vert, position));
glVertexArrayAttribBinding(va, 1, 1);
glEnableVertexArrayAttrib(va, 1);
glVertexArrayAttribFormat(va, 1, 4, GL_FLOAT, 0, offsetof(Inst, position));
glVertexArrayAttribBinding(va, 2, 1);
glEnableVertexArrayAttrib(va, 2);
glVertexArrayAttribFormat(va, 2, 4, GL_FLOAT, 0, offsetof(Inst, color));

Часть рендеринга:

glClear(GL_COLOR_BUFFER_BIT);
glBindProgramPipeline(pp.get());
glBindVertexArray(va);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buf[2]);
glMultiDrawArraysIndirect(GL_TRIANGLE_STRIP, 0, sizeof(cmds)/sizeof(cmds[0]), 0);

Вершинный шейдер:

#version 450 core
layout(location = 0) in vec2 position;
layout(location = 1) in vec4 inst_position;
layout(location = 2) in vec4 color;

out gl_PerVertex {
    vec4 gl_Position;
};

layout(location = 0) out PerVertex {
    vec4 color;
} OUT;

void main() {
    OUT.color = color;
    gl_Position = vec4(mix(inst_position.xy, inst_position.zw, position), 0, 1);
}

Фрагмент шейдера:

#version 450 core
layout(location = 0) in PerVertex {
    vec4 color;
} IN;
layout(location = 0) out vec4 OUT;
void main() {
    OUT = IN.color;
}

Результат:

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...