首先,如果您想了解幕后发生的事情,您需要阅读Android图形架构文档。虽然很长,但如果您真诚地想了解“为什么”,那么就从这里开始吧。
关于纹理视图
TextureView的工作原理是这样的:它有一个Surface,它是一个具有生产者-消费者关系的缓冲区队列。如果您使用软件(Canvas)渲染,则锁定 Surface,这会为您提供缓冲区;你在它上面画画;然后解锁 Surface,它将缓冲区发送给消费者。在这种情况下,消费者位于同一进程中,称为 SurfaceTexture 或(在内部,更恰当)GLConsumer。它将缓冲区转换为 OpenGL ES 纹理,然后渲染到视图。
如果关闭硬件加速,GLES 将被禁用,TextureView 将无法执行任何操作。这就是为什么当你关闭硬件加速时你什么也得不到的原因。文档非常具体:“TextureView只能在硬件加速窗口中使用。当在软件中渲染时,TextureView将不绘制任何内容。”
如果指定脏矩形,软件渲染器会将以前的内容memcpy到帧中after渲染完成。我不相信它设置了剪辑矩形,因此如果您调用drawColor(),您将填充整个屏幕,然后覆盖这些像素。如果您当前没有设置剪辑矩形,您可能会发现这样做会带来一些性能优势。 (不过我没有检查代码。)
脏矩形是一个输入输出参数。当你打电话时你传递了你想要的矩形lockCanvas()
,并且允许系统在调用返回之前对其进行修改。 (实际上,这样做的唯一原因是如果没有先前的框架或调整了 Surface 的大小,在这种情况下它会将其扩展以覆盖整个屏幕。我认为使用更直接的方法可以更好地处理这个问题“我拒绝你的矩形”信号。)你需要更新你返回的矩形内的每个像素。你是not允许改变矩形,您似乎试图在示例中执行此操作 - 无论之后的脏矩形中是什么lockCanvas()
成功是你需要借鉴的。
我怀疑肮脏的矩形处理不当是你闪烁的根源。可悲的是,这是一个很容易犯的错误,因为lockCanvas()
dirtyRect
arg 仅记录在表面类 itself.
表面和缓冲
所有表面都是双缓冲或三缓冲的。没有办法解决这个问题——你不能同时读写而不被撕裂。如果您想要一个可以在需要时修改和推送的缓冲区,则需要锁定、复制和解锁该缓冲区,这会在组合管道中造成停顿。为了获得最佳吞吐量和延迟,翻转缓冲区更好。
如果您想要锁定-复制-解锁行为,您可以自己编写(或找到一个可以执行此操作的库),并且它将与系统为您执行此操作一样高效(假设您擅长使用blit 循环)。绘制到离屏 Canvas 并位图位图,或者绘制到 OpenGL ES FBO 并位图位图。您可以在 Grafika 的“中找到后者的示例”记录总帐应用程序“ Activity,其模式是在屏幕外渲染一次,然后进行两次位图传输(一次用于显示,一次用于录制视频)。
更快的速度等等
在 Android 上绘制像素有两种基本方法:使用 Canvas 或使用 OpenGL。 Canvas 渲染到 Surface 或位图始终在软件中完成,而 OpenGL 渲染则通过 GPU 完成。唯一的例外是,当渲染到自定义视图,您可以选择使用硬件加速,但这不适用于渲染到SurfaceView或TextureView的Surface。
绘图或绘画应用程序可以记住绘图命令,或者只是将像素放入缓冲区并将其用作内存。前者允许更深入的“撤消”,后者更简单,并且随着要渲染的内容量的增加而具有越来越好的性能。听起来你想做后者,所以从屏幕外传输是有道理的。
大多数移动设备对 GLES 纹理的硬件限制为 4096x4096 或更小,因此您无法将单个纹理用于更大的纹理。您可以查询大小限制值(GL_MAX_TEXTURE_SIZE),但是您最好使用一个与您想要的一样大的内部缓冲区,并且只渲染适合屏幕的部分。我不知道 Skia(画布)的限制是什么,但我相信您可以创建更大的位图。
根据您的需求,SurfaceView 可能比 TextureView 更好,因为它避免了中间的 GLES 纹理步骤。您在 Surface 上绘制的任何内容都会直接进入系统合成器 (SurfaceFlinger)。这种方法的缺点是,由于 Surface 的使用者不是进程内的,因此 View 系统没有机会处理输出,因此 Surface 是一个独立的层。 (对于绘图程序来说,这可能是有益的——正在绘制的图像位于一层上,而您的 UI 位于顶部的单独层上。)
FWIW,我没有看过代码,但是 Dan Sandler 的Markers应用程序可能值得一看(源代码here).
Update:腐败是被识别为错误 and 固定为“L”.