Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Синхронизация с монитором/видеокартой при выводе изображения под Виндой
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Dr.Alex
Если тупо выводить 25 кадров в секунду по таймеру (или по мере поступления этих кадров из источника), то возникает известный артефакт (небольшой, но всё-таки).
Если момент отрисовки виндой нового кадра придётся на момент, когда видеокарта решит выплюнуть очередной кадр на монитор, то получится что часть кадра моего видео будет содержать прошлый кадр, а часть новый. При быстрых движениях в сюжете это бывает заметно.
Можно ли как-то синхронизироваться?
Конечно, если через директшоу работать, то там всё это решено, ну а если сам выводишь?
TSerg
Cмотря через что выводить. Обычно buffered помогает.
Dr.Alex
Цитата(TSerg @ May 14 2015, 21:20) *
Cмотря через что выводить. Обычно buffered помогает.

Проясните что имеется в виду.

У меня всё тупо::
простой диалог на MFC, в функции OnPaint() вышвыриваем кадр функцией BitBlt().
slanted
Цитата(Dr.Alex @ May 14 2015, 22:27) *
простой диалог на MFC, в функции OnPaint() вышвыриваем кадр функцией BitBlt().


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

Если вам нужен полный контроль, то выводите графику через DirectX или более низкоуровневые API (Mantle, DX12 и у nvidia что-то еще было).

Да, еще. BitBlt сколь я себе помню — медленный. StretchDiBits был побыстрее, но я такой графикой развлекался последний раз еще в институте (>15 лет назад), так что могу и путать.
Dr.Alex
Цитата(slanted @ May 17 2015, 14:00) *
Все что вы рисуете из программы, не попадает напрямую в фреймбуфер, а сначала в виде команд или битмапов подается на вход compositor'а, который уже сводит итоговую картинку (с двойной буферизацией или без — это зависит от множества факторов)

Верно ли я понял что начиная с висты моей проблемки вообще не должно быть? (Я на Server 2003 x64 работаю)
Чё-то не верится. Хотя это конечно хорошо.

Цитата(slanted @ May 17 2015, 14:00) *
Если вам нужен полный контроль, то выводите графику через DirectX

Так вот знать бы, как это делать.
Тупо составить граф фильтров я не могу, так как видео нестандартного формата.
То есть либо писать свой фильтр (чего я пока не умею, да и не хочу),
либо есть такая идея - нельзя ли не писать свой фильтр а кормить Renderer прямо из приложения?
Либо может быть ещё какие-то возможности есть, а то например всякие там медиаплеер классик и VLC ведь как-то работают, не строя граф фильтров..

Цитата(slanted @ May 17 2015, 14:00) *
Да, еще. BitBlt сколь я себе помню — медленный. StretchDiBits был побыстрее

Вот со скоростью проблем не вижу, загрузка при выводе 1080p25 крошечная (а ещё ведь декодирование),
но вашу функцию попробую.

UPD: по поводу StretchDiBits:
оказалось что одно другому не мешает, у меня вместо неё SetDIBits, которая делает из буфера HBITMAP,
потом HBITMAP привязывается к CDC, который затем всё равно нужно выводить в DC функцией BitBlt или StretchBlt.
jcxz
Цитата(Dr.Alex @ May 15 2015, 00:10) *
Если тупо выводить 25 кадров в секунду по таймеру (или по мере поступления этих кадров из источника), то возникает известный артефакт (небольшой, но всё-таки).

Это не зависит от частоты обновления картинки.

Цитата(Dr.Alex @ May 15 2015, 00:10) *
Если момент отрисовки виндой нового кадра придётся на момент, когда видеокарта решит выплюнуть очередной кадр на монитор, то получится что часть кадра моего видео будет содержать прошлый кадр, а часть новый. При быстрых движениях в сюжете это бывает заметно.

Не так давно занимался выводом быстроменяющейся картинки под виндой (осциллограмма).
Обычным WinAPI (пробовал и DirectDraw - разницы в скорости - никакой).
Указанного артефакта не наблюдал. Видимо современные видеокарты (в том числе и встроенные как у меня), по дефолту уже делают двойную буферизацию
(копируют весь отображаемый экран из области хранения в область формирования сигнала в момент когда сигнал не формируется (кадровый импульс)).

Есть конечно артефакт связанный с наложением частот кадровой развёртки и обновления экрана.
Например: если картинка в каждом новом кадре смещается на один пиксел, и частота обновления картинки близка к частоте кадров, типа: 60.1 Hz и 60 Hz.
Тогда картинка будет смещаться не плавно, а дёргаться с разностной частотой. Но тут уже ничего не сделаешь. Имхо.
Dr.Alex
Цитата(jcxz @ May 18 2015, 22:24) *
Это не зависит от частоты обновления картинки.

Этого никто и не утверждал.

Цитата(jcxz @ May 18 2015, 22:24) *
Указанного артефакта не наблюдал.

Не уверен, что его можно видеть на осциллограмме. Слишком простая картинка.

Цитата(jcxz @ May 18 2015, 22:24) *
Видимо современные видеокарты (в том числе и встроенные как у меня), по дефолту уже делают двойную буферизацию
(копируют весь отображаемый экран из области хранения в область формирования сигнала в момент когда сигнал не формируется

Этого недостаточно. Видеокарта не может знать, закончила ли моя прога перерисовывать своё окно (а это только ЧАСТЬ! а не весь экран) или ещё не закончила.
Чтобы закрыть вопрос о необходимости синхронизации, вот статья Интел (с описанием именно этого артефакта).
https://software.intel.com/ru-ru/articles/v...synchronization
Жаль старая, основанная на директдро, который уже слит в отстой.
Может быть конечно в современных виндах проблема решена под корень, как тут утверждалось, но сомнительно......
Жаль пока не могу проверить под 7..
jcxz
Цитата(Dr.Alex @ May 19 2015, 02:07) *
Не уверен, что его можно видеть на осциллограмме. Слишком простая картинка.

Вполне можно. Осциллограмма у меня была без синхронизации (как на стандартных осциллографах), а бегущая по экрану (для кардио так надо).
В этом случае отрисовка в двух кадрах кадровой развёртки чётко бы виделась как постоянный излом на графике. Этого не было.

Цитата(Dr.Alex @ May 19 2015, 02:07) *
Этого недостаточно. Видеокарта не может знать, закончила ли моя прога перерисовывать своё окно (а это только ЧАСТЬ! а не весь экран) или ещё не закончила.

Этого достаточно если рисовать правильно. Как Вам выше советовали. Т.е. - рисовать полностью видеостраницу в памяти, а затем одной BitBlt переносить из неё в видеобуфер видеокарты.
Видеокарта не может знать, а WinAPI вполне знает о Вашем вызове BitBlt и вполне может его синхронизировать с кадровой развёрткой.

Цитата(Dr.Alex @ May 19 2015, 02:07) *
Жаль старая, основанная на директдро, который уже слит в отстой.

Я уже выше писал про DirectDraw. Я пробовал его. Имхо - функции синхронизации с кадровой развёркой в DirectDraw сейчас не поддерживаются. По крайней мере мне не удалось их использовать на моей карте.
Перенос картинки через DD вполне работает, но скорость та же, что и обычной WinAPI BitBlt.
Dr.Alex
Цитата(jcxz @ May 19 2015, 10:38) *
В этом случае отрисовка в двух кадрах кадровой развёртки чётко бы виделась как постоянный излом на графике. Этого не было.

Постоянного быть не может, потому что это проявляется редко и всё время в разных (случайных) местах экрана.
Чтобы это видеть нужно чтобы было большое изображение (у меня фуллХД), и чтобы ваша кардиограмма очень быстро ползла.

Цитата(jcxz @ May 19 2015, 10:38) *
Т.е. - рисовать полностью видеостраницу в памяти, а затем одной BitBlt переносить из неё в видеобуфер видеокарты.
Видеокарта не может знать, а WinAPI вполне знает о Вашем вызове BitBlt и вполне может его синхронизировать с кадровой развёрткой.

Я так и делаю. Я ж говорил:: сначала SetDIBits() делает из обычного буфера HBITMAP, который затем копируется в CDC (.SelectObject()), и уж только тогда в OnPaint() происходит dc.BitBlt().
jcxz
Цитата(Dr.Alex @ May 19 2015, 15:21) *
Постоянного быть не может, потому что это проявляется редко и всё время в разных (случайных) местах экрана.
Чтобы это видеть нужно чтобы было большое изображение (у меня фуллХД), и чтобы ваша кардиограмма очень быстро ползла.

Берём картинку движущуюся с постоянной скоростью (N пикселей за один кадр развёртки) по экрану по горизонтали.
Примем: луч рисует экран сверху вниз, BitBlt заполняет в том-же направлении, картинка движется слева-направо.
Если BitBlt вдруг начнёт копирование этой картинки в видеобуфер во время прорисовки кадра лучом, то в какой-то момент
вывод BitBlt обгонит текущую позицию луча. Соответственно - на экране получится кадр состоящий в верхней части из старой картинки, в нижней - из новой.
Новая часть будет сдвинута относительно старой на N пикселей.
Следующий BitBlt если наложится на след. ход луча - опять будет такой-же результат но N пикселей правее.
Если частота вызовов BitBlt будет примерно равна частоте кадровой развёртки или выше её, то и получим описанный мной эффект - по экрану слева -направо будет ехать картинка сдвинутая на некоторой высоте.
В зависимости от разности частот вызовов BitBlt и кадровой место сдвига будет ехать вверх по картинке или вниз. Вот даже нарисовать этот вариант сподобился в Паинте wink.gif
Нажмите для просмотра прикрепленного файла
Если частота вызовов BitBlt будет ниже кадровой примерно в 2 и более раз - получим дёрганье верхней-нижней частей картинки.

Цитата(Dr.Alex @ May 19 2015, 15:21) *
Я так и делаю. Я ж говорил:: сначала SetDIBits() делает из обычного буфера HBITMAP, который затем копируется в CDC (.SelectObject()), и уж только тогда в OnPaint() происходит dc.BitBlt().

Ну и ладушки laughing.gif
Dr.Alex
Цитата(jcxz @ May 19 2015, 16:34) *
Если частота вызовов BitBlt будет примерно равна частоте кадровой развёртки или выше её, то и получим описанный мной эффект - по экрану слева -направо будет ехать картинка сдвинутая на некоторой высоте.

Чего-то вы не догоняете. Соотношение частот вообще ни на что не влияет. А важна лишь длительность выполнения BitBlt().
Если допустим она выполняется мгновенно, то проблем нет. В любом случае выполнится либо до, либо после.
А если выполняется скажем 1 мс, то у нас есть 1/16.6666 вероятности (монитор 60 Гц) что её выполнение разделится на "до" и "после".
Если к примеру выводим по таймеру, а его точность в винде это несколько миллисекунд в лучшем случае, то понятно что разрыв происходит СЛУЧАЙНО и НЕ ВСЕГДА, и ваша идеализированная картинка не может иметь места.
jcxz
Цитата(Dr.Alex @ May 19 2015, 20:06) *
Чего-то вы не догоняете. Соотношение частот вообще ни на что не влияет. А важна лишь длительность выполнения BitBlt().

ну да lol.gif

Цитата(Dr.Alex @ May 19 2015, 20:06) *
А если выполняется скажем 1 мс, то у нас есть 1/16.6666 вероятности (монитор 60 Гц) что её выполнение разделится на "до" и "после".

Интересно - а почему Вы считаете, что рисование кадра происходит мгновенно???? И потом 16.6666мсек монитор ничего не делает?
Ведь только в этом случае выводимая картинка не будет разделена на две части.
Вообще-то как раз большую часть времени монитор находится в режиме рисования кадра и только очень короткое время - в состоянии кадрового импульса (или импульса кадрового гашения).
Так что вывод картинки с вероятностью около 99% попадёт на время рисования кадра лучом.

Быстро происходит только копирование из теневого буфера в видимый буфер в схеме двойной буферизации.
Менее процента времени - копирование, более 99% - рисование лучом видимой части буфера.
В такой схеме не будет артефактов искажения картинки. Именно поэтому ещё в первом своём сообщении я и предположил, что сейчас WinAPI-ный BitBlt уже использует
двойную буферизацию.
Если-бы BitBlt писал прямо в видимую часть видеобуфера, то был бы артефакт со сдвигом картинки при её движении.
Dr.Alex
Повторяю, от соотношения частот ничего не зависит.

Цитата(jcxz @ May 19 2015, 18:34) *
Интересно - а почему Вы считаете, что рисование кадра происходит мгновенно????

Не надо приписывать мне глупостей.
Никого не волнует, чем занимается монитор, а при рассуждениях об "импульсе гашения" у меня вообще глаза на лоб лезут. :-))))))))))
Волнует, что делает видеокарта (а синхронизация с монитором это её личное дело, и нет сомнений что там всё тип-топ).
Если во время выполнения BitBlt() драйвер видеокарты решит флипнуть буфер, то и произойдёт этот артефакт.
Под синхронизацией и подразумевается, что сразу после флипа мне должен прийти месседж, и у меня тогда будет гарантированных 17 мс на выполнения BitBlt() до следующего флипа.

Проверил под Вин7, но на другом компе. Как ни странно, артефакта не заметил.
А на моём XP-64 он есть. Экзешник один и тот же.
Вот так.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.