Occasional Stutter/Lag in game loop from lockCanvas method

880 Views Asked by At

I am smooth scrolling a bitmap at a given speed. I'm doing this with a game loop. It scrolls pretty smoothly, around 60 fps, except for occasional stutters / jumps. These jumps occur anywhere from once a second to a couple of times a second. Usually they start or become more frequent after a few seconds of running, but I'm not sure if this is a big clue or not.

The reason for the jumps is that occasionally an iteration of the run loop will take about twice as long as usual, so the bitmap stays in one place for a while and then jumps further to catch up and maintain its constant speed. I used interpolation to figure out the new position of the bitmap with each update based on the time that has elapsed. When a longer than usual time has elapsed, I've tried doing a couple of mini-updates instead of moving the entire distance at once, but the paused bitmap is still very noticeable.

I ran traceview, and the extra time is being spent inside lockCanvas. Most of the time this method takes around 10 ms, but in these long cases, it takes around 24ms. When I traced for four seconds, this happened 7 times.

In the following code, I'm having it sleep for a bit if it ran fast, but that's not actually making any difference. If I get rid of that part of the code my problem is not solved. There doesn't need to be a constant frame rate, since I'm just calculating the position based on how much time has passed.

@Override
    public void run() {


         long beginTime = 0;     // the time when the cycle begun
         long timeDiff;      // the time it took for the cycle to execute
         int sleepTime;      // ms to sleep (<0 if we're behind)
         sleepTime = 0;
        while (mRun) {
            Canvas c = null;

            try {
                beginTime = System.currentTimeMillis();

                c = mSurfaceHolder.lockCanvas(null);
                synchronized (mSurfaceHolder) {

                    if (mMode == STATE_RUNNING) {
                        updatePhysics();
                    }
                    doDraw(c);
                }


            } catch(Exception e){
                System.out.println(e.getStackTrace());
            }finally {

                if (c != null) {
                    mSurfaceHolder.unlockCanvasAndPost(c);
                }
            }
            timeDiff = System.currentTimeMillis() - beginTime;

            sleepTime = (int)(FRAME_PERIOD - timeDiff);

            if(sleepTime > 0){

                 try {
                    Thread.sleep(sleepTime);
                 } catch (InterruptedException e) {}
            }
        }
    }

The code to updatePhysics() and doDraw() do a little bit of math, and I've tried to make that as efficient as possible. Basically they just calculate the new position of the bitmap based on time and speed. I just have one bitmap, and it is not being reallocated every time or something like that.

Also, I'm positive that my surfaceHolder is ready, so it's not the common answer I've found from searching google that repeated calls to a non-ready surfaceHolder have been throttled.

Any ideas what could cause this? My surface holder uses PixelFormat.RGB_565 and my Bitmap is encoded as Bitmap.Config.RGB_565 if that makes a difference. I originally got the Bitmap from a relative layout that I made.

1

There are 1 best solutions below

1
On

One possible explanation is that your code generates a lot of short-lived objects, and the garbage collector kicks in periodically to reclaim memory.

These noticeable pauses were the bane of developers in early versions of Java, until generational garbage collection pretty much eliminated this issue. However, as far as I know, Android's Dalvik virtual machine does not employ generational garbage collection, so you should be cautious about creating objects that you immediately discard, especially in loops.

Profiling memory allocation will shine more light on this issue.

If this is indeed the problem, you could try to reuse objects, or handle data using primitives.