Android -- SurfaceFlinger Surface创建 系列 (二)

来源:互联网 发布:mac系统更新10.12 编辑:程序博客网 时间:2024/05/16 19:23

应用程序中的每个窗口,对应本地代码中的Surface,而Surface又对应 于SurfaceFlinger中的各个Layer,SurfaceFlinger的主要作用是为这些Layer申请内存,根据应用程序的请求管理这些 Layer显示、隐藏、

重画等操作,最终由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刷新到屏幕上。



 /** unlock the surface and asks a page flip */
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: 绘图基元,如矩形,圆,文本,图片等


原创粉丝点击