В моем механизме рендеринга я реализовал тройную буферизацию для обновления буферов шейдеров с использованием glMapSubBuffer вместе с сиротами и ограждениями. Все работает отлично в Windows, но когда я запускаю свой движок на Mac, возникает проблема. Когда я вызываю glClientWaitSync
, чтобы ждать, когда мои буферы будут бесплатными, он всегда возвращает GL_TIMEOUT_EXPIRED
, поэтому я не могу обновить свои буферы, и двигатель продолжает бесконечный цикл, ожидая их освобождения.glClientWaitSync не работает на MacOS
Я думаю, проблема заключается в том, как я реализовал тройную буферизацию, также потому, что после ее использования я не получал так много производительности.
В принципе я это сделал:
- Для каждого шейдера я проверить, если он имеет буфера и, если да, то я использую класс для обработки обновления этих буферов, которые держат 3 буфера, которые я создаю время анализируя шейдер, и я установил 3 забора на
0
(по одному для каждого буфера) - Во время моего цикла рендеринга я настраиваю данные для каждой шейдерной группы, моделируя модель, чтобы сохранить переключение шейдеров, и поэтому я делаю рендеринг с пер- шейдерный цикл.
Затем для каждого шейдера я вызываю функцию, которая связывает ее буферы, чтобы обновить их данными материалов и моделей. Я вызываю эту функцию, передавая индекс, который сообщает, какой из трех буферов шейдера привязан. Этот индекс обновляется при каждом вызове ничьей, который выполняет основная программа. Функция, которую я использую для привязки буферов для обновления, - это функция 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
и он работал в течение короткого промежутка времени. Я haf OS X 10.10, и он работал с glFlush
(все еще были проблемы, потому что иногда glFlush
выдавал исключение во время выполнения), но затем я обновлялся до OS X 10.11 и glFlush
, каждый раз начинал выдавать исключения, сбой программы.
И, прежде всего, я не думаю, что это был правильный способ решения этой проблемы.
Вы должны вызвать 'glClientWaitSync()' с помощью 'GL_SYNC_FLUSH_COMMANDS_BIT', по крайней мере, в первом вызове перед циклом ожидания. В противном случае GL никогда не сможет обработать ожидающие команды (это потенциально более эффективно и менее глобально, чем вручную «glFlush'ing»). Случайные исключения звучат как некоторая ошибка в вашей логике приложения. – derhass
Вы были совершенно правы. Использование 'GL_SYNC_FLUSH_COMMANDS_BIT', похоже, работает, и он решил некоторые другие проблемы, которые у меня были (главным образом, вызванные' glFlush'). Если вы опубликуете свой комментарий в качестве ответа, я помету его как принятый. Меня что-то беспокоит. Принудительно сбросить буферы, правильно ли это делать? Я имею в виду, я использую тройную буферизацию, чтобы иметь буфер, занимаемый данными, в то время как я обновляю другой буфер для другого вызова рисования. Итак, если я заставляю буфер сбрасывать, это похоже на использование всегда одного и того же буфера, и тройная буферизация бесполезна, не так ли? – zeb
Ну, вы не можете синхронизировать и запускать AL одновременно одновременно. Если вы ожидаете, что GL завершит определенную последовательность команд перед выдачей новых команд GL, это означает, что вы накладываете некоторый верхний предел на команды, которые могут быть поставлены в очередь. Тем не менее, флеш не означает, что очередь запускается пустым - особенно, если есть другие команды рисования, стоящие в очереди после забора, поэтому вы будете продолжать очереди новых команд после пробуждения до того, как очередь будет пуста. Таким образом, тройная буферизация сама по себе не бесполезна. – derhass