8

I'm implementing a SurfaceView subclass, where I run a separate thread to draw onto a SurfaceHolders Canvas. I'm measuring time before and after call to lockCanvas(), and I'm getting from about 70ms to 100ms. Does anyone could point me why i'm getting such high timings? Here the relevant part of the code:

public class TestView extends SurfaceView implements SurfaceHolder.Callback {

....

boolean created;
public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

   mThread = new DrawingThread(mHolder, true);
   mThread.onWindowResize(width, height);
   mThread.start();
}

public void surfaceCreated(SurfaceHolder holder) {

    created = true;
}

public void surfaceDestroyed(SurfaceHolder holder) {
    created = false;

}
class DrawingThread extends Thread {
public void run() {
while(created) {



            Canvas canvas = null;
            try {
                            long t0 = System.currentTimeMillis();
            canvas = holder.lockCanvas(null);
            long t1 = System.currentTimeMillis();
                            Log.i(TAG, "Timing: " + ( t1 - t0) );
            } finally {
                holder.unlockCanvasAndPost(canvas);
            }
}
kaneda
  • 5,981
  • 8
  • 48
  • 73

3 Answers3

5

You're creating a thread every time the surface is changed. You should start your thread in surfaceCreated and kill it in surfaceDestroyed. surfaceChanged is for when the dimensions of your surface changes.

From SurfaceView.surfaceCreated docs:

This is called immediately after the surface is first created. Implementations of this should start up whatever rendering code they desire. Note that only one thread can ever draw into a Surface, so you should not draw into the Surface here if your normal rendering will be in another thread.

The multiple threads are probably getting you throttled. From SurfaceHolder.lockCanvas docs:

If you call this repeatedly when the Surface is not ready (before Callback.surfaceCreated or after Callback.surfaceDestroyed), your calls will be throttled to a slow rate in order to avoid consuming CPU.

However, I'm not convinced this is the only problem. Does surfaceChanged actually get called multiple times?

idbrii
  • 10,975
  • 5
  • 66
  • 107
  • When the docs say `only one thread can ever draw into a Surface` ... should this statement be taken at face value, or are they saying "... can ever draw into a Surface *at the same time*". – Brian Vandenberg Sep 06 '12 at 05:45
  • 1
    I think it should be taken at face value. Even if not, then you have to handle serializing your draw commands. (Which is probably why they require you to submit them on a single thread.) According to http://stackoverflow.com/a/11258546/79125 I'm right (but we're just two guys on the internet ;). – idbrii Sep 06 '12 at 22:32
  • ooh. I'm definitely fond of a good tutorial (which that response links to). Thanks. I wish I could give you two +1s for that =) – Brian Vandenberg Sep 07 '12 at 15:29
2

This is related to how lockCanvas is actually implemented in the android graphic framework.

You should probably already know that lockCanvas will return you an free piece of memory that you will be used to draw to. By free, it means this memory has not be used for composition and not for display. Internally, simply speaking, an SurfaceView is backed up by double buffer, one is for drawing , one is for composition/display. This double buffer is managed by BufferQueque. If composition/display is slow than drawing, we have to wait until we have free buffer available.

pierrotlefou
  • 39,805
  • 37
  • 135
  • 175
1

read this:

What does lockCanvas mean (elaborate)

Community
  • 1
  • 1
CMA
  • 2,758
  • 5
  • 28
  • 40
  • Ok, thanks. I have already read that before, and understand the concepts. But I find no solution yet. Even if I remove the `synchronized` block (which is not recommmended), it does not help me out. Still those high timings. – kaneda May 16 '11 at 12:44