glMultiDrawIndirect和DrawElementsIndirectCommands无法呈现所有对象

时间:2019-04-05 16:45:20

标签: c++ opengl

我似乎错误地使用了 glMultiDrawIndirect (MDI)和/或 DrawElementsIndirectCommand (DEIC),因为我无法正确渲染所有对象。尝试对相似的对象和纹理重复使用绘制命令的方法(“实例化” ... ish)会在所有位置绘制所有对象。 “调试”方法仅对每个对象(两个三角形)使用1个绘制命令,但输出始终是对象太少,第一个对象的位置永远不会用于任何对象。

使用实例化失败的尝试时,会发生以下情况:enter image description here

在每个DEIC中使用一个对象(两个三角形)的“调试”方法时,会发生这种情况:debug render method

目标是正确使用DEIC中的instanceCount,以允许实例化,同时在正确的位置绘制正确数量的对象。我在Google突袭中的冒险经历表明,如果DEIC存储在缓冲区中,则DEIC 的baseInstance字段可以用作drawID。如果这是不可能的,或者如果我对用途有极大的误解,请打电话给我,让我知道!我已尝试使用尽可能少的适用代码,以避免发布10,000个字数的邮件。

下面,我正在创建“绘制路径”对象,这些对象是要加载到缓冲区中的缓冲区ID和向量的集合(基于与此问题无关的众多变量)。

// 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);
    }

如果我使用非DSA删除了drawIDs块并替换为以下代码段,则会绘制白色三角形,重点放在屏幕中心。

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上创建了一个gist,其中包含渲染器的完整标题/源。链接:https://gist.github.com/bbilyeu/bbf74ef4eaf979b5d2b4f2c2a9dcce48

1 个答案:

答案 0 :(得分:1)

元素,实例和绘制命令的数量都不同,并且引用的事物也不同。特别是,使用任何多绘制命令并不一定意味着您要进行任何实例化。

以下代码使用一个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