OSX выводит пиксели на экран с минимальной задержкой

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

Проверь это:

https://www.dropbox.com/s/dbz4bq67cxluhs7/MouseLatency.MOV?dl=0

Вы, наверное, замечали это раньше: с приложением OpenGL на С++, перетаскиванием мыши по экрану и отрисовкой местоположения мыши в OpenGL, OpenGL отстает на 3 или 4 кадра. Ясно, что OSX МОГУТ отрисовывать [курсор] на экран с очень малой задержкой, но OpenGL намного медленнее. Итак, скажем, мне не нужно делать какой-то причудливый рендеринг OpenGL. Я просто хочу как-то вывести пиксели на экран. Есть ли способ полностью обойти OpenGL и быстрее рисовать на экране? Или такая функциональность будет заблокирована где-то внутри ядра, и я не могу ее получить?


person Josh Silverman    schedule 03.08.2015    source источник
comment
Попробуйте дать более подробную информацию о том, что вы уже пробовали, возможно, некоторые примеры кода вашей реализации.   -  person Snappawapa    schedule 03.08.2015
comment
Возможно, вы захотите прочитать эту статью о потоковой передаче объектов буфера. Я бы предположил, что способ полного обхода opengl будет включать аппаратно-зависимые API.   -  person Colonel Thirty Two    schedule 03.08.2015
comment
В OSX El Capitan можно использовать простой 4-вершинный шейдер с пиксельным шейдером для рисования из кадрового буфера с использованием Metal. См. пример objc.io/issues/18-games/metal   -  person Saharsh Bishnoi    schedule 03.08.2015
comment
Я просмотрел статью о потоковой передаче буфера, но мне не удалось получить ее, чтобы она мне помогла. Единственный способ, которым я могу воспользоваться, чтобы увидеть улучшение, - это если я каким-то образом смогу напрямую получить доступ к отрисованной памяти после openGL и перерисовать ее вне конвейера openGL. Но я не думаю, что это решение, которое я ищу...   -  person Josh Silverman    schedule 06.08.2015
comment
Когда я отключаю vsync, моя задержка внезапно падает до нуля. Может быть, дело в том, что я все еще жду, пока буфер перевернется 3 или 4 раза, но с отключенной vsync это происходит каждые 3 мс, а не каждые 16 мс?   -  person Josh Silverman    schedule 08.08.2015


Ответы (2)


ответ datenwolf отличный. Я просто хотел добавить к этому обсуждению одну вещь, касающуюся тройной буферизации на уровне компоновщика, поскольку я хорошо знаком с компоновщиком рабочего стола Microsoft Windows.

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

Тройная буферизация, которую вы можете включить на уровне приложения, добавляет в цепочку обмена третий буфер, который синхронизируется с обновлением. Такой способ выполнения тройной буферизации увеличивает задержку, потому что этот третий буфер должен отображаться, и ничто не может касаться его, пока это не произойдет (это обязательное поведение D3D — поведение и сама функция не определены в OpenGL); но способ работы диспетчера окон рабочего стола (Windows) немного отличается.

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

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

Я считаю, что вы можете добиться такого поведения в OS X (если хотите), отключив VSYNC и рисуя в окне. VSYNC в основном служит только формой синхронизации кадров (обмен задержкой на согласованность) в этом сценарии, и разрыв устраняется самим компоновщиком независимо от того, с какой скоростью вы рисуете.


Что касается задержки курсора мыши:

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

Это полностью обходит все графические API, но весьма ограничено (например, он использует курсор, определенный ОС).


TL;DR: задержка проявляется во многих формах.

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

  • Минимизируйте время до того, как что-то появится на экране

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

  • Минимизировать время блокировки (увеличить FPS); некоторые кадры никогда не будут отображаться

Предварительно визуализированные кадры — это небольшая мощная функция, которую вы не можете контролировать на уровне API OpenGL. Он устанавливает, насколько глубоко драйверу разрешено конвейеризировать все, и в зависимости от желаемой задачи вы будете торговать различными типами задержки, возясь с ней. Многие геймеры клянутся, что устанавливают это значение равным 1, чтобы свести к минимуму задержку ввода за счет общей плавности кадров.

ОБНОВИТЬ:

Предварительно отрендеренные кадры являются одной из причин вашей многокадровой задержки. Исправить это кросс-платформенным способом сложно (это настройка драйвера), но если у вас есть доступ к Fence Sync Objects, вы можете добиться того же поведения, что и принудительное значение 1.

Я могу объяснить это более подробно, если это необходимо, общая идея заключается в том, что вы вставляете синхронизацию ограждения после замены буфера, а затем ждете, пока она будет сигнализирована, прежде чем будет разрешено начать первую команду в следующем кадре. Производительность может упасть, но задержка будет сведена к минимуму, поскольку ЦП больше не будет выполнять рендеринг раньше, чем ГП.

person Andon M. Coleman    schedule 05.08.2015

Здесь есть несколько задержек.

  • Событие ввода → задержка состояния рисования

В вашем типичном интерактивном приложении у вас есть цикл событий, который обычно

  1. собирать пользовательский ввод
  2. обрабатывать пользовательский ввод
  3. определить, что будет нарисовано
  4. рисовать в задний буфер
  5. переключиться обратно на передний буфер

При обычных способах написания циклов событие-обновление-отображение почти нет задержки между шагом 5 предыдущей и шагом 1 следующей итерации. это означает, что шаги 2, 3 и 4 работают с данными, которые отстают примерно на один период кадра.

Итак, это первый источник задержки.

  • Тройная буферизация/задержка композиции

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

Если вы работаете в системе с оконным компоновщиком (который используется по умолчанию в MacOS X), это эффективно добавляет еще один этап буфера, поэтому, если у вас есть режим двойного буфера, он дает вам тройной буфер, а если у вас был тройной буфер это даст вам «четырехкратный» буфер (цитирует здесь, потому что четырехъядерный буфер - это термин, обычно используемый со стереоскопическим рендерингом).


Что вы можете с этим сделать:

  • Отключить композицию

Windows через DWM API и MacOS X позволяют отключить композицию или обойти компоновщик.

  • Уменьшение задержки ввода

Старайтесь собирать и интегрировать пользовательский ввод как можно позже (используйте спящие режимы с высоким разрешением). Если у вас есть только очень простая сцена, вы можете приблизить рисунок к крайнему сроку V-Sync; на самом деле реализация NVidia OpenGL имеет расширение для конкретного поставщика, которое позволяет засыпать до определенного периода времени до следующей вертикальной синхронизации.

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

person datenwolf    schedule 04.08.2015
comment
В Windows 8+ компоновщик окон вообще нельзя отключить. Однако на самом деле это не создает такой же задержки, как традиционная тройная буферизация. Композитор VSYNC, а приложение нет. То, как я видел, как это работает с драйверами AMD и NV, похоже на две системы с двойной буферизацией, работающие вместе. У приложения есть передний/задний буфер, а у композитора фактически есть передний/задний. Когда вы переключаетесь в приложении, передний буфер копируется в задний буфер компоновщика. Вы можете рисовать намного выше частоты обновления без разрывов или задержки, связанной с тройной буферизацией. - person Andon M. Coleman; 06.08.2015
comment
Но вы можете нарисовать много кадров, которые никогда не появятся, или повторять кадры дважды, потому что время было нарушено. Это не идеальное решение (идеальным был бы G-Sync), но оно действительно может уменьшить задержку. - person Andon M. Coleman; 06.08.2015
comment
@AndonM.Coleman: До, включая состав Windows-7, можно отключить: msdn.microsoft.com/en-us/library/windows/desktop/ — интересно, какие препараты принимали ребята из MS, когда решили, что вы не можете больше не отключать композицию в Win-8 и более поздних версиях. - person datenwolf; 06.08.2015
comment
реализация NVidia OpenGL имеет расширение для конкретного поставщика, которое позволяет спать до определенного периода времени до следующей V-Sync WGL_NV_delay_before_swap/GLX_NV_delay_before_swap ? - person genpfault; 04.01.2017