SurfaceView flickering/tearing
Asked Answered
H

4

7

I'm trying to figure out how to work around my problem. I've read http://groups.google.com/group/android-developers/browse_thread/thread/a2aac88a08cb56c2/b7dff4ba388cd664?lnk=gst&q=SurfaceView#b7dff4ba388cd664 which sort of answers my question but as far as I can tell, it's a sort of "tough luck" answer. So here's my problem:

I'm using SurfaceView in the normal way (lock/unlockAndPost) to draw a bitmap of my game background whenever the surface is changed (e.g. orientation etc) and I'm rendering a series of moving circles (up to 30 with a radius of about 25.f). The x,y data for the positions of these circles is coming from a server and that all runs fine. All circle objects are stored in a list and their position is updated taking care to ensure this update is synchronized. However, when I draw these circles to the screen (during canvas.lock() ), most of the time they render fine but occasionally (e.g. once every few seconds) some of the circles seem to tear or flicker briefly for a frame. The number of circles is always constant so that's not the problem and there's no concurrent modifications to the list of circles (as I said, it is synchronized). I have even tried drawing all these circles to a Bitmap on each render loop and drawing that bitmap to the main canvas. This just seems to affect the perfomance even more (~13FPS as opposed to ~30FPS when drawing circles directly to main canvas). Sorry if the information is a little vague guys, (trying to keep the company happy :p ) but I was just wondering if anyone could give me a clue? Or am I just out of luck. I should note that the positioning data coming from the server is realtime data and it is vitally important that the rendering reflects these realtime positions.

Thanks for any help! Chris

EDIT:

Fair enough. Here is the run() from the render thread.

@Override
    public void run() {
        Canvas c;
        while (mRun) {
            c = null;
            try {
                c = mSurfaceHolder.lockCanvas(null);
                synchronized (mSurfaceHolder) {
                    panel.onDraw(c);
                }
            } finally {
                if (c != null) {
                    mSurfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }

Pretty standard stuff (almost a carbon copy of lunar lander :p)

@Override
public void surfaceCreated(SurfaceHolder holder) {
    mBackground= Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.RGB_565);
    screenOrientation = getResources().getConfiguration().orientation;
    if(thread.getState()== Thread.State.TERMINATED){
        thread = new RenderThread(holder, this);
        thread.setRunning(true);
        thread.start();
    }else {
        thread.setRunning(true);
        thread.start();
    }
}



@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {
    Canvas c = new Canvas(mField);
    c.drawARGB(255,0,144,0);
    backgroundDetails.renderOnPanel(c, this);
    screenOrientation = getResources().getConfiguration().orientation;
}

Pretty easy to follow, just redraw the background bitmap if the orientation changes and start the rendering thread.

public void onDraw(Canvas canvas) {
    canvas.drawBitmap(mField,0,0,null);
    drawPositionBeans(canvas, this);
}

And finally:

    public void onDraw(Canvas canvas, RadarView radarView) {
    float beanX=0, beanY=0;
    float radius = 25.0f;
    final List<PositionBean> copyOfList = Collections.synchronizedList(positionBeans.getPositionBeans());
    synchronized(copyOfList){
        try {
            for (final PositionBean pb : copyOfList)
            {
                if (panel.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
                    beanX = (float)pb.getY()*(-1);
                    beanY = (float)pb.getX();
                } else {
                    beanX = (float)pb.getY()*(-1);
                    beanY = (float)pb.getX()*(-1);
                }
                mPaint.setARGB(255,pb.getRgbColor().getR(), pb.getRgbColor().getG(), pb.getRgbColor().getB());
                panel.drawSpot(canvas, beanX, beanY, radius, mPaint);

                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setARGB(255,255,222,1);
                for (int j = 0; j < selectedBean.size(); ++j)
                {
                    if(pb.getTrack()==selectedBean.get(j)) {
                        panel.drawSpot(canvas, beanX, beanY, radius+1, mPaint);
                    }
                }
                mPaint.setStyle(Paint.Style.FILL);
            }

        } catch(Exception e) {
            Log.e("render", "Exception Drawing Beans: " + e);
        }
    }
}

Thanks again guys. Chris

Hypersonic answered 26/8, 2010 at 7:42 Comment(4)
Impossible to answer without seeing your gameloop and drawing code.Stuppy
Same as Sebi said - a code sample would be helpful - but have you considered the possibility that the positions you are receiving occasionally are incorrect? Just a thought as to why the circles might be 'flickering', if they are being positioned off-view temporarily. Again, without any code this will be impossible to answer.Socage
Many thanks for the help guys. But yes I am sure the positioning data is correct.Hypersonic
I have also noticed now that my positionUpdate thread is running at 500-1000 FPS (I know, wide range), the I'm receiving packets at ~20-70 FPS and I'm rendering between 15-30 FPS. Would this be causing any problems? I would have thought the Synchronized nature of the render would have stopped any problems with that?Hypersonic
T
1

I had that problem. Solution suitable for me is synchronization of onDraw method and method that updates transformation matrix. Because there was frames when matrix is identical (initial value of new Matrix()) and in next frame it had right values.

Tannertannery answered 5/1, 2011 at 20:6 Comment(0)
N
1

It's happening becasue the android graphics has a buffer surface and a visible surface and to make sure that they are always available they simply alternate. Which means that if you draw on one frame it won't be there the next unless you draw it on every frame.

http://groups.google.com/group/android-developers/msg/8d1243c33f9b7b6e?pli=1

Nth answered 15/3, 2011 at 18:5 Comment(0)
C
1

Heres a great explaination and possible workaround

http://android-coding.blogspot.ca/2012/01/flickering-problems-due-to-double.html

Concertino answered 20/3, 2012 at 1:8 Comment(0)
T
0

I found after reading a bit about surface view that if you call

getHolder().lockCanvas();

You need to draw everything again.

Unless you provide your dirty area.

getHolder().lockCanvas(Rect dirty);

If you only want to redraw the area defined by the Rect

Thursday answered 5/6, 2015 at 16:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.