Я разрабатываю научное приложение, которое должно оценивать (как можно лучше) разницу во времени между объектом, рисуемым в буфере видеоввода, и точкой, в которой этот объект фактически становится видимым на экране. Другими словами, как DirectX в Windows XP + справляется с циклом вертикального обновления монитора.
Начну с того, что мои подпрограммы основаны на библиотеке SDL 1.3. В результате у меня нет немедленного доступа к DirectX API, но это можно изменить при необходимости. DirectX инициализируется с помощью D3DSWAPEFFECT_DISCARD, D3DPRESENT_INTERVAL_ONE и BackBufferCount = 1 в полноэкранном режиме. Похоже, что это самые важные параметры, но я с удовольствием изучу оставшуюся часть кода SDL, если потребуется дополнительная информация.
Флаг D3DPRESENT_INTERVAL_ONE гарантирует, что задний и передний буферы меняются местами не чаще одного раза за цикл обновления и никогда в середине обновления (в основном это включает vsync). Действительно, если у меня есть простой цикл, который постоянно вызывает IDirect3DDevice9 :: Present (в моем случае SDL_RenderPresent), эта функция будет блокировать количество миллисекунд между двумя циклами обновления (16,67 мс с 60 Гц, 10 мс с 100 Гц и т. Д.) .
Вот мой вопрос ... Предположим, я рисую белый квадрат в заднем буфере и вызываю SDL_RenderPresent, который блокируется на 16,67 мс (при условии обновления 60 Гц). Что я могу сделать вывод о состоянии видимого изображения на мониторе, когда возвращается вызов SDL_RenderPresent? Вот возможности, которые я вижу:
- Белый квадрат был просто нарисован на мониторе.
- Белый квадрат равен около (менее чем за 1 мс).
- Предыдущий передний буфер был только что нарисован; пройдет еще один цикл обновления (16,67 мс), прежде чем появится мой белый квадрат (повторный вызов SDL_RenderPresent приведет меня к случаю 1).
- Предыдущий передний буфер был нарисован за последние 16,67 мс, мой белый квадрат следующий, но точное время до следующего обновления неизвестно.
Из всего прочитанного мной я склоняюсь к варианту 3, но не могу найти никаких гарантий против 4. В моей конфигурации функция Present должна блокироваться, только если она вызывается для второго время паузы между двумя циклами обновления. Поскольку цель состоит в том, чтобы поменять местами передний и задний буферы, самая ранняя точка, в которой второй вызов может это сделать, - это сразу после обновления монитора (предыдущий передний буфер был только что нарисован). Именно в этот момент задний буфер, содержащий мой белый квадрат, можно переместить на передний план, но он должен ждать (не более) 16,67 мс, прежде чем монитор действительно прочитает и отобразит содержимое буфера. В идеале хотелось бы услышать, что функция всегда должна возвращаться, как только завершится предыдущий цикл обновления.
Может ли кто-нибудь более опытный с DirectX дать какое-либо понимание этой темы? Мои предположения верны или я что-то упустил? Будут ли эти предположения правильными для любой системы, поддерживающей DirectX, или логика может измениться в зависимости от видеокарты, монитора или некоторых других вещей?
В качестве последнего незначительного вопроса, возвращаясь к моему циклу, который просто снова и снова вызывает SDL_RenderPresent, я заметил, что первые 3 или 4 вызова возвращаются немедленно, в то время как все последующие ожидают цикла обновления. Правильно ли я полагаю, что ограничение D3DPRESENT_INTERVAL_ONE просто игнорируется до первого обновления (в отличие от некоторого рода очередей, имеющих более 2 буферов, которые я ожидаю иметь)?
Другими словами, предположим, что цикл вводится с ~ 8 мс до следующего цикла обновления. В течение этого периода он может поменять местами передний и задний буферы 4 раза. Пока это первое обновление не произойдет, SDL_RenderPresent будет возвращаться немедленно (поскольку у нас пока нет никаких фронтальных буферов, только 2 обратных буфера), но блокировка начнется, как только один из этих буферов отобразится на экране. , Это правильное объяснение или нет?
[править]
Исходя из ответов ниже, ясно, что мой подход с использованием vsync и Present не будет работать. Я думаю, что нашел другой способ достичь желаемого результата, поэтому я публикую его здесь на тот случай, если кто-то обнаружит ошибки в моем мышлении или просто для информации кого-то, кто работает над аналогичной проблемой.
Первый шаг - избавиться от D3DPRESENT_INTERVAL_ONE. Это отключает vsync и гарантирует, что любой вызов SDL_RenderPresent будет немедленно возвращен. Далее вы можете использовать IDirect3DDevice9 :: GetRasterStatus, чтобы получить информацию о текущем состоянии монитора. Он предоставляет логическое поле со значением true во время паузы между двумя циклами обновления, а другое поле сообщает текущую строку сканирования во время активного обновления. Используя эти две части информации, можно реализовать собственные подпрограммы вертикальной синхронизации, хотя и запустив цикл, который постоянно опрашивает состояние монитора и, таким образом, потребляет 100% ЦП. Это приемлемо для моих нужд.
Все еще остается вопрос буферизации - как узнать, какой кадр должен быть нарисован на экране, когда я вызываю SDL_RenderPresent? Я думаю, что нашел способ определить это, опираясь на свою способность знать, какая линия на мониторе в настоящее время рисуется. Вот основная логика:
- Подождите, пока начнется новый цикл обновления (pause = false, scanline = 0).
- Заполните следующий задний буфер красным цветом и вызовите Present.
- Подождите, пока сканлайн достигнет 32.
- Заполните следующий резервный буфер зеленым и вызовите Present.
И так далее ... В моей демонстрационной реализации я использовал красный, зеленый, синий и, наконец, черный. Идея состоит в том, что вы увидите цветовой шаблон RGB only , если GetRasterStatus предоставляет точную информацию о состоянии обновления, а передний и задний буферы немедленно переворачиваются при вызове SDL_RenderPresent. Если какое-либо из этих условий не выполнено, вы можете ничего не видеть, цвета могут поменяться местами или перекрыться и т. Д. Если, с другой стороны, вы видите постоянный шаблон RGB в верхней части экрана для каждого кадра, то это доказывает, что у вас есть прямой контроль над нарисованным изображением.
Я должен добавить, что сегодня я проверял эту теорию на нескольких компьютерах. Большинство отображало шаблон, но по крайней мере у одного весь экран был окрашен в красный цвет. У некоторых цветовые полосы будут прыгать вверх и вниз, что указывает на некоторую несогласованность в замене буферов. Это обычно случалось на старых машинах. Я думаю, что это хороший калибровочный тест, чтобы определить, подходит ли оборудование для наших целей тестирования.