glClientWaitSync在MacOS上不工作

glClientWaitSync not working on MacOS

本文关键字:工作 MacOS glClientWaitSync      更新时间:2023-10-16

在我的渲染引擎中,我已经实现了三重缓冲,用于使用glMapSubBuffer以及orphaning和fence更新着色器缓冲区。在Windows中一切都很好,但当我在Mac上运行引擎时,就会出现问题。当我调用glClientWaitSync以等待缓冲区释放时,它总是返回GL_TIMEOUT_EXPIRED,所以我无法更新缓冲区,引擎会无限循环等待它们释放。

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

基本上我已经做到了:

  1. 对于每个着色器,我都会检查它是否有缓冲区,如果有,我会使用一个类来处理这些缓冲区的更新,这些缓冲区包含我在分析着色器时创建的3个缓冲区,并将3个栅栏设置为0(每个缓冲区一个)
  2. 在我的渲染循环中,我为每个着色器设置数据,对模型进行分组,以保存着色器切换,因此我使用每个着色器循环进行渲染
  3. 然后,对于每个着色器,我调用一个绑定其缓冲区的函数,以便使用材质和模型的数据更新缓冲区。我调用这个函数传递一个索引,该索引告诉要绑定着色器的3个缓冲区中的哪个缓冲区。该索引在主程序执行的每次绘图调用时都会更新。我用来绑定缓冲区进行更新的函数是函数A(见下文)

  4. 更新缓冲区后,我用glDrawElementsInstanced(GL_TRIANGLES, mesh->GetFacesIndicesCount(), GL_UNSIGNED_INT, 0, _instances.size());绘制对象,然后为刚刚与下面给出的函数B一起使用的缓冲区创建围栏

  5. 然后,对于刚才使用的同一个着色器,可能会有更多的数据,所以我重新执行步骤a第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());后更改缓冲区索引,而不是每次渲染调用只更改一次,但仍然存在同样的问题。


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

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

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

注意,这可能比使用glFlush更有效。引用OpenGL 4.5核心配置文件规范第4.1.2节:

如果在flags中设置了SYNC_FLUSH_COMMANDS_BIT位,并且sync为调用ClientWaitSync时无信号,则等效于CCD_ 18将在对CCD_。

因此,只有当围栏还没有发出信号时,才会发出冲水信号。理想情况下,您应该以这样一种方式编写代码,即实际等待同步将是例外,而不是规则。