Мерцание / повреждение OpenGL при изменении размера окна и активном DWM
У меня есть приложение wxWidgets, которое имеет несколько дочерних окон Open GL. Я использую свой собственный класс холста GL, а не WX. Окна имеют общий контекст Open GL. Я не думаю, что факт, что это wxwidgets, действительно уместен здесь.
Окна opengl являются дочерними элементами окон, которые являются братьями и сестрами друг друга, содержащиеся в элементе управления вкладки. Вид интерфейса в стиле MDI, но это не окно MDI. Каждый из них может быть индивидуально изменен. Все работает прекрасно, если Aero не включен и DWM активен.
Изменение размера любого окна (даже не opengl) приводит к тому, что все окна opengl время от времени мерцают с устаревшим видом хранилища резервных копий, который содержит любой мусор, который был на экране в тот момент, который не является opengl. Это ТОЛЬКО происходит при включенном Aero.
Я почти уверен, что это DWM, на самом деле не имеющий содержимого opengl в хранилище резервных копий поверхности окна, и окно не перерисовывается в нужный момент.
Я пробовал так много вещей, чтобы обойти это, у меня есть решение, но оно не очень хорошее и включает в себя чтение кадрового буфера с glReadPixels в DIB и затем перетаскивание его на DC рисования в моей подпрограмме onPaint. Этот обходной путь активен только в том случае, если DWM активен, но я бы предпочел не делать этого вообще, так как он немного снижает производительность (но не слишком плохо для работоспособной системы - сцены представляют собой относительно простые трехмерные графики). Также не рекомендуется смешивать GDI и opengl, но этот подход работает на удивление. Я могу жить с этим сейчас, но я бы не хотел. Я все еще должен сделать это в WM_PRINT, если я все равно хочу сделать снимок экрана дочернего окна, я не вижу способа обойти это.
Кто-нибудь знает лучшее решение этого?
Прежде чем кто-либо спросит, я определенно сделаю следующее:
- Класс окна имеет CS_OWNDC
- WM_ERASEBACKGROUND ничего не делает и возвращает TRUE.
- Двойная буферизация включена.
- В Windows есть стили окон WS_CLIPSIBLINGS и WS_CLIPCHILDREN.
- В моем обработчике изменения размера я немедленно перекрашиваю окно.
Я пробовал:
- Установка PFD_SUPPORT_COMPOSITION в дескрипторе формата пикселей.
- Не использовать wxPaintDC в обработчике рисования и вызывать::ValidateRect(hwnd, NULL).
- Обработка WM_NCPAINT и исключение клиентской области
- Отключение NC-краски через DWM API
- Исключая клиентскую область в событии рисования
- Вызов glFlush и / или glFinish до и после замены буфера.
- Делать окно недействительным при каждом событии рисования (как тест!) - все еще мерцает!
- Не используется общий контекст GL.
- Отключение двойной буферизации.
- Запись в GL_FRONT_AND_BACK
Отключение DWM не вариант.
И, насколько мне известно, это даже проблема, если вы используете Direct3D вместо этого в Open GL, хотя я не проверял это, поскольку это представляет собой большую работу.
3 ответа
Это длинный пример, но я сам решил именно эту проблему.
Пришла длинная часть, потому что мы выполняем отрисовку владельцем контура группового блока без заголовков, который окружает наше окно OpenGL (то есть, чтобы сделать красивую небольшую границу), и это может не описывать ваш случай.
То, что мы обнаружили, вызвало проблему:
Мы использовали вызов RoundRect() (с HOLLOW_BRUSH), чтобы нарисовать схему группового блока. Изменив его на вызовы MoveToEx() и LineTo(), чтобы ТОЛЬКО нарисовать линии и ничего не делать внутри группового блока, не позволяли GDI неожиданно перерисовать все содержимое элемента управления. Возможно, есть разница в логике аннулирования (или у нас была ошибка при загрузке предполагаемой полой кисти). Мы все еще расследуем.
-Ноэль
У моего приложения есть только одно окно OpenGL (главное окно), но я столкнулся с некоторыми неприятными проблемами с изменением размера DWM при изменении размера окна, и мне интересно, может ли одно из решений работать на вас.
Прежде всего, я обнаружил, что во время изменения размера окна есть как минимум два разных плохих парня, которые хотят "помочь" вам, изменив вашу клиентскую область, прежде чем вы сможете обновить окно самостоятельно, создавая мерцание.
Первый плохой парень восходит к XP/Vista/7 BitBlt
внутри SetWindowPos()
что Windows делает внутренне во время изменения размера окна, и может быть устранено с помощью хитрости перехвата WM_NCCALCSIZE
или другой трюк с перехватом WM_WINDOWPOSCHANGING
,
В Windows 8/10 у нас все еще есть эта проблема, но у нас есть новый плохой парень, оконный менеджер Aero DWM.exe, который будет делать свой собственный другой вид BitBlt
когда он думает, что вы "позади" обновления экрана.
Я подозреваю, что мусорные пиксели, которые вы видите, на самом деле могут быть преднамеренной и очень-очень плохой попыткой DWM заполнить что-то "приемлемое", пока оно ждет вас. Я обнаружил, что DWM расширяет граничные пиксели старых данных клиентской области, когда он закрывает новую клиентскую область, что безумие.
К сожалению, я не знаю ни одного 100% -го решения, которое бы препятствовало тому, чтобы DWM делал это, но у меня действительно есть взлом синхронизации, который значительно уменьшает частоту этого.
Для исходного кода на WM_NCCALCSIZE
/WM_WINDOWPOSCHANGING
Взломать, а также взломать DWM, пожалуйста, смотрите:
Хм, может быть, вы столкнулись с той же проблемой: если вы используете "новый" MFC, он создаст и приложение с вкладками и Window Spliter.
Сплиттер имеет некоторую логику (я предполагаю, что где-то вокруг прозрачного окна и рисуя линии XOR для разделения), которая вызывает такое поведение. Удалите сплиттер, чтобы подтвердить, что он решил вашу проблему. Если вам нужна функция разделения - установите другой разделитель.
Также вкладки позволяют закреплять и снова разбивать окна, что имеет ту же проблему - удалить / заменить.
Удачи Игорь