glDrawElementsInstanced冻结或减速为18680个实例

时间:2015-06-03 14:57:51

标签: c++ macos opengl opengl-3

我正在开发一个模拟降雨的C ++程序。 我正在使用OpenGL实例功能来渲染越来越多的水滴。 (一个实例=一个液滴)

程序在调用glDrawElementsInstances时运行正常,直到实例数达到18680.然后,它冻结或产生奇怪的行为(巨大的减速,实例的不连贯渲染)。

我的渲染循环:

GLObject GLDrop(*this->_model._drop, *this->_shader); // generate buffer
while (!glfwWindowShouldClose(this->_window))
{
     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     this->_model._drop->create();
     GLDrop.setDropState();
     glDrawElementsInstanced(GL_TRIANGLE_STRIP, this->_model._drop->getElementsSize(), GL_UNSIGNED_INT, 0, this->_model._drop->getInstances());
     GLDrop.disableDropState();
     glfwSwapBuffers(this->_window);
     glfwPollEvents();
}

我的缓冲区生成函数,在渲染循环之前调用:

void            GLObject::generateDropBuffers(void)
{
    glGenBuffers(1, &this->_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, this->_vbo);
    glBufferData(GL_ARRAY_BUFFER, this->_module.getVerticesSize() * sizeof(GLfloat), this->_module.getVertices(), GL_STATIC_DRAW);

    glGenBuffers(1, &this->_ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->_ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->_module.getElementsSize() * sizeof(GLuint), this->_module.getElements(), GL_STATIC_DRAW);

    glGenBuffers(1, &this->_pbo);
    glBindBuffer(GL_ARRAY_BUFFER, this->_pbo);
    glBufferData(GL_ARRAY_BUFFER, this->_module.getMaxInstances() * DIMENSIONS * sizeof(GLfloat), NULL, GL_DYNAMIC_DRAW);

    glGenBuffers(1, &this->_cbo);
    glBindBuffer(GL_ARRAY_BUFFER, this->_cbo);
    glBufferData(GL_ARRAY_BUFFER, this->_module.getMaxInstances() * COLOR_CHANNELS * sizeof(GLfloat), NULL, GL_DYNAMIC_DRAW);
}

每次调用Drop.create()时,都会创建一批新的Droplet,增加要绘制到屏幕的实例数。

void            Drop::create(void)
{
    unsigned int    i;
    unsigned int    j;

    if (this->_instances < this->_maxInstances - this->_dropBatch)
    {
        for (GLuint drop = 0; drop < this->_dropBatch; ++drop)
        {
           i = this->_instances * DIMENSIONS;
           j = this->_instances * COLOR_CHANNELS;

           this->_positions[i] = rand() % this->_model._vertexCol * UNIT;
           this->_positions[i + 1] = this->_model._top + 3.0f * UNIT;
           this->_positions[i + 2] = rand() % this->_model._vertexRow * UNIT;

           this->_colors[j] = 0.0f;
           this->_colors[j + 1] = 0.0f;
           this->_colors[j + 2] = 1.0f;
           this->_colors[j + 3] = 1.0f; 
           this->_instances += 1;
        }
     }
}

我的缓冲区绑定功能:

void            GLObject::setDropState(void)
{
    GLuint      instances = this->_module.getInstances() - this->_module.getBatchSize();
    GLuint      posOffset = instances * DIMENSIONS;
    GLuint      colorOffset = instances * COLOR_CHANNELS;
    GLuint      posSize = this->_module.getBatchSize() * DIMENSIONS * sizeof(GLfloat);
    GLuint      colorSize = this->_module.getBatchSize() * COLOR_CHANNELS * sizeof(GLfloat);

    glBindBuffer(GL_ARRAY_BUFFER, this->_vbo);
    glVertexAttribPointer(this->_shader.getAPosition(), DIMENSIONS, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(this->_shader.getAPosition());

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->_ebo);

    glBindBuffer(GL_ARRAY_BUFFER, this->_pbo);
    glBufferSubData(GL_ARRAY_BUFFER, posOffset * sizeof(GLfloat), posSize, this->_module.getPositions() + posOffset);
    glVertexAttribPointer(this->_shader.getAInstance(), DIMENSIONS, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(this->_shader.getAInstance());

    glBindBuffer(GL_ARRAY_BUFFER, this->_cbo);
    glBufferSubData(GL_ARRAY_BUFFER, colorOffset * sizeof(GLfloat), colorSize, this->_module.getColors() + colorOffset);
    glVertexAttribPointer(this->_shader.getAColors(), COLOR_CHANNELS, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(this->_shader.getAColors());

    glVertexAttribDivisor(this->_shader.getAPosition(), 0);
    glVertexAttribDivisor(this->_shader.getAInstance(), 1);
    glVertexAttribDivisor(this->_shader.getAColors(), 1);
}

经过测试:

  

NVIDIA GeForce GT 330M 512 MB,MacbookPro。

     

NVIDIA GeForce 9400M 256 MB,MacbookPro

我不知道可能是什么问题。我可以在一次通话中绘制的实例数量有限制吗? (我非常怀疑是这种情况)。 我使用的缓冲区比我需要的大,以确保它不是内存访问问题。

这可能是内存对齐问题吗?

OpenGL手册说这是关于对齐的,但我无法真正理解。

  

客户必须使数据元素符合要求   客户端平台,具有额外的基本级要求   缓冲区内的偏移量包含N个字节的数据   Ñ

欢迎任何帮助。

我的场景,在撞击之前绘制了18860个液滴实例

my scene 修改

在Google上搜索时,我在此处发现了类似的案例:https://www.opengl.org/discussion_boards/showthread.php/181142-glDrawArraysInstanced-max-number-of-instances

线程的作者和我有同样的问题,但实际上是实例数量的两倍(我猜因为他的GPU内存是512而不是256)。此外,我发现根据每个循环添加的实例数量,我有不同的行为。

例如: 如果我为每个循环添加100个实例,我的程序就会冻结。 如果我为每个循环添加200个或更多实例,我的程序会减慢到4fps。

在我上面发布的链接的底部,authour exaplains详细说明了这种行为的原因。显然,差异是由于跳过(或不跳跃)特定的实例间隔。 如果我为每个循环添加100个实例,我会陷入死亡间隙(如此冻结),但是如果我添加超过100个,我会跳过死亡间隙(如此巨大的减速)

对这个奇怪的macbook&#34; bug&#34;?

的任何想法

1 个答案:

答案 0 :(得分:0)

我在glDrawElementsInstancedEXT(iPhone 5)上遇到了类似的问题。我尝试了不同的网格,发现如果索引总数(实例数乘以网格索引数)超过大约4百万,则FPS下降百倍。我想补充一点,使用glDrawElements的绘图在这种情况下以类似的方式减慢。

还有另一个问题。 如果多个实例超过65535,glDrawElementsInstancedEXT将根据第一次调用glDrawElementsInstancedEXT中绘制的实例数绘制:

  • 如果第一次调用中的实例数小于65536,则以下调用仅绘制前65535个实例

  • 如果第一次调用中的实例数超过65535,则以下调用将绘制完整的实例数。

它看起来像是驱动程序中的错误。