在我的渲染引擎中,我实现了三重缓冲,以便使用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
之前执行。
因此,仅在尚未发出围栏信号时才发出冲洗。理想情况下,您将以这样的方式编写代码:实际上等待同步是例外,而不是规则。