重畫等操作,最終由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: 繪圖基元,如矩形,圓,文本,圖片等