SurfaceView 闪烁/撕裂

Posted

技术标签:

【中文标题】SurfaceView 闪烁/撕裂【英文标题】:SurfaceView flickering/tearing 【发布时间】:2011-04-04 03:12:08 【问题描述】:

我正试图弄清楚如何解决我的问题。我读过http://groups.google.com/group/android-developers/browse_thread/thread/a2aac88a08cb56c2/b7dff4ba388cd664?lnk=gst&q=SurfaceView#b7dff4ba388cd664,这回答了我的问题,但据我所知,这是一种“运气不好”的答案。所以这是我的问题:

每当表面发生变化(例如方向等)时,我都会以正常方式(锁定/解锁和后置)使用 SurfaceView 绘制我的游戏背景的位图,并且我正在渲染一系列移动圆圈(最多 30 个半径约为 25.f)。这些圆圈位置的 x,y 数据来自服务器,并且运行良好。 所有圆形对象都存储在一个列表中,并且它们的位置会更新,注意确保此更新是同步的。 但是,当我将这些圆圈绘制到屏幕上时(在 canvas.lock() 期间),大多数情况下它们渲染得很好,但偶尔(例如每隔几秒一次),一些圆圈似乎在一帧中短暂撕裂或闪烁。圆圈的数量始终是恒定的,所以这不是问题,并且圆圈列表没有并发修改(正如我所说,它是同步的)。 我什至尝试在每个渲染循环上将所有这些圆圈绘制到位图上,并将该位图绘制到主画布上。这似乎对性能的影响更大(~13FPS 而不是直接在主画布上绘制圆圈时的~30FPS)。 对不起,如果信息有点模糊,(试图让公司开心:p)但我只是想知道是否有人能给我一个线索?或者我只是运气不好。我应该注意,来自服务器的定位数据是实时数据,渲染反映这些实时位置至关重要。

感谢您的帮助! 克里斯

编辑:

很公平。这是来自渲染线程的 run()。

@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);
                
            
        
    

相当标准的东西(几乎是月球着陆器的副本: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;

很容易理解,如果方向改变,只需重新绘制背景位图并启动渲染线程。

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

最后:

    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);
        
    

再次感谢各位。 克里斯

【问题讨论】:

没有看到你的游戏循环和绘图代码就无法回答。 和 Sebi 说的一样 - 代码示例会有所帮助 - 但您是否考虑过您偶尔收到的职位不正确的可能性?只是想一想为什么圆圈可能会“闪烁”,如果它们被暂时放置在视野之外。同样,如果没有任何代码,这将无法回答。 非常感谢大家的帮助。但是是的,我确信定位数据是正确的。 我现在也注意到我的 positionUpdate 线程以 500-1000 FPS 的速度运行(我知道,范围很广),我以 ~20-70 FPS 的速度接收数据包并且我正在渲染15-30 帧/秒。这会引起任何问题吗?我会认为渲染的同步性质会阻止任何问题? 【参考方案1】:

我遇到了这个问题。适合我的解决方案是同步 onDraw 方法和更新变换矩阵的方法。因为当矩阵相同时存在帧(new Matrix() 的初始值)并且在下一帧中它具有正确的值。

【讨论】:

【参考方案2】:

发生这种情况是因为 android 图形具有缓冲表面和可见表面,并且为了确保它们始终可用,它们只是交替使用。这意味着如果您在一个帧上绘制,除非您在每一帧上都绘制它,否则下一帧将不会出现。

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

【讨论】:

【参考方案3】:

这是一个很好的解释和可能的解决方法

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

【讨论】:

【参考方案4】:

我在阅读了一些关于表面视图的信息后发现,如果你调用

getHolder().lockCanvas();

您需要重新绘制所有内容。

除非你提供你的脏区。

getHolder().lockCanvas(Rect dirty);

如果只想重绘 Rect 定义的区域

【讨论】:

以上是关于SurfaceView 闪烁/撕裂的主要内容,如果未能解决你的问题,请参考以下文章

WPF动画有撕裂和闪烁

SurfaceView滚动时闪烁

Android SurfaceView导致屏幕闪烁

SurfaceView 在加载时闪烁黑色

安卓——SurfaceView。在不清除屏幕的情况下更新。 (注:我是菜鸟)

避免linux + QT中的屏幕撕裂