Оптимизация потокового vbo openg
Я рендерим нисходящий мир на основе плиток, используя opengl 3.3, используя полностью потоковые VBO.
После некоторой задержки я провел несколько сравнительных тестов, и то, что я нашел, было ужасно!
Позвольте мне объяснить картину. На первом отмеченном квадрате я запускаю игру, используя самые простые шейдеры. Там нет молнии, нет ничего! Я просто загружаю 5000 вершин и рисую их. У меня загрузка памяти составляет около 20-30%, загрузка процессора - 30-40%.
Второй с молнией. Каждый источник света загружается как массив в фрагментный шейдер, и каждый фрагмент обрабатывает источники света. загрузка около 40-50%. 100% с 60 лампами.
Третий - с отложенным затенением. Сначала я рисую обычное и рассеянное изображение в FBO, затем отображаю каждый источник света в FB по умолчанию, читая из них. нагрузка около 80%. В основном не зависит от количества огней.
Это сцены, которые я представляю:
Как видите, ничего особенного. Это стиль ретро. Мой план состоял в том, чтобы добавить тонны сложности и по-прежнему бесперебойно работать на недорогих компьютерах. У меня i7 nvidia 660M, поэтому проблем не должно быть.
Для сравнения я запустил Warcraft 3, и потребовалось около 50-60% нагрузки, 20% памяти.
Одна странная вещь, которую я заметил, это то, что если я отключаю V-синхронизацию и не вызываю glFinish до тех пор, пока не произойдет обмен swapbuffers, нагрузка значительно снизится. Однако часы идут вверх и выделяется тепло (53C*).
Теперь сначала мне интересно, если вы думаете, что это нормально. Если нет, то в чем может быть мое узкое место? Может ли это быть моим потоковым VBO? Я пробовал двойную буферизацию и сиротство, но ничего. Удвоение количества спрайтов в основном увеличивает нагрузку на память на 5-10%. нагрузка на GPU остается в основном неизменной.
Я знаю, что на этот вопрос нелегко ответить, но я предоставлю более подробную информацию по мере необходимости. Не хочу размещать мои 20000 строк кода здесь.
Ох, и еще одна вещь... Это колеблется. Вызовы отрисовки идентичны, но загрузка может увеличиваться на 2-100%, когда бы это ни было похоже.
ОБНОВИТЬ:
мой основной цикл выглядит так:
SwapBuffers
renderAndDoGlCalls
updateGameAndPoll
спать, если есть время (1/60 секунды)
повторение.
Без v-sync, glflush или glfinsih это приводит к проценту использования:
своп: 0.16934400677376027
ren: 0.9929640397185616
УПП: +0,007698000307920012
Опрос: 0,0615780024631201
сон: 100.39487801579511
С glFinish до свопбуферов:
swap: 26.609977064399082 (обычно до 80%)
ren: 1.231584049263362
УПП: +0,010266000410640016
Опрос: +0,07697400307896013
сон: 74.01582296063292
с Vsync все начинается хорошо, обычно так же, как с glFinish, затем bam!
своп: 197.84934791397393
ren: 1.221324048852962
УПП: +0,007698000307920012
Опрос: +0,05644800225792009
сон: 0.002562000102480004
И так и остается.
1 ответ
Позвольте мне уточнить... Если я вызываю swapbuffers сразу после всех вызовов opengl, мой процессор останавливается на 70% времени обновления, что позволяет мне ничего не делать. Таким образом, я даю графическому процессору как можно больше времени, чтобы завершить работу с буфером, прежде чем снова вызывать своп.
Вы на самом деле случайно вызываете противоположный сценарий.
Единственный раз, когда SwapBuffers вызывает остановку вызывающего потока, - это когда очередь предварительно обработанных кадров заполнена, и ему приходится ждать, пока VSYNC сбросит законченный кадр. Процессор может легко быть на 2-3 кадра впереди графического процессора в любой момент, и не текущее завершение кадра вызывает ожидание (в этом сценарии уже есть готовый кадр, который необходимо поменять местами).
Ожидание происходит из-за того, что драйвер не может поменять задний буфер спереди назад, пока не появится сигнал VBLANK (что происходит только один раз каждые 16.667 мс). Драйвер фактически продолжит принимать команды, пока он ожидает замены до тех пор, пока не достигнет определенного предела (предварительно обработанные кадры на аппаратном уровне NVIDIA / размер очереди переворачивания на AMD) на основе перестановок в очереди. Как только этот предел будет достигнут, команды GL будут вызывать блокировку до тех пор, пока не будут / не заменены задние буферы.
Вы спите в конце своих кадров, поэтому заметного параллелизма между CPU и GPU не происходит; на самом деле вы, скорее всего, пропустите кадр таким образом.
Это то, что вы видите здесь. Абсолютный наихудший сценарий - когда вы спите на 1 мс слишком поздно, чтобы своевременно поменять буферы для VBLANK. Ваше время между двумя кадрами становится 16,66667 + 15,66667 = 32,33332 мс. Это вызывает заикание, которое не произошло бы, если бы вы не добавили свое время ожидания. Драйвер мог бы легко скопировать задний буфер назад и продолжить принимать команды в течение добавленных вами дополнительных 1 мс, но вместо этого он блокируется еще на 15 в начале следующего кадра.
Чтобы избежать этого, вы хотите поменять буферы как можно скорее после того, как все команды для кадра были выполнены. У вас больше всего шансов уложиться в сроки VBLANK. Заявленное использование ЦП может возрасти, так как меньше времени затрачивается на сон, но производительность должна измеряться с использованием времени кадра, а не запланированного времени ЦП.
Обсуждаемый VSYNC и предел предварительно отрендеренного фрейма не дадут вашему CPU и GPU выйти из-под контроля и генерировать огромное количество тепла, как упоминалось в вопросе.