The Coding Flow

Code Clean Or Die Tryin'

Double Buffering in SWT

To draw the contents of a custom widget in SWT, you have to implement a PaintListener. The paint method is the only place where you have access to the widget’s GC (aka “Graphic Context”), which provides the actual drawing methods.Paint events are handled in the Display thread. This means, the UI is not responding while you are drawing. If you do complex calculations while drawing, this results in flickering.

To prevent this flickering and to keep the UI responsive, the usual approach is to use double buffering: you use two buffers, one is used for drawing and one is visible. When you are finished with drawing a complete frame, you swap the two buffers: the buffer for drawing is now visible and vice versa.

In SWT, you don’t have an explicit buffer you are working on, but you have the GC which provides the methods to manipulate the contents of the buffer. You get the GC instance for your widget visible on the screen as part of the PaintEvent that is provided to the paint callback method. To obtain an independent GC for drawing, you have to do a little trick: create an Image with the size of your widget. For this Image, you can create a GC instance which is usable independently of the UI thread.

Putting all pieces together, you use two Image instances which are used alternating for drawing and showing. The paint callback method just draws the one image that is currently used for showing to the GC of the widget.

SWT snippet 48 shows a basic version of this approach. For Vex, I started to implement a more gerneral version, applicable to Scrollable instances, that uses a dedicated rendering thread for drawing: the DoubleBufferedRenderer.

DoubleBufferedRenderer

The DoubleBufferedRenderer handles the paint events for an instance of Scrollable using double buffering as described above. It allows you to schedule a list of IRenderStep instances, a simple functional interface to implement the distinct steps of your drawing logic, that depend on access to the GC.

Internally, the execution of the render steps happens in a dedicated rendering thread. When all steps are executed, a redraw of the widget is triggered. The finished Image is then used in the paint callback method until a new image is ready.

Usage

Example: When the control is resized, we have to layout the content and paint it. Therefor we schedule two IRenderStep instances:

renderer.schedule(layoutContent(), paintContent());

To make it more readible, I put the creation of the IRenderStep into factory methods, which return an anonymous implementation:

private IRenderStep layoutContent() {
   return new IRenderStep() {
     public void render(final Graphics graphics) {
       rootBox.layout(graphics);
       cursor.reconcile(graphics);
       updateVerticalBar();
    }
  };
}

The anonymous class looks cumbersome. In Java 8 this can be expressed in a much more elegant way, but unfortunately the Eclipse platform is still on Java 6.

For more examples of how DoubleBufferedRenderer can be used, you should have a look at the implementation of BoxWidget.

Outlook

This all is still work in progress. For example the schedule method does not queue the lists of IRenderStep instances, it just knows about the currently executed list of steps and the following list of steps. Each call to schedule overwrites the latter.

Also the usage of threads is still very ineffective. If you have multiple widgets, each using its own instance of DoubleBufferedRenderer, each one will run its own thread. Using a thread pool or more sophisticated techniques will help here.

If you still want to give it a try, there is one dependency to Vex that you have to be aware of: IRenderStep provides an instance of Graphics, an abstraction of the graphics framework in use. For use outside of Vex, you can just replace this with an instance of GC.