lockCanvas()非常慢。

14

我在较慢的设备(Orange San Francisco,也称为ZTE Blade)上测试我的游戏,结果帧率极低。

我在绘制循环中加入了一些调试代码,并发现以下代码行需要超过100毫秒:

c = mSurfaceHolder.lockCanvas();

有其他人看到这种情况吗?我临时用扩展View并实现onDraw()来替换SurfaceView,帧率显著提高。

虽然一般来说,在我的HTC Desire上,SurfaceView要快得多。我怀疑这可能是一个Android 2.1的问题。我正在考虑root手机并升级到2.2(如果可能的话),但我确实想要一个运行在2.1上的设备,所以从长远来看可能会产生反效果。

** 更新 **

我继续处理这个问题,并发现了一些更加令人困惑的方面。

我root了手机并安装了2.2,但问题仍然存在。当应用程序首次启动时,lockCanvas按预期工作(0-1毫秒)。然后在我的初始化过程中的某个时候,lockCanvas突然开始花费大约100毫秒。

值得指出的是,我正在使用异步任务加载资产,以便可以显示加载屏幕。

尽管我尽力确定程序在减速时实际上正在执行什么操作,但我无法做到这一点。事实上,当我在调试模式下单步运行时,它运行得很快!

现在我发现,如果在我的SurfaceView构造函数中添加一个约10秒的延迟,那么减速就不会发生,一切都正常。

但是,如果按Home键,然后再切回来,减速问题就会重新出现。

我对这个愚蠢而不合逻辑的问题感到非常绝望!我认为这可能与内存使用有关。也许某些内容被交换出去,影响了视频RAM?

至少我对理论很感兴趣。


描述控制渲染发生的方式...您是使用专用线程,还是在每次绘制()结束时使其无效,或者使用定时机制,或其他方式? - Reuben Scratton
我使用线程进行绘制(与SpriteMethodTest相同的方式)。只需在while true循环即可。 - Kevin
你是否可能被卡在一个大的GC中了?我的意思是,我同意lockCanvas()不是最便宜的调用,但它不应该花费那么长时间。如果你在iowait期间的调用中被卡在GC中,那么这个调用似乎会比它应该的多花费100毫秒。你有logcat或一些代码示例吗? - Greg Giacovelli
我想这是可能的,但我已经优化了我的代码,不需要任何GC操作,以便获得最佳帧速率。不过稍后我会再次检查以确保。如果GC每帧都在运行,那么我必须疯狂地使用资源! - Kevin
我从你之前的帖子中得到的印象是暂停只会在某些渲染上发生。所以我不知道每个堆栈帧上正在生成什么,但我只是假设它运行了X次调用,然后不可预测地花费了很长时间。 - Greg Giacovelli
我真的希望S/O能在有人回复评论时通知我!那么,几天后,您是否根据SpriteMethodTest的CanvasSurfaceView.java从onPause()和onResume()暂停和恢复线程?您的资产加载线程和渲染线程之间存在什么同步? - Reuben Scratton
4个回答

11
关于 lockCanvas() 的说明如下:
如果在 Surface 还未准备好(即在 Callback.surfaceCreated 或 Callback.surfaceDestroyed 之前或之后)多次调用此方法,你的调用将被限制为缓慢的速率,以避免消耗 CPU。
可能有些设备启动绘图循环过早导致了问题。因为你写道:
现在我发现,如果在我的 SurfaceView 构造函数中添加一个约为 10 秒的延迟,则不会出现卡顿情况,一切都可以正常工作。

我给你加一分,因为你在文档中找到了那个。上周我也看到了那个,并测试了一下,但它并没有解决问题,不过再次阅读后我需要再确认一下我做了什么。无论如何,我将把这个答案评为最佳答案。 - Kevin

0
我最近发现,如果在画布上绘制时使用大位图,并且这些位图存储在活动类中,"_surfaceHolder.lockCanvas()" 命令本身会非常耗时(根据设备约为70毫秒)。然而,将这些位图存储移到其他类(在不同的文件中,比如 MY_DATA),并且活动只引用该新类 - 就可以解决问题。
我对这种现象没有任何解释。

0

我在使用Canvas绘图时遇到了同样神秘的问题,但通过将Canvas绘图更改为在SurfaceView上绘制来解决了这个问题。但现在我不断遇到缓慢的lockCanvas()调用。

以下是我的观察结果。

  • Galaxy Note 3 n900:存在问题
  • Galaxy Note 3 n9005:存在问题
  • Galaxy S4 i9505:存在问题
  • Galaxy Gio:没有问题
  • LG G2 D802:存在问题
  • Galaxy S2 i9100:没有问题

我暂时通过扩展View并实现onDraw()方法来替换surfaceview,并获得了更好的帧率。

我还注意到,三星手机使用GLES20Canvas而不是常规的CanvasonDraw()绘图,因此具有更好的性能。


0

那么,也许我们可以使用holder.isCreating()来检查状态? 如果画布仍在创建,则此方法将返回true。

类似于 while(holder.isCreating()) {} can=holder.lockCanvas();

但我现在有点困惑。据我所知,当SurfaceView被创建时,colbeck会被调用。我们应该实现SurfaceHolder.Callback接口。当表面被创建时,回调方法 public void surfaceCreated(SurfaceHolder holder) {} 将被调用。 从surfaceCreated方法中,我正在启动gameloop线程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接