2015-11-02 2 views
1

В моем механизме рендеринга я реализовал тройную буферизацию для обновления буферов шейдеров с использованием glMapSubBuffer вместе с сиротами и ограждениями. Все работает отлично в Windows, но когда я запускаю свой движок на Mac, возникает проблема. Когда я вызываю glClientWaitSync, чтобы ждать, когда мои буферы будут бесплатными, он всегда возвращает GL_TIMEOUT_EXPIRED, поэтому я не могу обновить свои буферы, и двигатель продолжает бесконечный цикл, ожидая их освобождения.glClientWaitSync не работает на MacOS

Я думаю, проблема заключается в том, как я реализовал тройную буферизацию, также потому, что после ее использования я не получал так много производительности.

В принципе я это сделал:

  1. Для каждого шейдера я проверить, если он имеет буфера и, если да, то я использую класс для обработки обновления этих буферов, которые держат 3 буфера, которые я создаю время анализируя шейдер, и я установил 3 забора на 0 (по одному для каждого буфера)
  2. Во время моего цикла рендеринга я настраиваю данные для каждой шейдерной группы, моделируя модель, чтобы сохранить переключение шейдеров, и поэтому я делаю рендеринг с пер- шейдерный цикл.
  3. Затем для каждого шейдера я вызываю функцию, которая связывает ее буферы, чтобы обновить их данными материалов и моделей. Я вызываю эту функцию, передавая индекс, который сообщает, какой из трех буферов шейдера привязан. Этот индекс обновляется при каждом вызове ничьей, который выполняет основная программа. Функция, которую я использую для привязки буферов для обновления, - это функция A (см. Ниже)

  4. После обновления буфера я рисую объекты с помощью glDrawElementsInstanced(GL_TRIANGLES, mesh->GetFacesIndicesCount(), GL_UNSIGNED_INT, 0, _instances.size());, а затем создаю ограждения для буфера, который используется только с функцией B, приведенной ниже

  5. Затем, для того же самого используемого шейдера, может быть больше данных, поэтому я повторно делаю шаги в точках 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 освободить буфер после вызова glDrawElementsInstancedglFlush и он работал в течение короткого промежутка времени. Я haf OS X 10.10, и он работал с glFlush (все еще были проблемы, потому что иногда glFlush выдавал исключение во время выполнения), но затем я обновлялся до OS X 10.11 и glFlush, каждый раз начинал выдавать исключения, сбой программы.

И, прежде всего, я не думаю, что это был правильный способ решения этой проблемы.

+0

Вы должны вызвать 'glClientWaitSync()' с помощью 'GL_SYNC_FLUSH_COMMANDS_BIT', по крайней мере, в первом вызове перед циклом ожидания. В противном случае GL никогда не сможет обработать ожидающие команды (это потенциально более эффективно и менее глобально, чем вручную «glFlush'ing»). Случайные исключения звучат как некоторая ошибка в вашей логике приложения. – derhass

+0

Вы были совершенно правы. Использование 'GL_SYNC_FLUSH_COMMANDS_BIT', похоже, работает, и он решил некоторые другие проблемы, которые у меня были (главным образом, вызванные' glFlush'). Если вы опубликуете свой комментарий в качестве ответа, я помету его как принятый. Меня что-то беспокоит. Принудительно сбросить буферы, правильно ли это делать? Я имею в виду, я использую тройную буферизацию, чтобы иметь буфер, занимаемый данными, в то время как я обновляю другой буфер для другого вызова рисования. Итак, если я заставляю буфер сбрасывать, это похоже на использование всегда одного и того же буфера, и тройная буферизация бесполезна, не так ли? – zeb

+0

Ну, вы не можете синхронизировать и запускать AL одновременно одновременно. Если вы ожидаете, что GL завершит определенную последовательность команд перед выдачей новых команд GL, это означает, что вы накладываете некоторый верхний предел на команды, которые могут быть поставлены в очередь. Тем не менее, флеш не означает, что очередь запускается пустым - особенно, если есть другие команды рисования, стоящие в очереди после забора, поэтому вы будете продолжать очереди новых команд после пробуждения до того, как очередь будет пуста. Таким образом, тройная буферизация сама по себе не бесполезна. – derhass

ответ

0

Вы должны позвонить glClientWaitSync() с помощью GL_SYNC_FLUSH_COMMANDS_BIT, по крайней мере, в первый раз перед циклом ожидания. В противном случае GL никогда не сможет обработать ожидающие команды, и ожидание будет длиться вечно.

Обратите внимание, что это потенциально более эффективно, используя glFlush. Цитирование из раздела 4.1.2 OpenGL 4.5 core profile specification:

если SYNC_FLUSH_COMMANDS_BIT бит установлен в flags и sync является unsignaled, когда ClientWaitSync называют, то эквивалент Flush будет выполняться до блокировки на sync.

Таким образом, флеш будет выдаваться только тогда, когда забор еще не сигнализирован. В идеале вы должны написать код таким образом, чтобы на самом деле ждать синхронизации было бы исключение, а не правило.

+0

Конечно, я напишу свой код таким образом, что ожидание на синхронизации будет исключением, а на моем ПК это в основном так, но с Mac я пробовал даже ждать секунды на 'GL_SYNC_FLUSH_COMMANDS_BIT', но он все же вернул' GL_TIMEOUT_EXPIRED' – zeb

Смежные вопросы