Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenGL flickering/damaged with window resize and DWM active

I have a wxWidgets application that has a number of child opengl windows. I'm using my own GL canvas class, not the wx one. The windows share their OpenGL context. I don't think the fact it is wxwidgets is really relevant here.

The opengl windows are children of a windows that are siblings of one another, contained within a tab control. Kind of an MDI style interface, but it is not an MDI window.. Each one can be individually resized. All works lovely unless Aero is enabled and the DWM is active.

Resizing any window (not even the opengl ones) causes all of the opengl windows to flicker occasionally with a stale backing-store view that contains whatever rubbish has been on the screen at that point that is not opengl. This ONLY happens with Aero enabled.

I'm pretty certain that this is the DWM not actually having the opengl contents on its drawing surface backing store and the window not being repainted at the right moment.

I've tried so many things to get round this, I do have a solution but it is not very nice and involves reading the framebuffer with glReadPixels into a DIB and then blitting it to the paint DC in my onPaint routine. This workaround is only enabled if DWM is active but I'd rather not have to do this at all as it hurts performance slightly (but not too bad on a capable system - the scenes are relatively simple 3d graphs). Also mixing GDI and opengl is not recommended but this approach works, surprisingly. I can live with it for now but I'd rather not have to. I still have to do this in WM_PRINT if I want to take a screenshot of the child window anyway, I don't see a way around that.

Does anyone know of a better solution to this?

Before anyone asks I definitely do the following:

  • Window class has CS_OWNDC
  • WM_ERASEBACKGROUND does nothing and returns TRUE.
  • Double Buffering is enabled.
  • Windows have the WS_CLIPSIBLINGS and WS_CLIPCHILDREN window styles.
  • In my resize event handler I immediately repaint the window.

I've tried:

  • Setting PFD_SUPPORT_COMPOSITION in the pixel format descriptor.
  • Not using a wxPaintDC in the paint handler and calling ::ValidateRect(hwnd, NULL) instead.
  • Handling WM_NCPAINT and excluding the client area
  • Disabling NC paint via the DWM API
  • Excluding the client area in the paint event
  • Calling glFlush and/or glFinish before and after the buffer swap.
  • Invalidating the window at every paint event (as a test!) - still flickers!
  • Not using a shared GL context.
  • Disabling double buffering.
  • Writing to GL_FRONT_AND_BACK

Disabling DWM is not an option.

And as far as I am aware this is even a problem if you are using Direct3D instead on OpenGL, though I have not tested this as it represents a lot of work.

like image 433
Pete Avatar asked May 16 '12 09:05

Pete


2 Answers

This is a longshot, but I just solved exactly this same problem myself.

The longshot part comes in because we're doing owner draw of the outline of a captionless group box that surrounds our OpenGL window (i.e., to make a nice little border), and that may not describe your case.

What we found caused the problem was this:

We had been using a RoundRect() call (with a HOLLOW_BRUSH) to draw the outline of the group box. Changing it to a MoveToEx() and LineTo() calls to ensure JUST the lines are drawn and nothing gets done inside the group box kept the GDI from trying to unexpectedly repaint the whole content of the control. It's possible there's a difference in invalidation logic (or we had a bug somehow in loading the intended hollow brush). We're still investigating.

-Noel

like image 119
NoelC Avatar answered Dec 22 '22 06:12

NoelC


My app has only a single OpenGL window (the main window) but I ran into some nasty DWM tearing issues on window resize and I wonder if one of the solutions may work for you.

First of all, I found that during window resize there are at least two different bad guys who want to "help" you by modifying your client area before you have a chance to update the window yourself, creating flicker.

The first bad guy dates back to a XP/Vista/7 BitBlt inside the SetWindowPos() that Windows does internally during window resize, and can be eliminated with a trick involving intercepting WM_NCCALCSIZE or another trick involving intercepting WM_WINDOWPOSCHANGING.

In Windows 8/10 we still have that problem but we have a new bad guy, the Aero DWM.exe window manager, who will do his own different kind of BitBlt when he thinks you are "behind" updating the screen.

I suspect that the rubbish pixels you are seeing might actually be an intentional and very very poor attempt by DWM to fill in something "acceptable" while it waits for you to draw. I discovered that DWM extends the edge pixels of old client area data when it blits the new client area, which is insane.

Unfortunately, I don't know of any 100% solution to prevent DWM from doing this, but I do have a timing hack that greatly reduces the frequency of it.

For source code to the WM_NCCALCSIZE/WM_WINDOWPOSCHANGING hack as well as the DWM timing hack, please see:

How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?

like image 22
Louis Semprini Avatar answered Dec 22 '22 05:12

Louis Semprini