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

應用程序中的每個窗口,對應本地代碼中的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: 繪圖基元,如矩形,圓,文本,圖片等


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章