我有一个小实验应用程序(本质上是Android SDK中LunarLander演示的非常缩减版本),只有一个SurfaceView
。我有一个Drawable
“精灵”,我定期在不同位置绘制到SurfaceView
的{{1}}对象,而不会尝试删除上一张图像。因此:
Canvas
在模拟器中运行,我看到的是,在发生一些更新之后,图像的几个旧“副本”开始闪烁,即出现和消失。我最初假设也许我误解了private class MyThread extends Thread {
SurfaceHolder holder; // Initialised in ctor (acquired via getHolder())
Drawable sprite; // Initialised in ctor
Rect bounds; // Initialised in ctor
...
@Override
public void run() {
while (true) {
Canvas c = holder.lockCanvas();
synchronized (bounds) {
sprite.setBounds(bounds);
}
sprite.draw(c);
holder.unlockCanvasAndPost(c);
}
}
/**
* Periodically called from activity thread
*/
public void updatePos(int dx, int dy) {
synchronized (bounds) {
bounds.offset(dx, dy);
}
}
}
的语义,并且它以某种方式保持“层次”,并且我正在将它颠倒过来。然而,我后来发现,如果我尝试以大约每200毫秒的速度更新,我只会得到这种效果。所以我的下一个最好的理论是,这可能是模拟器无法跟上并撕裂显示器的神器。 (我还没有可以测试的物理设备。)
这些理论中的任何一个都是正确的吗?
注意:我实际上并不想在实践中这样做(即绘制数百个相同内容的重叠副本)。但是,我想了解为什么会发生这种情况。
环境:
切线问题:
我的Canvas
方法本质上是LunarLander示例如何工作的精简版本(删除了所有多余的逻辑)。我不太明白为什么这不会让CPU饱和,因为似乎没有什么可以阻止它以完全的毛皮运行。任何人都可以澄清这个吗?
答案 0 :(得分:12)
好吧,我以与你类似的方式屠杀了Lunar Lander,看到闪烁的我可以告诉你,你所看到的是双缓冲机制的一个简单的人工制品,每个Surface
都有
当您在附加到Canvas
的{{1}}上绘制任何内容时,您将绘制到“后退”缓冲区(不可见的缓冲区)。当你Surface
你正在交换缓冲区时......你突然变得可见,因为“后退”缓冲区变成“前面”,反之亦然。所以你的下一帧绘图是在旧的“前”缓冲区完成的......
重点是你总是在备用帧上绘制单独的缓冲区。我想在图形架构中存在一个隐含的假设,即总是将写入每个像素。
理解了这一点后,我认为真正的问题是它为什么不在硬件上闪烁?在经历了多年的图形驱动程序研究之后,我可以猜测原因,但却犹豫不决。希望上述内容足以满足您对此渲染工艺的好奇心。 : - )
答案 1 :(得分:1)
您需要清除精灵的先前位置以及新位置。这就是View系统自动执行的操作。但是,如果直接使用Surface并且不重绘每个像素(使用不透明颜色或使用SRC混合模式),则必须自己清除缓冲区的内容。请注意,您可以将脏矩形传递给lockCanvas(),它将为您执行上一个脏矩形和您传递的矩形的并集(这是UI工具包使用的机制。)它还将设置剪辑rect画布是这两个矩形的结合。
至于你的第二个问题,unlockAndPost()会做一个vsync,所以你永远不会以超过60fps的速度进行绘制(我见过的大多数设备的显示刷新率设置在55Hz左右。)