Double buffering and rendering -> Passive rendering

Double Buffering and Passive Rendering in Java using a Timer Object

Download PassiveCircles example application
Download PassiveCircles example source code

For active rendering, please check out my guide Double Buffering and Active Rendering in Java with Swing Integration, or if needed, go back to the main double buffering and rendering tutorial page.

Double Buffering and Passive Rendering in Java using a Timer Object,
Article and code written by James Murphy.

Passive rendering in general refers to the fact that you do not know when the next render of graphics will occur. With Swing in Java, passive rendering is when the Event Dispatch Thread (EDT) controls how and when it responds to repaint requests (requests to show a new frame of graphics). With Swing for passive rendering, graphics calls are made via the paint(Graphics g), or paintComponent(Graphics g) method in a component, where draw calls in paintComponent(Graphics g) are double buffered. Passive rendering can be seen as easier to do than active rendering as it is essentially the act of creating your own JComponent that can be integrated with other Swing components that may control the application. Passive rendering has the benefit of being naturally multithreaded between the EDT that draws the graphics and your own application threads. For a quick summary, having a game declared as using passive rendering techniques with Swing is essentially a game whose graphics are represented by a Swing component.

In this tutorial I will cover some main issues I and others find for creating a small hobby game or application. These main issues and topics are the following:

Be sure to check the bottom of this page where you can view and download my entire Java program using all of the techniques I will mention. The entire code base is in one class with a lot of static classes, this was my idea of simplifying the distribution of the code and the viewing of the source online for quick reference.

Java program using passive rendering and double buffering techniques to render a bunch of circles bouncing around.
Screenshot of PassiveCircles example

Properly set the width and height of a JFrame

When you create a JFrame with a set width and height, some of that width and height goes to the title bar and the borders of the application window. In a full screen program, this does not apply as there are no decorative surroundings. To fix the window to have a drawing space within the application window that matches what you originally set the JFrame to you need to retrieve the Inset object of the JFrame and resize it according to how much space in pixels the insets take up. This is especially handy if you do not allow the resizing of your application and need a specific resolution.

frame.setVisible(true);

...

// Change width and height of window so that the available
// screen space actually corresponds to what is passed, another
// method is the Canvas object + pack()
frame.setSize(width, height);
Insets insets = frame.getInsets();
int insetWide = insets.left + insets.right;
int insetTall = insets.top + insets.bottom;
frame.setSize(frame.getWidth() + insetWide,
                    frame.getHeight() + insetTall);
                    

Another method to accomplish this is to set a preferred size (setPreferredSize) on a JPanel and then call pack().

Double buffering with the paintComponent method, and stretchable graphics

In the code example we will see shortly, we draw to an image's graphics instead of an actual Graphics object that's passed into the paintComponent method. We do this because we can use the image as our canvas to draw our application's graphics to, and then stretch the image to the entire viewable area of the JFrame in case the user resizes the application. If we didn't use this stretchable image technique and resized the frame smaller, some of our graphics would be drawn off screen, and if we resized the frame larger, our graphics would just appear in the top left with a blank area to the bottom and right, or we might actually be showing more graphics of our application than we actually want to. Stretching the graphics isn't always what is needed (some applications are better with naturally displaying more information), but it works well in a lot of cases (like games, where you do not want the user to see more than what you wanted them to). The following shows the creation of a compatible BufferedImage that we will use throughout the program's execution.

// We draw almost all graphics to this image, then stretch it over the
// entire frame.
// This allows a resize to make the game bigger, as opposed to
// just providing a larger area for the sprites to be drawn onto.
// We also are using this image's pixel coordinates as the coordinates
// of our circle sprites.
drawing = GraphicsEnvironment.getLocalGraphicsEnvironment()
    .getDefaultScreenDevice().getDefaultConfiguration()
    .createCompatibleImage(width, height);

The "compatible image", means that it's an image that has a layout and color model that can best support the display format that the application is running on. From the image, we can extract the graphics object from it, and then draw all of our graphics to it, starting with the background to erase what was previously there from the last update, and ending with our sprites and other graphical effects.

/**
 * Draws the GameData as needed
 */
 public synchronized void drawGameData(Graphics2D drawingBoard,
                                      int drawAreaWidth,
                                      int drawAreaHeight) {
    // This allows our text and graphics to be nice and smooth
    drawingBoard.setRenderingHint(
            RenderingHints.KEY_TEXT_ANTIALIASING,
            RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    drawingBoard.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

    // Always draw over the image with a blank background, so we
    // don't see the last frame's drawings! (comment this out and
    // see what happens, it's fun pressing the change color button
    // rapidly too!)
    drawingBoard.setColor(Color.LIGHT_GRAY);
    drawingBoard.fillRect(0, 0, drawAreaWidth, drawAreaHeight);


    // Creating a graphics object to not clobber parameter drawingBoard
    // where MovingCircle's drawing method may change some state of
    // the drawingBoard parameter graphics object
    Graphics circleGraphics = drawingBoard.create();
    // Now draw all the circles, location 0,0 will be top left
    // corner within the borders of the window
    for (MovingCircle circle : circles) {
        circle.draw(circleGraphics);
    }
    circleGraphics.dispose();

    // We should only increment frames here, because Swing coalesces
    // repaint calls. So if a repaint is scheduled and hasn't
    // completed yet, other repaint requests will be ignored, so
    // this is the only spot in code within GameData where we can know
    // for sure that a frame has been produced.
    // (Assuming this method being called means the graphics will be
    // used for a frame update)
    frames++;
}

...

 MovingCircle's draw method:

 ...

/**
 * Draw the circle
 */
 public synchronized void draw(Graphics g) {
    g.setColor(color);
    g.fillOval((int) x, (int) y, width, height);
}

After all the graphics are painted to the image, we can then draw it to the JFrame in the correct location by using the Insets object again, and having it stretched or squeezed to fill the entire draw area of the JFrame.

// Now draw the drawing board over the panel, and stretch the
// image if needed.
// NOTE: This method of stretching graphics is not optimal.
// This causes a computation of a stretched image each time. A
// better implementation would be to cache an image of the latest
// representation of a drawn circle, and re-cache whenever there is
// a visible change (like color, or it's size, which would be
// due to a window resize), and draw that cached image at the
// correct calculated location.
// Additionally, it also causes the rendering to this image to
// be done on the CPU. See the improvements section in the
// tutorial.
tempGraphics.drawImage(drawing, 0, 0, this.getWidth(),
        this.getHeight(), null);
        

All of the drawing code seen so far has originated from a Graphics object that was passed by Swing to our custom JComponent's double buffered paintComponent method, which in this tutorial, is a custom JPanel (I chose a panel so I could add components to it, on top of whatever graphics we will draw). Below is an excerpt from the main code example at the bottom of this page, showing the use of the paintComponent(Graphics g) method in a custom component, which calls the draw methods shown previously. Note that this method needs to be executed by the EDT from calling a repaint request, you do not call the method yourself in any manner with your own Graphics object. In addition, JFrame follows a different object hierarchy than most Swing components, and does not have a paintComponent method, so you will want to overwrite the method in a JComponent object.

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);

    // Obtaining the graphics of our drawing image we use,
    // most of the graphics drawn are drawn to this object
    Graphics2D drawingBoard = drawing.createGraphics();
    gameData.drawGameData(drawingBoard, drawing.getWidth(),
            drawing.getHeight());
    drawingBoard.dispose();

    // Creating a graphics object to not clobber parameter g
    // where our state changes to g may affect other not yet rendered
    // Swing components
    Graphics tempGraphics = g.create();

    // Now draw the drawing board over the panel, and stretch the
    // image if needed.
    // NOTE: This method of stretching graphics is not optimal.
    // This causes a computation of a stretched image each time. A
    // better implementation would be to cache an image of the latest
    // representation of a drawn circle, and re-cache whenever there is
    // a visible change (like color, or it's size, which would be
    // due to a window resize), and draw that cached image at the
    // correct calculated location.
    // Additionally, it also causes the rendering to this image to
    // be done on the CPU. See the improvements section in the
    // tutorial.
    tempGraphics.drawImage(drawing, 0, 0, this.getWidth(),
            this.getHeight(), null);

    // In addition, draw the FPS/UPS post stretch, so we always can read
    // the info even if you shrink the frame really small.
    tempGraphics.setColor(Color.WHITE);
    // Grab the font height to make sure we don't draw the stats outside
    // the panel, or over each other.
    int fontHeight = g.getFontMetrics(g.getFont()).getHeight();
    tempGraphics.drawString("FPS: " + gameData.getFPS(), 0, fontHeight);
    tempGraphics.drawString("UPS: " + gameData.getUPS(), 0,
            fontHeight * 2);
    tempGraphics.dispose();
}

The method of stretching graphics used in this tutorial is not that efficient. A better implementation would be to cache an image of the latest representation of a drawn circle, and re-cache whenever there is a visible change (like color, or its size, which would be due to a window resize), and draw that cached image (at that point, a managed image existing in VRAM) at the correct calculated location (this also saves us from having to do the same scaling operation on every render). See the improvements section for more information.

Using a Timer object to periodically update the game

In Swing, calls to repaint(), usually follow after a physical change within the program, such as a user interacting with the user interface, or the resizing of the application window. Since we want to see the program animate at a fast rate, we will incorporate a java.util.Timer object (not to be confused with javax.Swing.Timer) to issue a call to repaint() every so often, as well as to update our application's data. This is still considered passive rendering, because a call to repaint() is just a request to repaint, and does not necessarily receive a paint every time it's called, especially if the first request to repaint isn’t finished yet. In the code, we have a TimerTask scheduled to a Timer call repaint() every 20 milliseconds, or every 1 millisecond.

Before we get into the details of using the Timer, there are a few details on how and when repaints occur that are important. A repaint request is handled by the EDT (Event Dispatch Thread), which handles all the interactions with Swing (and anything you execute on that Thread, perhaps on accident). This means that the EDT can have other tasks besides a repaint to accomplish, and it can even be hung up by things such as a long computation that you accidently coded to that thread. Also note that the EDT will not schedule a repaint if there's a repaint already queued in the EDT. So let's say you were scheduling repaints with a timer every 5 milliseconds, and when you call for the second repaint and the first one hasn't happened yet, then the second request is ignored and 10 milliseconds later (when you expected 5 milliseconds), the third repaint acting as the second finally updates your application. This will be visible in the program when you notice that the updates per second (UPS), will be higher than the frames per second (FPS). If the update code calls repaint every time it executes, and the update schedule is slow then the repaint should be answered every time, but if the execution is enqueuing repaint calls too fast, they will be ignored.

The timer we use is a Timer from the java.util package. With that timer, you can schedule TimerTasks to occur at certain intervals, which will be executed on some thread. There are two different interval mechanisms to be aware of, fixed delay, and fixed rate. Fixed delay tries to create an event every x amount of milliseconds, longer if the prior task did not finish in x milliseconds. Fixed rate does the same but with a small difference where if one event takes too long to finish, it will speed up the next few events to maintain an overall average speed, helping to maintain a certain FPS goal you might have. By default, TimerTasks use fixed delay. Now let's see some code on how to create a Timer, and TimerTask, and the code that executes when the TimerTask executes.

...
// Create the timer that will handle our update task
updateTimer = new java.util.Timer();
// Initializing TimerTask with a default do nothing task to avoid
// having a null reference
updateTask = new TimerTask() {
    @Override
    public void run() {
        // do nothing default initialization
    }
};
...

private void scheduleGameUpdate(int updateSpeedInMilliseconds) {
    // Stop the previous update task (safe to call if not scheduled)
    updateTask.cancel();
    // Create the update task which will simply call updateData
    updateTask = new TimerTask() {
        @Override
        public void run() {
            updateData();
        }
    };
    updateTimer.scheduleAtFixedRate(updateTask, 0,
            updateSpeedInMilliseconds);
}

And here's the code that executes when the update task executes. It maintains the updates per second value, calling any other relevant update methods, and calling repaint on a specific Swing component that draws the data we update if one exists.

private synchronized void updateData() {
    // Calculating a new fps/ups value every second
    if (nanoseconds >= 1000000000) {
        fps = frames;
        ups = updates;
        nanoseconds = nanoseconds - 1000000000;
        frames = 0;
        updates = 0;
    }

    long elapsedTime = System.nanoTime() - oldTime;
    oldTime = oldTime + elapsedTime;
    nanoseconds = nanoseconds + elapsedTime;

    // Loop through all circles, update them
    for (MovingCircle circle : circles) {
        circle.update(elapsedTime);
    }

    // An update occurred, increment.
    updates++;

    // Ask for a repaint if we know of a component to repaint
    if (gameDrawingComponent != null) {
        gameDrawingComponent.repaint();
    }
}

Using a high resolution timer to time our animations, movements, and events

In the previous section of the tutorial, you may have noticed that we are doing math on some sort of time values we are retrieving from System.nanoTime(), we are doing this in order to time our animations, movements, and events perfectly based on how much time has elapsed since we last updated their state. Before Java 1.5, the easiest way to do this was to use System.currentTimeMillis() which returns a time in milliseconds, however on some older operating systems, that timer has a poor resolution, which is the amount of time that must separate two timer calls. Windows XP/Vista/7 has a default resolution of 16ms, and I've read that Windows 98 even has a resolution of 55ms (not sure if that is correct). This can create some not so fluid animations. Usually we would want games to hit 60 noticeable frames per second, as that's a common max refresh rate of monitors, and anything higher is usually said to be unnoticeable (for most people including myself at least), so having a low timer resolution will help us create more frames that are noticeably different. An alternative to System.currentTimeMillis(), is System.nanoTime(), a high resolution timer since Java 1.5 that returns a time in nanoseconds (note that the precision is in nanoseconds, not accuracy). With a better resolution, it is best to use nanoTime() to measure time. A good read on the two timing calls is Inside the Hotspot VM: Clocks, Timers and Scheduling Events - Part I - Windows by David Holmes.

/**
 * Update the circle, such as moving the circle, and detecting
 * collisions.
 *
 * @param elapsedTime The time that has elapsed since the last time
 *                    the circle was updated.
 */
 public synchronized void update(long elapsedTime) {
    float pixelMovement = elapsedTime * speed;
    if (down) {
        y = y + pixelMovement;
    } else {
        y = y - pixelMovement;
    }
    if (right) {
        x = x + pixelMovement;
    } else {
        x = x - pixelMovement;
    }

    // Test if circle hit a side of the known bounds
    // Also move the circle off the wall of the bounded area to prevent
    // the collision from sticking (comment out that code to see the
    // effect)
    if (y < 0) {
        down = !down;
        y = 0;
    }
    if (y > boundedArea.height - height) {
        down = !down;
        y = boundedArea.height - height;
    }
    if (x < 0) {
        right = !right;
        x = 0;
    }
    if (x > boundedArea.width - width) {
        right = !right;
        x = boundedArea.width - width;
    }
}

Another approach to updating animations is updating them by a tick based approach. An example of this approach is when you move a sprite a fixed amount of distance every time a certain piece of code pertaining to updating a position or animation is executed. Updating by how much time has elapsed is mostly superior to tick based animations, since with tick based animations, the speed of animations and movements of the sprite is never certain on any computer besides your own, as other computers will execute the code at a different speed, making some animations impossible to use, or very annoying to use. While tick based updating can help you do a few things with more ease (like detecting collisions), it is generally worth it to spend the time needed to program in time based updating.

Synchronizing data between the update tasks and the EDT

In the main code example for this tutorial, and as you might have seen so far, there are some synchronized methods. It's important to make sure that data is not viewed as stale or out of date between one thread and another, as you have your thread that updates data (in this example, the Timer thread that executes TimerTasks), and the EDT which draws the data.

Another thing to watch out for is to make sure that the creations of Swing components happen on the EDT. From what I have been able to discover on the internet, there seems to have been official word from Sun a while go around the time of Java 1.5 that initializing Swing components off the EDT was okay, but that has since been renounced. Because of that, you'll see in the main code example that I initialize my JFrame on the EDT with a runnable, which also takes in a GameData object which then passes it a reference to the JPanel that will end up drawing the GameData's data. The runnable is scheduled with an invokeAndWait call, so the main thread blocks until the runnable completes, including setting the reference (GameData is defensively coded against the possibility that that does not happen for a while though).

Room for improvements

One major improvement to gain more FPS is to cache the graphics of the drawn circle to create a managed image. Caching graphics that are used often to an image (BufferedImage created by createCompatibleImage) to draw again later lets you skip the time it takes to compute certain data about what you are drawing (such as the arcs of the circles drawn in this code), and also enables the image to be hardware accelerated after you draw it a few times. A BufferedImage created this way that isn't changed and drawn a few times will be cached to VRAM, creating what is known as a managed image. A managed image is a lot faster to render to the screen because it's transferring pixel data from VRAM to VRAM, leaving out the CPU, the bus, and main memory. This also allows the GPU to handle the task of drawing the image, freeing up the CPU to handle other tasks. For every time the circle we draw changes, if we cached those graphics to such an image, and drew that image instead of manually constructing it every time with the Java 2D paint calls, we would see a great boost in rendering time. Also another good time to cache graphics is when the rendering actions we do cannot be handled directly by the underlying native graphics API such as antialiasing, so caching graphics that use those techniques is a great thing to do. Check out section 3.2.2. of Troubleshooting Guide for Java SE6 Desktop Technologies for a list of actions that will be bound to the CPU.

One other improvement, already mentioned but expanded here is to get rid of the idea of stretching graphics. The method of stretching graphics used in this tutorial is not that efficient, regardless of how many cached images are drawn to the image we stretch, there's still a lot of computation involved in stretching the image (which we do every render!). A better implementation would be to cache the stretched resized circle, and then draw the cached resized images at the correct calculated location to obtain the same effect. This eliminates the stretching of graphics at every render, to only one time when we cache the individual needed graphics. One other major improvement that this brings us, is that we will start to draw the graphics object passed by Swing, which is an improvement because Swing uses ViotileImage under the hood, where our Java 2D calls to methods such as drawRect are passed off to the underlying native API such as OpenGL or Direct3d.


Summary, advantages and disadvantages

Main advantages: Easy to code, easy to relate to as it's essentially creating your own Swing components, and just as easy to use with other Swing components for application controls (Swing components can also be used in an actively rendered application, check my active rendering tutorial for more information).

Main disadvantages: Don't have total control over repaints. When exactly, and even if a repaint will occur remains unknown, although, it isn't as bad as it sounds, especially for small hobby applications that don't require the most efficient solution. Emphasizing in code for all graphics to be hardware accelerated will help ensure a good frame rate.

Program example

Here's a program example I wrote that uses all the techniques I mentioned above.

Download PassiveCircles example application
Download PassiveCircles example source code
Site Smith badge icon