Не могу избавиться от дрожания при перетаскивании левой границы окна
По состоянию на 26 марта 2015 года проблема решена, см. Внизу этой страницы очень подвох.
По состоянию на 18 августа 2014 года частично решено: виновником является DWM (см. Последний комментарий).
Я создал свой собственный стиль окна, используя Win32 API. В Windows XP и Windows 7 все прошло нормально. Однако в Windows 8 происходят странные вещи: перетаскивание левой границы окна вызывает сильное дрожание в правой части, в то время как оно вообще не должно двигаться. Посмотрите на это, и вы поймете, что я имею в виду.
Когда я перетаскиваю правую границу, левая сторона не двигается, как должно быть. По общему признанию, есть некоторое мерцание, но это приемлемо. Смотрите это
я пробовал SetWindowPos()
а также (begin/End)DeferWindowPos
с несколькими флагами, но безрезультатно. Даже с SWP_NOREDRAW
и блокировка всех других картин не помогает. Ни с, ни без CS_HREDRAW
а также CS_VREDRAW
,
Конечно, я использую двойную буферизацию. Все же кажется невозможным избавиться от этого надоедливого дрожания. Я также попробовал другой драйвер для графического процессора Intel HD 4000, опять же безрезультатно. Я что-то упускаю? Это ошибка Windows 8?
Кстати, когда я включаю опцию "Показывать содержимое окна при перетаскивании" (в меню "Дополнительные параметры системы"), все остальные приложения показывают то же поведение.
Любая помощь будет оценена.
Ура, Эдмонд.
PS: отключение "Показать содержимое окна при перетаскивании" не вариант, потому что это встроенная функция моего приложения.
Edit1: кажется, что d7samurai борется с той же проблемой.
Продолжение: я много чего пробовал, чтобы избавиться от дрожания, но ничего не помогает. Проблема в том, что W8 просто не делает то, что должен. Возьмите для примера SWP_NOREDRAW
флаг. Как и следовало ожидать, перекраска стороны окна, которая была перемещена, подавлена, пока все хорошо. НО.... другая сторона (окна), которая все еще действительна, эта перекрашивается! Он не только совершенно бесполезен и бесполезен, но и перекрашен с помощью джиттера! Более того, живопись в два раза медленнее по сравнению с W7 и XP. Потратив целую неделю на эту проблему, я полностью справился с W8! Это действительно POC, и парни, которые несут ответственность за эту неудачную работу, психически ненормальные. Я действительно надеюсь, что W9 сделает лучшую работу. Аминь.
ВЫ ДУМАЙТЕ:
Оказалось, что описанные выше аномалии не ограничиваются только W8. Также W7 продемонстрировал то же поведение, когда он установлен на "лучший внешний вид" или, по крайней мере, на "Включить композицию рабочего стола" вместе с "Использовать визуальные стили на окнах и кнопках". Тогда я подумал, что можно обмануть окна, вообще не используя функцию SetWindowPos, а отправив ShowWindow( hWnd, SW_MAXIMIZE )
командовать и перехватывать WM_GETMINMAXINFO
MSG, где я указываю желаемый размер и положение. Угадай, что? У меня все еще есть дрожание. Аааааа!!!
Что делать дальше? Есть ли возможность перехватить картину на более низком уровне (крючки?), Чтобы перерисовать окно достойным образом?
Обновление дд 03-26-2015, очень подвох
Эврика! Я наконец нашел решение проблемы. Но сначала позвольте мне объяснить, что происходит и почему перетаскивание сопровождается дрожанием. Предположим, вы хотите расширить левую часть окна. В отличие от расширения правой стороны, это делается в два этапа. Сначала содержимое окна с его исходным размером перемещается влево. После того, как это было сделано, содержимое расширяется до такой степени, что новая правая сторона совпадает с предыдущей правой стороной. Короче говоря, это комбинация "движения", за которым следует "изменение размера", и это вызывает уродливое дрожание. Подобные вещи случаются, когда вы перетаскиваете верхнюю часть окна. В Windows 8.1 или 10 вы ничего не можете с этим поделать. Я также попробовал новые функции DWM (BufferedPaintInit
, BeginBufferedPaint
, EndBufferedPaint
, BufferedPaintUnInit
К сожалению, безрезультатно. Также SetWindowPlacement()
производит дрожание. По-видимому, все эти функции приводят к одному и тому же преступнику, который портит изменение размера. Затем я рассуждал, что когда вы создаете новое окно и отображаете его с новым размером, оно совсем не дрожит. Скорее очевидно, я бы сказал. Поэтому повторное создание нового окна и последующее уничтожение предыдущего может быть решением, но, конечно, это не очень эффективно. Играя с этой идеей, я случайно обнаружил, что отображение скрытого окна с новым / другим размером, за которым следует скрытие предыдущего окна, также не отображает дрожание. Эта процедура намного эффективнее, чем последовательность создания / уничтожения. Итак, в начале моей программы я только что создал два окна, первое скрытое, а второе видимое. Приведенный ниже фрагмент кода должен более подробно объяснить процедуру изменения размера без дрожания:
........
i = prm->hpi; // get current index
h1 = prm->parent[i]; // get current handle
flags = SWP_NOCOPYBITS;
flags |= SWP_NOSENDCHANGING;
// if DWM tries to make a mess of it:
// DWM enabled top border right border
if( prm->parent[1] && ( rgn == TBE || rgn == LBE ) )
{
i = i + 1 & 1; // get new index
prm->hpi = i; // save new index
h2 = prm->parent[i]; // get new handle
prm->hParent = h2; // set as current one
flags |= SWP_NOREDRAW; // bypass message pump
flags |= SWP_SHOWWINDOW; // make h2 visible
SetWindowPos( h2, HWND_TOP, px, py, cx, cy, flags );
PaintParent( h2 ); // paint it now
DwmFlush(); // wait till finished
ShowWindow( h1, SW_HIDE ); // make h1 invisible
}
// DWM disabled or dragging the right or bottom border:
else SetWindowPos( h1, HWND_TOP, px, py, cx, cy, flags );
Еще одна вещь: использовать DwmFlush()
чтобы убедиться, что все рисование нового окна закончено, а затем скрыть другое окно, в противном случае будет видно некоторое мерцание. Конечно выше процедура немного медленнее, чем обычная. В моей системе (с процессором i5-3570K) 3,26 мс, в зависимости от размера окна и его содержимого. Но, по крайней мере, этот ужасный джиттер исчез.
Я понимаю, что это довольно грязный трюк, поэтому, если кто-то знает более точное решение, пожалуйста, дайте нам знать.
3 ответа
Похоже, под Windows 8.1 и выше, вы должны сотрудничать с DWM, а не обходить его.
Это означает, что вы должны узнать график, когда композиция происходит через DwmGetCompositionTimingInfo
и убедитесь, что вы помещаете события рендеринга в очередь в соответствующие моменты. Вы также можете обнаружить, что вам необходимо повторно сделать недействительной и перекрасить окно сразу после события WM_SIZING, или даже использовать собственный таймер для рисования чаще, чем отправляются события WM_SIZING. Например, если DWM осуществляет компоновку со скоростью 50 кадров в секунду, но вы получаете только события WM_Sizing в секунду, то, если вы используете свой собственный таймер с GetWindowPos
Вы можете рисовать чаще, чем если бы вы полагались на WM_SIZING.
Вы можете найти, что разумное использование DwmFlush
Сразу после покраски также может помочь уменьшить этот джиттер.
Я нашел решение проблемы. Пожалуйста, ознакомьтесь с последним обновлением исходного сообщения.
Это решение с двумя окнами очень креативно! Я не думаю, что смогу применить это решение, потому что мое окно - это окно OpenGL с тяжелым состоянием, которое должно поддерживаться / реплицироваться во всех экземплярах окна.
Я думаю, что смогу дать более подробное представление о том, почему вы продолжали видеть этот джиттер, несмотря на то, что возились со всеми обычными подозреваемыми, такими как SWP_NOREDRAW
а также SWP_NOCOPYBITS
,
Я столкнулся с той же проблемой дрожания и обнаружил, что, поскольку приложения в Windows 8/10 Aero не рисуют непосредственно на экране, а рисуют в закадровых буферах, которые затем компоноваются злым оконным менеджером DWM.exe, оказывается, что DWM фактически добавляет еще один слой BitBlt
поведение поверх существующей устаревшей XP/Vista/7 BitBlt
поведение.
Блиц-поведение DWM еще более сумасшедшее, потому что они не просто копируют клиентскую область, но фактически копируют пиксели по краям вашей старой клиентской области, чтобы создать новую. К сожалению, заставить DWM не делать блиц намного сложнее, чем просто передать несколько дополнительных флагов.
Кажется, ваш трюк с двумя окнами обманывает DWM в том, что он не делает лишних бликов, но я ищу способ сделать это с одним окном.
У меня нет 100% -го решения, но я надеюсь, что приведенная выше информация поможет, и у меня есть код для реализации классной идеи Бена по рисованию времени, чтобы минимизировать шансы DWM на успех:
Наслаждайтесь!