Измерение задержки кадра WebGL - PullRequest
0 голосов
/ 21 октября 2018

Я написал трассировщик пути GPU в WebGL и хотел бы узнать, сколько времени потребуется для визуализации одного кадра.Как я могу сделать это переносимо как в настольном, так и в мобильном браузерах?

У меня было несколько идей, как это сделать, но ни один из них не работает:


Идея 1: Измерение задержки между отделками:

gl.finish(); var t0=performance.now();
//(render)
gl.finish(); var t1=performance.now();
var latency = 0.001*( t1 - t0 );

Это не работает!Chrome (ужасно и по назначению) псевдонимы gl.finish() до gl.flush(), поэтому измеренная задержка практически не связана с выполненной работой.


Идея 2: Использование EXT_disjoint_timer_query / EXT_disjoint_timer_query_webgl2:

Это не работает!Злоупотребление этим может использоваться при атаке в стиле Роухаммера, поэтому отключено во всех браузерах .


Идея 3: Используйте performance.now() для измерения времени между вызовами на window.requestAnimationFrame(...).

Это не работает!Поскольку рендеринг дорогой, по энергетическим / тепловым причинам я перерисовываю кадр только тогда, когда что-то меняется (например, положение камеры).Таким образом, измеренная задержка может быть произвольно большой (и в любом случае указывается в следующем кадре).

1 Ответ

0 голосов
/ 21 октября 2018

Вы не можете проверить задержку изнутри браузера.Нет никакого способа узнать, когда изображение действительно появится на экране.Браузер может быть с двойной или тройной буферизацией, сама ОС часто имеет составной шаг и поэтому может добавить кадр, если пользователь находится на телевизоре с интерполяцией кадров, который также может добавить кадр.Возможно, вы на самом деле не имели в виду, что хотите измерить «задержку», но если вы имели в виду «задержку», вы можете сделать это только с внешним оборудованием.

Вы также не можете измерить время рендеринга, используя gl.finishпрямо даже в OpenGL.Вы не будете измерять время рендера.Вы будете измерять «время запуска» + «время рендеринга» + «время остановки», так что вы можете использовать gl.finish, чтобы узнать, быстрее ли один метод, чем другой, но вы не можете использовать gl.finish, чтобы узнать, какБыстрый кадр связан с тем, что при нормальной работе графика конвейерная, выполняется через несколько потоков или процессов.Вызов gl.finish добавляет накладные расходы на синхронизацию тех потоков и процессов, которые могут быть намного больше накладных расходов, чем просто рендеринг.

Вы могли бы потенциально использовать синхронизацию gl.finish для рендеринга наименьшей возможной вещи (один 1 пиксель).треугольник со сплошным цветом).Используйте это, чтобы измерить накладные расходы на «синхронизацию» нескольких потоков и вычесть это время из более длительных периодов времени при более длительных рендерингах, но даже при этом возникают проблемы с графическими процессорами с мозаичной архитектурой, поскольку графические процессоры с мозаичной архитектурой используют методы, позволяющие избежать перерисовки.Другими словами, если вы рисуете 2 перекрывающихся непрозрачных треугольника на традиционном графическом процессоре, каждый пиксель обоих треугольников будет нарисован, но на мозаичном перекрывающемся пикселе будет нарисован только один раз.Это означает, что отдельные чертежи по времени не сообщают вам, насколько они быстры, когда объединены.

В любом случае вы можете смоделировать gl.finish (остановка всех процессов), вызвав gl.readPixels для чтения одного пикселя.поскольку для того, чтобы перевести этот пиксель в JavaScript, все процессы должны быть остановлены и синхронизированы.

Как уже упоминалось выше, сначала вы должны нарисовать один пиксель, чтобы измерить издержки синхронизации процессов и вычесть это время.из ваших измерений.

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

Итакшаги будут выглядеть примерно так:

  1. init webgl и все ваши ресурсы
  2. нарисуют один пиксель с помощью простого шейдера
  3. нарисуйте то, что вы хотите измерить
  4. gl.readПиксели в один пиксель (чтобы очистить предыдущий материал)
  5. syncStart = performance.now ()
  6. рисование одного пикселя простым шейдером
  7. gl.readПиксели одного пикселя
  8. syncTime = performance.now () - syncStart
  9. drawStart = performance.now ()
  10. нарисуйте объект, который вы хотите измерить
  11. gl.readПиксели в один пиксель
  12. renderTime = (performance.now () - drawStart) - syncTime

Шаги 2 и 3 должны заставить любую скрытую инициализацию произойти, поэтому убедитесь, что все ресурсы используются на шагах 2 и 3, все текстуры визуализируются, все буферы доступны, и т. Д ....

...