在 canvas 上绘制全屏可绘制对象时帧率低

Low frame rate when drawing full screen drawable on canvas

我正在开发的应用程序是 Flappy Bird 的克隆版。 我正在使用一个 surfaceView 对象,其中我有一个 gameThread,在它的 运行 方法中,我在 canvas 上绘制了游戏的各个组件。

一切 运行 只要我只绘制 Rects 来表示对象,一切都很顺利,但是当我添加第一个 Drawables 时,我注意到平滑度有点下降。如果我尝试将背景绘制为 Drawable,游戏会遭受非常严重的帧率损失。

我尝试了什么:

None 这有任何实际效果。

基本上:

有点相关的代码:

public class CannonView extends SurfaceView
        implements SurfaceHolder.Callback {
    private CannonThread cannonThread; // controls the game loop
    private Drawable background;

    // constructor
    public CannonView(Context context, AttributeSet attrs) {
        super(context, attrs); // call superclass constructor
        getHolder().addCallback(this);
        background= ResourcesCompat.getDrawable(getResources(), R.drawable.background, null);
    }
    public void newGame() {
        background.setBounds(0,0, getScreenWidth(),getScreenHeight());
    }
    public void drawGameElements(Canvas canvas) {
        background.draw(canvas);
    }
    public void stopGame() {
        if (cannonThread != null)
            cannonThread.setRunning(false); // tell thread to terminate
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (!dialogIsDisplayed) {
            newGame(); // set up and start a new game
            cannonThread = new CannonThread(holder); // create thread
            cannonThread.setRunning(true); // start game running
            cannonThread.start(); // start the game loop thread
        }
    }
    private class CannonThread extends Thread {
        private SurfaceHolder surfaceHolder; // for manipulating canvas
        private boolean threadIsRunning = true; // running by default

        // initializes the surface holder
        public CannonThread(SurfaceHolder holder) {
            surfaceHolder = holder;
            setName("CannonThread");
        }

        // changes running state
        public void setRunning(boolean running) {
            threadIsRunning = running;
        }
        // controls the game loop
        @Override
        public void run() {
            Canvas canvas = null; // used for drawing
            while (threadIsRunning) {
                try {
                    // get Canvas for exclusive drawing from this thread
                    canvas = surfaceHolder.lockCanvas(null);
                    synchronized(surfaceHolder) {
                        drawGameElements(canvas);
                    }
                }
                finally {
                    if (canvas != null)
                        surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }

}

很明显,低帧率的主要原因是 background.draw()。切换到 Bitmap 稍微改善了这一点,可能是因为它缓存了 draw() 的输出,并且因为它可以与保证不需要缩放的 Canvas 函数一起使用(例如,drawBitmap( Bitmap, float, float, Paint))

您还发现切换到 RGB_565 作为中间格式可以显着提高性能,大概是因为它丢掉了 alpha。 (否则,我预计这会稍微慢一些,b/c 格式必须转换回 RGBA_8888,因为它被 blitted 到 SurfaceView。)

显然 Android 不会让您超过 60fps。这几乎可以肯定是因为 lockCanvas() 参与了限制绘图速率的 triple buffering 方案,以防止您提交永远无法显示的帧(由于您设备的固定屏幕刷新率为 60Hz)。

这留下了一个问题,即为什么您没有获得完整的 60fps,而是接近它的东西。如果 drawGameElements() 每次到 运行 花费相同的时间,并且少于 16 毫秒,那么 lockCanvas() 应该会限制你,并且永远不会丢帧(连续 60fps)。似乎线程调度器或其他东西中有一个 burble,而且每隔一段时间,CannonThread 执行得不够快,无法在三重缓冲方案需要 page-flip. In this event, the frame must be delayed until the next screen refresh. You might try increasing CannonThread's thread priority 之前提供帧,删除drawGameElements() 中不需要在 CannonThread 上发生的任何额外处理,或关闭您设备上 运行 的其他应用程序。

如前所述,OpenGL 是为此类游戏获得最大精灵性能的标准方式,因为它能够将许多操作卸载到硬件。您可能正在接近基于 drawBitmap() 的游戏的性能极限。