android graphics

本文详细解析了 Android 中 SurfaceView 的双缓冲机制工作原理,指出为了防止屏幕闪烁,开发者必须每次绘制整个 Surface 的后缓冲区。文章还提供了一种解决方案,即先将所有渲染内容绘制到自定义位图中,再将其复制到后缓冲区。
> Hi All,
> I've been using SurfaceView and Surface for one of my graphics
> project.
> The problem keep bothering me is that the content in Surface's two
> graphic buffers does not synchronized so that when the "lockCanvas-
> drawCanvas-unlockCanvas" cycle is running, it shows different content
> and generate screen flicker.

Here is some information on how the Android graphics system works:

In Android, every window is implemented with an underlaying Surface
object. A Surface is an object that gets composited onto the
framebuffer by SurfaceFlinger, the sytem-wide screen composer.
Each Surface is double-buffered, it has a back buffer which is where
drawing takes place and a front buffer which is used for composition.

When "unlockCanvas()" is called, the back buffer is "posted", which
means it's being displayed and  becomes available again.

However, the *implementation* of this mechanism is done by swapping
the front and back buffers. The back buffer becomes the front buffer
and vice-versa.

This mechanism ensures that there is a minimal amount of buffer
copying (a post operation doesn't move any pixels around) and that
there is always a buffer for SurfaceFlinger to use for composition.
This last feature is very important because it ensures that the screen
never flickers and never shows any artifacts *and* that
SurfaceFlinger never has to wait for a Surface to be ready (its front
buffer is always ready by definition), this means that a badly written
application cannot slowdown or interfere with other application's
windows.

As a side note, it is worth noting that the main screen itself (the
frame buffer) is double-buffered as well and uses the same posting
mechanism than any regular surface.

It should appear now that because of this posting mechanism,
incremental updates are not supported; and this is /the/ key point.

Back to your question now:

> After doing a bit research, I got my conclusion (not sure if it is
> correct though) here:
> 1. There are two separate buffers of each Surface object. According to
> SDK, they were called "front buffer" and "back buffer".

> 2. Android's Surface does not behaves like regular "double-buffer"
> system, which means: draw everything in back buffer and then flip the
> back buffer into the front buffer.
> What Android Surface does seems to be: draw in buffer A and flip it on
> the screen, then in next cycle, draw buffer B and flip it on the
> screen. As the result, the content visible on the screen comes from
> buffer A, B, A, B, A, .... I'm using buffer "A" and "B" here instead
> of "front" and "back" because I think they have equal role in
> rendering cycle.

> 3. The Surface buffer's alternate work style causes problem when I'm
> doing partial update (repaint only a portion of the Surface) for the
> performance purpose. Because one "lockCanvas-drawCanvas-unlockCanvas"
> cycle only update one buffer, the other one remains untouched, then
> when next "lockCanvas-drawCanvas-unlockCanvas" comes, it shows you an
> old content on the screen so that the flicker happens.

You are correct in your analysis. Something that the documentation
doesn't emphasis clearly is that with such a mechanism, your *must*
draw every single pixel of the Surface's back buffer each-time. In
essence, the back buffer is not preserved in post. This is a common
behavior  of hardware-accelerated graphics systems.

There is fact an API on the underlaying Surface object that allows to
update only a portion of the back buffer (but even then, every pixel
of that portion need to be redrawn). It is implemented by copying back
the former back buffer (now in the front) to the new back buffer
(formerly in the front). Unfortunately, this API is not exposed to
SurfaceView at the moment. I'm hoping to have this feature added
eventually.

> 4. My work around (works but with tremendous performance penalty)
> When I'm doing partial Surface update, I do the following thing in
> each rendering cycle:

The best solution available right now to solve this problem, is to do
all of your rendering into your own bitmap and simply copy it into the
back buffer:

- create a bitmap
- attach a canvas to it
- do the rendering into that canvas
- lockCanvas
- draw your bitmap into the backbuffer
- unlockAndPost

Note that this method involves an extra copy of the whole buffer size,
but it is in fact not slower than what it would have been if the
posting mechanism worked as you expected/suggested.

> 5. In order to improve my solution a bit, I try to use
> "s.unlockCanvas(b);" instead of "s.unlockCanvasAndPost(b);" because I
> really do not need to make the second buffer flipping, I just need to
> synchronize its content. However, unlockCanvas() generates a "Illegal
> argument" exception. I can not figure out the reason.

I will investigate the error you are reporting. However, this solution
would not have worked because unlockCanvas() is essentially a no-op,
it doesn't post the back buffer, which means it won't make it visible.
It is only intended to be used in very special situations.
> I have noticed someone has met the similar SurfaceView problem, see
> the following topic: http://groups.google.com/group/android-developers/browse_thread/threa...
> I'm not sure if someone has any better solution than mine?
> Is there a way to notify Surface that I would like to use a fixed
> buffer as front buffer?

There is no way to do that at the moment, but we might add this
functionality eventually. Note that in that case, your Surface will
suffer from tearing, so it won't be suited for Surfaces that do a lot
of animations.
阅读(1786) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
### Android Graphics 官方文档与资源 Android 官方为开发者提供了详尽的图形开发相关文档和资源,帮助开发者更好地理解 Android 图形渲染机制以及优化应用性能。以下是与 Android Graphics 相关的核心官方文档和网站: 1. **Android Developer 官方文档** Android 官方网站中包含了大量关于图形处理的内容,涵盖 2D 和 3D 图形渲染、OpenGL ES 支持、硬件加速等主题。可以访问以下链接获取详细信息: - [Android Graphics Overview](https://developer.android.com/guide/topics/graphics/overview) [^4] 此页面提供了 Android 图形系统的概览,包括视图绘制、Canvas API、Hardware Acceleration 等内容。 - [OpenGL ES](https://developer.android.com/guide/topics/graphics/opengl) [^5] 如果需要进行更底层的图形处理,可以参考此文档了解如何在 Android 中使用 OpenGL ES。 2. **Android 源码编译与图形调试** 对于希望深入了解 Android 图形系统内部实现的开发者,可以参考 Android 源码编译环境配置的相关文档[^2],并通过调试工具如 `systrace` 和 `GPU Debugger` 来分析图形性能问题。 - [Android Debugging Tools](https://developer.android.com/studio/profile) [^6] 提供了多种工具来分析应用的图形性能,例如 GPU 过度绘制(Overdraw)检测和帧时间分析。 3. **Android Jetpack 和 Compose** Android Jetpack 提供了现代 UI 开发框架,特别是 Jetpack Compose,这是一种声明式 UI 工具包,能够显著简化图形界面开发流程。可以参考以下链接: - [Jetpack Compose Documentation](https://developer.android.com/jetpack/compose) [^7] 4. **硬件加速与 GPU 渲染优化** 关于硬件加速和 GPU 渲染优化,可以参考以下内容: - [Optimizing the View Hierarchy](https://developer.android.com/training/improving-layouts/optimizing-view-hierarchy) [^8] 提供了优化视图层次结构以减少绘制开销的指导。 - [Profile GPU Rendering](https://developer.android.com/topic/performance/rendering/profile-gpu-rendering) [^9] 介绍了如何通过 Profile GPU Rendering 工具分析应用的渲染性能。 ### 示例代码:启用硬件加速 如果需要确保应用充分利用硬件加速功能,可以在 `AndroidManifest.xml` 文件中设置以下属性: ```xml <application android:hardwareAccelerated="true"> </application> ``` ### 注意事项 不同移动 GPU 架构对图形渲染的支持有所不同。例如,基于延迟渲染(deferred rendering)的 GPU 可以自动优化过度绘制问题,而立即渲染(immediate rendering)架构则需要开发者手动优化[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值