2014-11-25 2 views
1

Я пытаюсь использовать профилировщик процессора и графического процессора для использования в видеоигре. Цель состоит в том, чтобы иметь на экране 2 графики, которые дают время, затрачиваемое на различные задачи кадра. Это очень грубая иллюстрация того, что он выглядит следующим образом:Синхронизация таймера OpenGL и CPU/GPU

enter image description here

Я использую QueryPerformanceCounter (на Windows), чтобы получить время центрального процессора и glQueryCounter (GL_TIMESTAMP), чтобы получить раз GPU.

Проблема в том, что время, которое я получаю для CPU и GPU, не относится к одному и тому же происхождению. Моя первая попытка исправить это - сделать блокирующий вызов метки времени GPU (с glGetInteger64v (GL_TIMESTAMP)) во время инициализации приложения, за которым следует получение времени процессора. Разница между двумя временами позволила мне в основном конвертировать времена GPU в процессорное время и синхронизировать оба графика. Но через несколько секунд (или минут) время дрейфует, и мои графики неправильно синхронизированы.

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

Кто-нибудь пытался сделать что-то подобное или советом о том, как синхронизировать процессор и время GPU без ущерба для производительности?

У меня есть другая идея, но я не знаю, будет ли она работать хорошо: иметь отдельный поток, сделать блокирующий вызов, чтобы получить время GPU, также получить процессорное время и отправить обратно разницу в основной поток как-то , Затем поток будет спать несколько секунд и начнется снова. Я попробую это и обновить сообщение, если это будет работать хорошо.

Редактировать: Я пробовал идею выше как на AMD, так и на NV-карте, и, похоже, она работает нормально. Даже при вызове glGetInteger64v (GL_TIMESTAMP) много раз (со сном всего 1 мс между каждым вызовом), похоже, это не влияет на производительность. Мне все еще нужно делать более глубокие тесты, но пока это похоже на хорошее решение.

+0

Хотя у меня нет ответа на ваш вопрос, я бы определенно не использовал метод «другой идеи»: если вы используете один и тот же контекст в обоих потоках, блок будет останавливать игру, как если бы вы использовали только один нить. При использовании отдельных контекстов для каждого потока драйвер будет заменять ваш рендеринг-контекст всякий раз, когда вы выполняете блокирующий вызов, что также вызовет заикание в вашей игре. Для полноты: на всех графических процессорах, с которыми я работал, времена между gpu и CPU не сильно расходились с течением времени. На моей текущей nvidia 680 это составляет примерно миллисекунду в час. – BDL

+0

Синхронизация ... без ущерба для производительности ... маловероятно IMO – dtech

+0

Другая идея: Проблема с блокировкой вызовов OpenGL заключается в том, что они очищают ваш конвейер и ждут до этого. Как насчет синхронизации непосредственно после swapbuffer, так как это уже сбрасывает конвейер? В этом случае glGetInteger64v должен немедленно вернуться – BDL

ответ

0

Я сделал отдельный поток, который делает блокирующий вызов glGetInteger64v (GL_TIMESTAMP). Это не показало заметной разницы в производительности даже при ожидании чего-то глупого малого, как 1 мс между каждым звонком. Хотя следует отметить, что перед этой модификацией у меня уже было несколько контекстов OpenGL.

В конечном коде я жду 30 секунд между каждым вызовом glGetInteger64v (GL_TIMESTAMP). Кажется, что достаточно часто, чтобы избежать видимых дрейфов на всех проверенных мной ПК.

Я тестировал карты Nvidia (GTX 460, GTX 660, GTX 780ti, GTX 780M) и AMD (Radeon HD 7950), все с процессорами Intel.