Chandler, WxWidgets and Flicker
Chandler GUI Frameworks Engineer, OSA Foundation
07 September 2005
Reanimated this document to track improvements to wxWidgets composited window support.
The current state of the code is wx-ready to be rolled up as a patch and documented.
This document is jumbled and needs some cleanup.
BTW: should wx support MS layered windows and well as composited windows?
Chandler has substantial flicker problems.
Chandler is currently targeted for 3 platforms: Windows, MacOS X, and Linux. The current Windows version flickers the most, the MacOS X version flickers less and the Linux version not at all.
Most, if not all of the flicker can be attributed to specific implementation details within wxWidgets.
Flicker is often not considered a problem in applications that have either simple document views and/or static UIs; however, applications that have tabbed views, complex views with sub-panes, dynamically viewable content during window resizing will exhibit significant flicker. These applications fight an uphill battle for user acceptance.
This document discusses the sources of flicker within the Chandler UI and within wxWidgets, the underlying GUI framework, and the efforts underway to correct it.
Proposal to improve wxWidgets lack of support for composited windows
Task to write proposal on fixing wxWidgets DOSRB
- Existing support
- Windows: native support inaccessible and off
- Mac: native support inaccessible and on
- GTK: native support inaccessible and on
- common: nothing
- generic: nothing
- wxWidgets should:
- provide a generic platform-neutral set of APIs
- support whatever native double-buffering support is available
- allow for per-window selection of double-buffering state
- a toplevel window class should be, whenever possible, gracefully handle a failure when allocating a native compositing buffer
- code features:
- coalesce existing platform-specific variables and flags into wxTopLevelWindowBase
- implement wxTopLevelWindowBase::IsComposited
- implement wxTopLevelWindowBase::PreferComposited( bool required, bool makeComposited )
- modify existing top-level-window ::Create routines to conform to (use) the new APIs
- in platform-specific wxTopLevelWindow::CreateWindow:
- check for wxTopLevelWindow::IsComposited
- allocate window as appropriate
- check for rare errors
Causes of Flicker
Flicker is usually caused by some combination of the following conditions:
- a) performing non-blitted graphic operations to screen device bitmaps
- b) failure to aggregate (retain) drawing commands when drawing complex view components
- c) usage of UI components that "aggressively" manage their view state
- d) poor discipline when using window invalidation
- e) performing on-screen erase operations
- f) failure to flush results of retained graphic operations in a timely fashion
- g) inadvertantly invoking widget state management APIs at "inopportune" execution points
- some examples:
- invalidation of screen real estate from within a window redraw operation (Update - MacOS; OnPaint - Windows; ???? - Linux)
- activate/deactivate widget that induces an immediate redraw
- show/hide widget of a child control during a parent widget state change, for example, wxNotebook page changes
These conditions can overlap (and often do).
How To Avoid Flicker
Here are some technique commonly employed to minimize and/or eliminate flicker. The first of these (double-buffering) is essential.
- a) perform window update/paint operations indirectly via a blit operation from a dedicated off-screen raster buffer of identical color mapping, geometry characteristics and image depth. The buffer is typically implemented as a DIB (Windows), a GWorld (MacOS Classic) or a CGContext (MacOS X). Many names are given to this raster buffer (backing store, double buffer, refresh buffer, etc.); in this discussion I'll refer to it as a dedicated off-screen raster buffer (DOSRB).
- b) avoid widget state changes from within window update (OnPaint) and activation (OnActivate) routines
- c) allow drawing operations to accumulate without explicit drawing whenever possible
- d) within mouse tracking loops, explicitly flush the graphics context/device pipeline at the end of each mouse change response
- e) suppress responses to explicit erase events (Windows-specific)
Flicker, DOSRBs and and wxWidgets
Windows XP and MacOS X support native DOSRB management and nearly transparent retained-mode support for native graphics operations (GDI for Windows; QuickDraw/Quartz for MacOS). Additionally, the support is usually well-integrated with the various graphics hardware (ATI, nVidia, etc.) and the associated drivers. Such is the theory, anyways. It is true enough in practice to assert that taking advantage of available native DOSRB support is an imperative for an application framework.
Currently, wxWidgets supports only MacOS X DOSRBs. I hacked XP DOSRB support into OSAF's wxWidgets Windows DLL, and observed a significant reduction in flicker for parent window resizing, child splitter window drags and sub-pane swaps (UI view changes). The result brought Chander-Windows up to par with the MacOS version with respect to flicker.
The current wxWidgets implementation relies on explicit usage and maintenance of offscreen buffering objects (the wxDC class family) and provides relatively weak support for explicit DOSRB management. wxMemoryDC, wxBufferedDC, wxPaintDC and wxClientDC can be used, but are clumsy for any number of reasons:
- requires wx developer to implement platform-specific window compositing support
- no hooks in base wxWindow___ classes
- inability to share raster buffers between wxDC instances
- failure to integrate with native DOSRB
The following wxWidget used by Chandler classes have significant flickering problems:
- wxWindowMSW, wxSplitterWindow, wxNotebook
Apple - QD65 - QDFlushPortBuffer?
Catch22 - Flicker-Free Drawing
GameDev - flicker chat
MSDN - CreateWindowEx?
MSDN - offscreen DCs