重画等操作,最终由SurfaceFlinger把所有的Layer组合到一起,显示到显示器上。
一、Surface的创建过程:
请看如下序列图:
那么创建 Surface 主要分为两个步骤:
1、建立 SurfaceSession
主要利用 SurfaceComposerClient 实例,调用 sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection() 返回一个 ISurfaceFlingerClient接口给
SurfaceComposerClient,并在createConnection的过程中,SurfaceFlinger创建了用于管理缓冲
区切换的SharedClient。最后,本地层把SurfaceComposerClient的实例返回给 JAVA层,完成
SurfaceSession的建立。 这个过程一般只执行一次。
2、利用 SurfaceSession 创建 Surface
在 WindowManagerService.java 中创建不同属性的 Surface ,目前有如下三种类型:
(Surface.java 中定义)
public static final int FX_SURFACE_NORMAL = 0x00000000;
public static final int FX_SURFACE_BLUR = 0x00010000;
public static final int FX_SURFACE_DIM = 0x00020000;
一般如此使用:
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
mAttrs.getTitle().toString(),
0, w, h, mAttrs.format, flags)
比如没有硬件光标层就是在此创建一个Surface用于鼠标移动的显示,作为鼠标移动时的画布。
SurfaceFlinger::createSurface
根据flags创建不同类型的Layer,然后调用Layer的setBuffers()方法, 为该Layer创建了两个缓冲区,
然后返回该Layer的ISurface接口,SurfaceComposerClient使用这个 ISurface接 口创建一个
SurfaceControl实例,并把这个SurfaceControl返回给JAVA层。
重要代码:
sp<Layer> layer = new Layer(this, display, client, id);
status_t err = layer->setBuffers(w, h, format, flags);
...
return layer ;
二、获得Surface对应的显示缓冲区:
SurfaceFlinger在创建Layer时已经为每个Layer申请了两个缓冲区,但是此时在JAVA层并看不到这
两个缓冲区, JAVA层要想在Surface上进行画图操作,必须要先把其中的一个缓冲区绑定到Canvas
中,然后所有对该Canvas的画图操作最后都会画到该缓冲区内.
重要的几个操作分析:
/** draw into a surface */
lockCanvas -> lockCanvasNative() --> JNI 函数
static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
// 利用 SurfaceControl对象,通过getSurface() 取得本地层的Surface对象
const sp<Surface>& surface(getSurface(env, clazz));
// 利用dequeueBuffer(&backBuffer) 返回该Surface的信息,其中包含 Surface 缓冲区的
// 首地址vaddr, 最后调用到libgralloc.so 动态库中去了,这个后面分析
status_t err = surface->lock(&info, &dirtyRegion);
==>
void* vaddr;
status_t res = backBuffer->lock(
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
newDirtyRegion.bounds(), &vaddr);
...
// 需要将 Surface 转换成 Bitmap 对象,这个JAVA对象直接在Canvas画图,其所有操作都会在以
// vaddr为首的缓冲区中, 两个对象的关系通过setBitmapDevice建立
SkBitmap bitmap;
ssize_t bpr = info.s * bytesPerPixel(info.format);
bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr);
if (info.format == PIXEL_FORMAT_RGBX_8888) {
bitmap.setIsOpaque(true);
}
if (info.w > 0 && info.h > 0) {
bitmap.setPixels(info.bits);
} else {
// be safe with an empty bitmap.
bitmap.setPixels(NULL);
}
nativeCanvas->setBitmapDevice(bitmap); // bitmap绑定到Canvas中...
}
下面对 Surface::lock 补充说明一下:
a、dequeueBuffer(&backBuffer)获取backBuffer
SharedBufferClient->dequeue()获得当前空闲缓冲区的编号
通过缓冲区编号获得真正的GraphicBuffer:backBuffer
如果还没有对Layer中的buffer进行映射(Mapper),getBufferLocked通过ISurface接口重新重新映射
sp<GraphicBuffer> buffer = s->requestBuffer(index, usage);
b、获取frontBuffer --> const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
c、根据两个Buffer的更新区域,把frontBuffer的内容拷贝到backBuffer中,这样保证了两个Buffer中显示内容同步
利用 copyBlt(backBuffer, frontBuffer, copyback); --> 这里通过 memcpy,效率上是否有影响?
d、backBuffer->lock() 获得backBuffer缓冲区的首地址vaddr,最后通过info参数返回vaddr
三、释放Surface对应的显示缓冲区
画图完成后,要想把Surface的内容显示到屏幕上,需要把Canvas中绑定的缓冲区释放,并且把该缓冲区从
变成可投递(因为默认只有两个 buffer,所以实际上就是变成了frontBuffer),SurfaceFlinger的工作线程会
在适当的刷新时刻,把系统中所有的 frontBuffer混合在一起,然后通过OpenGL刷新到屏幕上。
public native void unlockCanvasAndPost(Canvas canvas);
JNI 操作:
static void Surface_unlockCanvasAndPost(
JNIEnv* env, jobject clazz, jobject argCanvas)
{
const sp<Surface>& surface(getSurface(env, clazz));
// 绑定一个空的bitmap到Canvas中
nativeCanvas->setBitmapDevice(SkBitmap());
// unlock surface
status_t err = surface->unlockAndPost();
}
status_t Surface::unlockAndPost()
{
status_t err = mLockedBuffer->unlock();
err = queueBuffer(mLockedBuffer.get());
}
调用Surface的unlockAndPost方法
1、 调用GraphicBuffer的unlock(),解锁缓冲区
2、queueBuffer()调用了SharedBufferClient的queue(),把该缓冲区更新为可投递状态
================= 小概念==========================================
画图需要“四大金钢”相互合作:
Bitmap: 用于存储像素,也就是画布,可把它当做一块数据存储区域
Canvas: 用于记载画图的动作,比如画一个圆,矩形等,提供这些基本的绘图函数
Paint: 用于描述绘画时使用的颜色,风格(如实线,虚线)等
Drawing primitive: 绘图基元,如矩形,圆,文本,图片等