|
|
  |
Синхронизация с монитором/видеокартой при выводе изображения под Виндой |
|
|
|
May 14 2015, 18:10
|
Профессионал
    
Группа: Свой
Сообщений: 1 386
Регистрация: 5-04-05
Из: моська, RF
Пользователь №: 3 863

|
Если тупо выводить 25 кадров в секунду по таймеру (или по мере поступления этих кадров из источника), то возникает известный артефакт (небольшой, но всё-таки). Если момент отрисовки виндой нового кадра придётся на момент, когда видеокарта решит выплюнуть очередной кадр на монитор, то получится что часть кадра моего видео будет содержать прошлый кадр, а часть новый. При быстрых движениях в сюжете это бывает заметно. Можно ли как-то синхронизироваться? Конечно, если через директшоу работать, то там всё это решено, ну а если сам выводишь?
|
|
|
|
Guest_TSerg_*
|
May 14 2015, 18:20
|
Guests

|
Cмотря через что выводить. Обычно buffered помогает.
|
|
|
|
|
May 14 2015, 18:27
|
Профессионал
    
Группа: Свой
Сообщений: 1 386
Регистрация: 5-04-05
Из: моська, RF
Пользователь №: 3 863

|
Цитата(TSerg @ May 14 2015, 21:20)  Cмотря через что выводить. Обычно buffered помогает. Проясните что имеется в виду. У меня всё тупо:: простой диалог на MFC, в функции OnPaint() вышвыриваем кадр функцией BitBlt().
|
|
|
|
|
May 17 2015, 11:00
|
Частый гость
 
Группа: Участник
Сообщений: 140
Регистрация: 2-01-08
Пользователь №: 33 768

|
Цитата(Dr.Alex @ May 14 2015, 22:27)  простой диалог на MFC, в функции OnPaint() вышвыриваем кадр функцией BitBlt(). В любой относительно современной винде (Vista и выше) весь gdi рисуется через т.н. compositor. Все что вы рисуете из программы, не попадает напрямую в фреймбуфер, а сначала в виде команд или битмапов подается на вход compositor'а, который уже сводит итоговую картинку (с двойной буферизацией или без — это зависит от множества факторов), стараясь при этом избегать фликера. В сухом остатке здесь то, что отрисовкой вы по факту управлять не можете, и между подачей команды BitBlt и появлением соответствующих пикселов на экране проходит некий слабопредсказуемый промежуток времени. Если вам нужен полный контроль, то выводите графику через DirectX или более низкоуровневые API (Mantle, DX12 и у nvidia что-то еще было). Да, еще. BitBlt сколь я себе помню — медленный. StretchDiBits был побыстрее, но я такой графикой развлекался последний раз еще в институте (>15 лет назад), так что могу и путать.
Сообщение отредактировал slanted - May 17 2015, 11:03
|
|
|
|
|
May 17 2015, 18:43
|
Профессионал
    
Группа: Свой
Сообщений: 1 386
Регистрация: 5-04-05
Из: моська, RF
Пользователь №: 3 863

|
Цитата(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.
|
|
|
|
|
May 18 2015, 19:24
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Dr.Alex @ May 15 2015, 00:10)  Если тупо выводить 25 кадров в секунду по таймеру (или по мере поступления этих кадров из источника), то возникает известный артефакт (небольшой, но всё-таки). Это не зависит от частоты обновления картинки. Цитата(Dr.Alex @ May 15 2015, 00:10)  Если момент отрисовки виндой нового кадра придётся на момент, когда видеокарта решит выплюнуть очередной кадр на монитор, то получится что часть кадра моего видео будет содержать прошлый кадр, а часть новый. При быстрых движениях в сюжете это бывает заметно. Не так давно занимался выводом быстроменяющейся картинки под виндой (осциллограмма). Обычным WinAPI (пробовал и DirectDraw - разницы в скорости - никакой). Указанного артефакта не наблюдал. Видимо современные видеокарты (в том числе и встроенные как у меня), по дефолту уже делают двойную буферизацию (копируют весь отображаемый экран из области хранения в область формирования сигнала в момент когда сигнал не формируется (кадровый импульс)). Есть конечно артефакт связанный с наложением частот кадровой развёртки и обновления экрана. Например: если картинка в каждом новом кадре смещается на один пиксел, и частота обновления картинки близка к частоте кадров, типа: 60.1 Hz и 60 Hz. Тогда картинка будет смещаться не плавно, а дёргаться с разностной частотой. Но тут уже ничего не сделаешь. Имхо.
|
|
|
|
|
May 18 2015, 20:07
|
Профессионал
    
Группа: Свой
Сообщений: 1 386
Регистрация: 5-04-05
Из: моська, RF
Пользователь №: 3 863

|
Цитата(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..
|
|
|
|
|
May 19 2015, 07:38
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(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.
|
|
|
|
|
May 19 2015, 09:21
|
Профессионал
    
Группа: Свой
Сообщений: 1 386
Регистрация: 5-04-05
Из: моська, RF
Пользователь №: 3 863

|
Цитата(jcxz @ May 19 2015, 10:38)  В этом случае отрисовка в двух кадрах кадровой развёртки чётко бы виделась как постоянный излом на графике. Этого не было. Постоянного быть не может, потому что это проявляется редко и всё время в разных (случайных) местах экрана. Чтобы это видеть нужно чтобы было большое изображение (у меня фуллХД), и чтобы ваша кардиограмма очень быстро ползла. Цитата(jcxz @ May 19 2015, 10:38)  Т.е. - рисовать полностью видеостраницу в памяти, а затем одной BitBlt переносить из неё в видеобуфер видеокарты. Видеокарта не может знать, а WinAPI вполне знает о Вашем вызове BitBlt и вполне может его синхронизировать с кадровой развёрткой. Я так и делаю. Я ж говорил:: сначала SetDIBits() делает из обычного буфера HBITMAP, который затем копируется в CDC (.SelectObject()), и уж только тогда в OnPaint() происходит dc.BitBlt().
|
|
|
|
|
May 19 2015, 13:34
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Dr.Alex @ May 19 2015, 15:21)  Постоянного быть не может, потому что это проявляется редко и всё время в разных (случайных) местах экрана. Чтобы это видеть нужно чтобы было большое изображение (у меня фуллХД), и чтобы ваша кардиограмма очень быстро ползла. Берём картинку движущуюся с постоянной скоростью (N пикселей за один кадр развёртки) по экрану по горизонтали. Примем: луч рисует экран сверху вниз, BitBlt заполняет в том-же направлении, картинка движется слева-направо. Если BitBlt вдруг начнёт копирование этой картинки в видеобуфер во время прорисовки кадра лучом, то в какой-то момент вывод BitBlt обгонит текущую позицию луча. Соответственно - на экране получится кадр состоящий в верхней части из старой картинки, в нижней - из новой. Новая часть будет сдвинута относительно старой на N пикселей. Следующий BitBlt если наложится на след. ход луча - опять будет такой-же результат но N пикселей правее. Если частота вызовов BitBlt будет примерно равна частоте кадровой развёртки или выше её, то и получим описанный мной эффект - по экрану слева -направо будет ехать картинка сдвинутая на некоторой высоте. В зависимости от разности частот вызовов BitBlt и кадровой место сдвига будет ехать вверх по картинке или вниз. Вот даже нарисовать этот вариант сподобился в Паинте   Если частота вызовов BitBlt будет ниже кадровой примерно в 2 и более раз - получим дёрганье верхней-нижней частей картинки. Цитата(Dr.Alex @ May 19 2015, 15:21)  Я так и делаю. Я ж говорил:: сначала SetDIBits() делает из обычного буфера HBITMAP, который затем копируется в CDC (.SelectObject()), и уж только тогда в OnPaint() происходит dc.BitBlt(). Ну и ладушки
|
|
|
|
|
May 19 2015, 14:06
|
Профессионал
    
Группа: Свой
Сообщений: 1 386
Регистрация: 5-04-05
Из: моська, RF
Пользователь №: 3 863

|
Цитата(jcxz @ May 19 2015, 16:34)  Если частота вызовов BitBlt будет примерно равна частоте кадровой развёртки или выше её, то и получим описанный мной эффект - по экрану слева -направо будет ехать картинка сдвинутая на некоторой высоте. Чего-то вы не догоняете. Соотношение частот вообще ни на что не влияет. А важна лишь длительность выполнения BitBlt(). Если допустим она выполняется мгновенно, то проблем нет. В любом случае выполнится либо до, либо после. А если выполняется скажем 1 мс, то у нас есть 1/16.6666 вероятности (монитор 60 Гц) что её выполнение разделится на "до" и "после". Если к примеру выводим по таймеру, а его точность в винде это несколько миллисекунд в лучшем случае, то понятно что разрыв происходит СЛУЧАЙНО и НЕ ВСЕГДА, и ваша идеализированная картинка не может иметь места.
|
|
|
|
|
May 19 2015, 15:34
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Dr.Alex @ May 19 2015, 20:06)  Чего-то вы не догоняете. Соотношение частот вообще ни на что не влияет. А важна лишь длительность выполнения BitBlt(). ну да Цитата(Dr.Alex @ May 19 2015, 20:06)  А если выполняется скажем 1 мс, то у нас есть 1/16.6666 вероятности (монитор 60 Гц) что её выполнение разделится на "до" и "после". Интересно - а почему Вы считаете, что рисование кадра происходит мгновенно???? И потом 16.6666мсек монитор ничего не делает? Ведь только в этом случае выводимая картинка не будет разделена на две части. Вообще-то как раз большую часть времени монитор находится в режиме рисования кадра и только очень короткое время - в состоянии кадрового импульса (или импульса кадрового гашения). Так что вывод картинки с вероятностью около 99% попадёт на время рисования кадра лучом. Быстро происходит только копирование из теневого буфера в видимый буфер в схеме двойной буферизации. Менее процента времени - копирование, более 99% - рисование лучом видимой части буфера. В такой схеме не будет артефактов искажения картинки. Именно поэтому ещё в первом своём сообщении я и предположил, что сейчас WinAPI-ный BitBlt уже использует двойную буферизацию. Если-бы BitBlt писал прямо в видимую часть видеобуфера, то был бы артефакт со сдвигом картинки при её движении.
|
|
|
|
|
May 19 2015, 15:48
|
Профессионал
    
Группа: Свой
Сообщений: 1 386
Регистрация: 5-04-05
Из: моська, RF
Пользователь №: 3 863

|
Повторяю, от соотношения частот ничего не зависит. Цитата(jcxz @ May 19 2015, 18:34)  Интересно - а почему Вы считаете, что рисование кадра происходит мгновенно???? Не надо приписывать мне глупостей. Никого не волнует, чем занимается монитор, а при рассуждениях об "импульсе гашения" у меня вообще глаза на лоб лезут. :-)))))))))) Волнует, что делает видеокарта (а синхронизация с монитором это её личное дело, и нет сомнений что там всё тип-топ). Если во время выполнения BitBlt() драйвер видеокарты решит флипнуть буфер, то и произойдёт этот артефакт. Под синхронизацией и подразумевается, что сразу после флипа мне должен прийти месседж, и у меня тогда будет гарантированных 17 мс на выполнения BitBlt() до следующего флипа. Проверил под Вин7, но на другом компе. Как ни странно, артефакта не заметил. А на моём XP-64 он есть. Экзешник один и тот же. Вот так.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|