2015-11-02 36 views
1

在我的渲染引擎,我实现了三重缓冲更新使用glMapSubBuffer有成为孤儿和围墙一起着色器缓冲区。在Windows中一切正常,但当我在Mac上运行我的引擎时出现问题。当我呼叫glClientWaitSync为了等待我的缓冲区空闲时,它总是返回GL_TIMEOUT_EXPIRED,所以我无法更新缓冲区,并且引擎进入无限循环,等待它们释放。glClientWaitSync不工作在MacOS

我认为这个问题是我如何实现三重缓冲,也因为我没有使用此之后获得了这么多的性能。

基本上,我已经做到了这一点:

  1. 对于每个着色器我检查是否有缓冲区,如果是这样,我用一个类来处理它认为我创造,而3个缓冲这些缓冲区的更新分析着色器和予设定的3个围栏0(每个缓冲器)
  2. 在我的渲染循环余设置的数据为每个着色器分组的模式,以节省着色器开关,所以我做我的呈现,并以每个着色器周期。
  3. 然后为每个着色器我称之为结合其缓冲区,以便与材料和模型的数据更新它们的功能。我调用这个函数传递一个索引,该索引告诉着色器的3个缓冲区中的哪一个要绑定。该索引在主程序执行的每次绘图调用中都会更新。我用来绑定缓冲区以进行更新的函数是函数A(参见下面)

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

  5. 然后,只需使用相同的着色器,可能会有更多的数据,所以我重新做步骤分3和4,直到着色器已绘制完成的数据。

功能:

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,每次都会发出异常,导致程序崩溃。

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

+0

至少在等待循环之前的第一次调用中,您应该用'GL_SYNC_FLUSH_COMMANDS_BIT'调用'glClientWaitSync()'。否则,GL可能永远不会实际处理挂起的命令(它可能更有效,而且手动执行'glFlush'操作的全局性更低)。随机异常听起来像你的应用程序逻辑中的一些错误。 – derhass

+0

你完全正确。使用'GL_SYNC_FLUSH_COMMANDS_BIT'似乎可以工作,并解决了其他一些问题(主要是由'glFlush'造成的)。如果您将评论发布为答案,我会将其标记为已接受。尽管如此,还是有些困扰我。强制缓冲区刷新,这是正确的吗?我的意思是,我正在使用三重缓冲,这样我可以使用数据占用的缓冲区,同时为另一个绘制调用更新另一个缓冲区。所以,如果我强制缓冲区刷新,就好像总是使用相同的缓冲区,而三重缓冲区没用,对吧? – zeb

+0

那么,你不能同步并让GL在同一时间异步运行。如果您在发出新的GL命令之前等待GL完成一定的命令序列,这意味着您对可能排队的命令施加了一些上限。但是,刷新并不意味着队列空运行 - 特别是如果其他绘图命令在围栏后排队,所以在队列运行为空之前,您将在唤醒后继续排队新命令。所以三脚架缓冲本身并不是无用的。 – derhass

回答

0

您应该致电glClientWaitSync()GL_SYNC_FLUSH_COMMANDS_BIT,至少在等待循环之前您的第一个电话。否则,GL可能永远不会处理待处理的命令,并且等待将永远持续。

请注意,使用glFlush这可能更有效。从OpenGL 4.5 core profile specification的部分4.1.2引述:

如果SYNC_FLUSH_COMMANDS_BIT位在flags设置,并且sync是 unsignaled当ClientWaitSync被调用,那么的 Flush相当于将被阻塞sync之前进行。

所以只有当栅栏尚未发出信号时才会发出冲水。理想情况下,你会以这样的方式编写代码,实际上等待同步将是例外,而不是规则。

+0

当然我会以等待同步的方式编写我的代码,在我的PC上大部分都是这样,但在Mac上,我甚至在'GL_SYNC_FLUSH_COMMANDS_BIT'上尝试了几秒钟,但仍然返回' GL_TIMEOUT_EXPIRED' – zeb