Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing "flickering" when calling Drawable.draw()

I have a little experimentation app (essentially a very cut-down version of the LunarLander demo in the Android SDK), with a single SurfaceView. I have a Drawable "sprite" which I periodically draw into the SurfaceView's Canvas object in different locations, without attempting to erase the previous image. Thus:

private class MyThread extends Thread {
    SurfaceHolder holder;  // Initialised in ctor (acquired via getHolder())
    Drawable      sprite;  // Initialised in ctor
    Rect          bounds;  // Initialised in ctor

    ...

    @Override
    public void run() {
        while (true) {
            Canvas c = holder.lockCanvas();
            synchronized (bounds) {
                sprite.setBounds(bounds);
            }
            sprite.draw(c);
            holder.unlockCanvasAndPost(c);
        }
    }

    /**
     * Periodically called from activity thread
     */
    public void updatePos(int dx, int dy) {
        synchronized (bounds) {
            bounds.offset(dx, dy);
        }
    }
}

Running in the emulator, what I'm seeing is that after a few updates have occurred, several old "copies" of the image begin to flicker, i.e. appearing and disappearing. I initially assumed that perhaps I was misunderstanding the semantics of a Canvas, and that it somehow maintains "layers", and that I was thrashing it to death. However, I then discovered that I only get this effect if I try to update faster than roughly every 200 ms. So my next best theory is that this is perhaps an artifact of the emulator not being able to keep up, and tearing the display. (I don't have a physical device to test on, yet.)

Is either of these theories correct?

Note: I don't actually want to do this in practice (i.e. draw hundreds of overlaid copies of the same thing). However, I would like to understand why this is happening.

Environment:

  • Eclipse 3.6.1 (Helios) on Windows 7
  • JDK 6
  • Android SDK Tools r9
  • App is targetting Android 2.3.1

Tangential question:

My run() method is essentially a stripped-down version of how the LunarLander example works (with all the excess logic removed). I don't quite understand why this isn't going to saturate the CPU, as there seems to be nothing to prevent it running at full pelt. Can anyone clarify this?

like image 938
Oliver Charlesworth Avatar asked Sep 02 '25 01:09

Oliver Charlesworth


1 Answers

Ok, I've butchered Lunar Lander in a similar way to you, and having seen the flickering I can tell you that what you are seeing is a simple artefact of the double-buffering mechanism that every Surface has.

When you draw anything on a Canvas attached to a Surface, you are drawing to the 'back' buffer (the invisible one). And when you unlockCanvasAndPost() you are swapping the buffers over... what you drew suddenly becomes visible as the "back" buffer becomes the "front", and vice versa. And so your next frame of drawing is done to the old "front" buffer...

The point is that you always draw to seperate buffers on alternate frames. I guess there's an implicit assumption in graphics architecture that you're always going to be writing every pixel.

Having understood this, I think the real question is why doesn't it flicker on hardware? Having worked on graphics drivers in years gone by, I can guess at the reasons but hesitate to speculate too far. Hopefully the above will be sufficient to satisfy your curiousity about this rendering artefact. :-)

like image 191
Reuben Scratton Avatar answered Sep 08 '25 01:09

Reuben Scratton