Требуется обновление браузера.

Баг: мерцание картинки в окне приложения при воспроизведении музыки в другом приложении


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

Debug


К счастью, я примерно в это же время изучал особенности синхронизации процессов и производительность различных таймеров Windows. Разгадка оказалась проста. Используемый в разрабатываемом приложении таймер (из какого-то там фреймворка для работы с ГИП), синхронизирующий частоту обновления картинки, судя по всему, базировался на мультимедийном таймере Windows. Настроен таймер был (по умолчанию) на минимальную паузу между кадрами. Базовая частота мультимедийного таймера позволяла тратить достаточно много (по меркам таких событий) времени на формирование кадра: к моменту, когда необходимо было начать рисовать новый кадр, текущий был полностью сформирован.

Когда пользователь включал музыку, плеер увеличивал частоту обновления значений мультимедийного таймера. Это распространённое поведение программ, предъявляющих высокие требования к точности синхронизации. Подобным образом ведут себя различные мультимедийные проигрыватели, веб-браузеры и игры. Программа выставляет желаемую производительность таймера, вызвав функцию
timeBeginPeriod
(или её обёртку). После того, как надобность в высокой точности отпала, программа вызывает функцию
timeEndPeriod
(по крайней мере - должна).

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

Повторюсь: "разгон" не ускоряет течение времени, а уменьшает шаг квантования возвращаемой величины. Иначе говоря, таймер начинает чаще обновлять свои значения. В этом обстоятельстве нет ничего ужасного, если алгоритм настроен реагировать на истечение определённого количества времени (Δt), а не просто на событие "время изменилось". Чтобы было совсем понятно, приведу синтетический пример для иллюстрации.

Реальное времяПоказания таймераНовый кадрПоказания таймера после "разгона"Новый кадр
1.001.00+1.00+
1.251.001.00
1.501.001.50+
1.751.001.50
2.002.00+2.00+
2.252.002.00
2.502.002.50+
2.752.002.50
3.003.00+3.00+

Отслеживать "разгон"/"торможение" таймера можно при помощи анализа среднего арифметического уровня квантования возвращаемого значения
timeGetTime
за небольшой период времени. Что я и сделал, чтобы убедиться в верности своей гипотезы. Производить подобные наблюдения можно при помощи отдельной программки, так как все приложения будут получать время с одинаковой точностью.

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

Fix


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

Запись опубликована в категориях:

Отладка Алгоритмы и аспекты  
 

Комментарии

Инкогнито
  Загружаем captcha