在我的渲染引擎中,我实现了三重缓冲,以便使用glMapSubBuffer以及孤立和围墙来更新着色器缓冲区。在Windows上一切正常,但是当我在Mac上运行引擎时,会出现问题。当我调用glClientWaitSync以等待缓冲区释放时,它总是返回GL_TIMEOUT_EXPIRED,因此我无法更新缓冲区,并且引擎进入无限循环以等待缓冲区释放。

我认为问题出在我如何实现三重缓冲上,这也是因为使用此方法后我没有获得太多性能。

基本上我已经做到了:


对于每个着色器,我都会检查它是否具有缓冲区,如果有,我将使用一个类来处理这些缓冲区的更新,这些更新包含在分析着色器时创建的3个缓冲区,并将3个篱笆设置为0(每个对应一个)缓冲)
在我的渲染循环中,我为每个着色器设置了将模型分组的数据,以节省着色器切换,因此我使用每个着色器周期进行渲染。
然后,对于每个着色器,我调用一个绑定其缓冲区的函数,以便使用材质和模型的数据更新它们。我通过传递一个索引的方法来调用此函数,该索引告诉绑定着色器的3个缓冲区中的哪一个。主程序执行的每个绘制调用都会更新该索引。我用来绑定缓冲区以进行更新的函数是函数A(请参见下文)
更新缓冲区后,用glDrawElementsInstanced(GL_TRIANGLES, mesh->GetFacesIndicesCount(), GL_UNSIGNED_INT, 0, _instances.size());绘制对象,然后为缓冲区创建围栏,该围栏仅与下面给出的功能B一起使用
然后,对于刚刚使用的同一着色器,可能会有更多数据,因此我重新执行步骤3和4,直到着色器必须绘制的数据完成为止。


功能A:

bool BindForUpdate(AUint bufferIndex)
{
    if (!_created)
        return false;

    if(_fences[bufferIndex] != nullptr)
    {
        // This is the point where the rendering goes into infinite loop
        unsinged int result = glClientWaitSync(_fences[bufferIndex], 0, BUFFERS_UPDATE_TIMEOUT);
        while (result == GL_TIMEOUT_EXPIRED || result == GL_WAIT_FAILED)
            result = glClientWaitSync(_fences[bufferIndex], 0, BUFFERS_UPDATE_TIMEOUT);

        glDeleteSync(_fences[bufferIndex]);
    }

    glBindBufferBase(GL_UNIFORM_BUFFER, _bindingPoint, _ubos[bufferIndex]);

    glBufferData(GL_UNIFORM_BUFFER, _bufferDataSize * _bufferLength, nullptr, GL_STREAM_DRAW);

    _updateDataBuffer = (unsigned char*)glMapBufferRange(GL_UNIFORM_BUFFER, 0, _bufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);

    if (_updateDataBuffer == nullptr)
        return false;
    return true;
}


功能B:

void SyncBuffers(unsinged int bufferIndex)
{
    _fences[bufferIndex] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}


我认为问题恰好是我执行三重缓冲的方式,因为我在同一渲染周期内多次使用同一缓冲区,因为要使用的缓冲区的索引在每个渲染周期都会更改一次,而不是每次绑定着色器时都会更改一次缓冲区。

我该如何解决这个问题?



我只是尝试在每次调用glDrawElementsInstanced(GL_TRIANGLES, mesh->GetFacesIndicesCount(), GL_UNSIGNED_INT, 0, _instances.size());之后更改缓冲区索引,而不是在每个渲染调用中仅更改一次,但是仍然存在相同的问题。



我尝试强迫OpenGL在glDrawElementsInstanced调用glFlush之后释放缓冲区,并且它工作了很短的时间。我使用的是OS X 10.10,它可以与glFlush一起使用(仍然存在问题,因为有时glFlush在执行过程中会给出异常),但是后来我更新到OS X 10.11,并且glFlush每次都开始给出异常,导致崩溃程序。

而且,最重要的是,我认为这根本不是解决此问题的正确方法。

最佳答案

您至少应该在等待循环之前的第一次调用中使用glClientWaitSync()调用GL_SYNC_FLUSH_COMMANDS_BIT。否则,GL可能永远不会真正处理待处理的命令,并且等待将永远持续下去。

注意,这可能比使用glFlush更有效。引用OpenGL 4.5 core profile specification的4.1.2节:


  如果SYNC_FLUSH_COMMANDS_BIT中的flags位置1,并且sync
  调用ClientWaitSync时无信号,则等价于
  Flush将在阻止sync之前执行。


因此,仅在尚未发出围栏信号时才发出冲洗。理想情况下,您将以这样的方式编写代码:实际上等待同步是例外,而不是规则。

10-08 00:43