2014-10-02 2 views
14

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

Подробности

анимация занимает такое же изображение, и перемещает его справа налево по экрану; количество пикселей в поперечнике определяется разницей между текущим временем и временем начала анимации и временем до конца, чтобы получить полную фракцию, которая применяется ко всей ширине окна, используя timeGetTime с 1ms resolution. Анимация рисует в цикле без обработки сообщений приложения; он вызывает метод (VCL library) Repaint, который внутренне недействителен, а затем вызывает UpdateWindow для соответствующего окна, непосредственно вызывая процедуру сообщения с помощью WM_PAINT. В реализации VCL обработчика краски используется BeginBufferedPaint. Картина сама по себе является двойной буферизацией.

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

При просмотре анимации отчетливо видна tearing. Это происходит в Windows Vista, 7 и 8.1 на нескольких системах с различными видеокартами.

Мой подход к управлению этим состоял в том, чтобы уменьшить скорость его рисования или попытаться подождать VSync, прежде чем рисовать снова. Это может быть неправильный подход, поэтому ответ на этот вопрос может быть «Сделать что-то еще полностью: X». Если это так, здорово :)

(То, что я бы очень хотел, это способ попросить DWM сочинить/использовать только полностью окрашенные кадры для этого конкретного окна.)

Я попытался следующие подходы , ни один из которых не удаляет все видимые разрывы. Поэтому возникает вопрос: Возможно ли избежать разрыва при использовании композиции DWM, и если да, то как?

подходы пытались:

  • Получение частоты обновления монитора через GetDeviceCaps(Application.MainForm.Handle, VREFRESH); сон за 1/частота обновления миллисекунд. Немного улучшена окраска как можно быстрее, но может быть желаемое за действительное. Восприимчивость немного менее плавная. (Tweaks: нормальный Sleep и высокое разрешение спин-подождите использование timeGetTime.)

  • Использование DwmSetPresentParameters, чтобы попытаться ограничить обновление до той же скорости, при которой код втягивает. (Вариации: много буферов (cBuffer = 8) (без видимого эффекта), указав скорость источника обновления монитора/1 и спящий режим с использованием вышеуказанного кода (так же, как и просто попытка спящего подхода), указав обновление на кадр 1, 10 и т. Д. (Без видимого эффекта), изменение покрытия исходного кадра (отсутствие видимого эффекта.)

  • Использование DwmGetCompositionTimingInfo в различных формах:

      • Хотя cFramesPending> 0, спина;
      • Получить cFrame (кадр состоит) и спин в то время как это число не меняется;
      • Получить cFrameDisplayed и спина в то время как это не меняет;
      • Расчет времени для сна, чтобы путем добавления qpcVBlank + qpcRefreshPeriod, а затем в то время как QueryPerformanceCounter возвращает время меньше, чем это, спина
  • Все эти подходы также варьировалась от живописи , затем спиннинг/сон, прежде чем снова рисовать; или наоборот: спать, а затем рисовать.

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

Совет оценил :)

+1

GDI - старый и хрустящий, а не гладкая анимация. Может быть, попробовать Direct2D? –

+1

@JonathanPotter Хорошая точка. Одним из ограничений, с которыми я столкнулся, является не использование DX любого типа - одна из причин - количество платформ и отсутствие требований, конечный код должен работать. (Не все являются современными Windows.) Возможно, вспомогательный DX-код, только когда Aero существует ...? Тем не менее, он должен будет легко переключаться на и из GDI/DX без видимых сбоев, поскольку он включает/выключает анимацию. Это возможно? –

+0

Возможно, вы могли бы написать класс вспомогательного чертежа, который использует DX, если он доступен, и в противном случае возвращается обратно в GDI. Было бы довольно редко найти систему, которая не поддерживала Direct3D, хотя и не D2D. –

ответ

2

Поскольку вы используете BitBlt, убедитесь, что DIBs 4 байта/пиксел. С 3 байтами/пикселями GDI работает медленно, а DWM работает, что может стать источником вашего разрыва. Еще одна проблема BitBlt, с которой я столкнулся, если ваш DIB несколько больше, чем вызов BitBlt call сделать неожиданно долгое время. Если вы разделите один вызов на более мелкие вызовы, а не нарисуете часть данных, это может помочь. Оба эти элемента помогли мне в моем случае, только потому, что сам BitBlt работал слишком медленно, что привело к созданию артефактов видео.

+0

На всякий случай добавление: не забывать обновлять только грязный прямоугольник (GetUpdateRect) вместо всего окна ... – Jurlie

Смежные вопросы