根據(jù)官方文檔可知:
SurfaceFlinger
從WindowManager
處接收buffers
和window
相關(guān)數(shù)據(jù)。
然后SurfaceFlinger
將buffers
和window
相關(guān)數(shù)據(jù),合成一個(gè)Layer
酒唉,發(fā)送給WindowManager
,由WindowManager
操控顯示在屏幕上。fling有扔,擲哨颂,拋的意思。flinger是指扔的人相种。SurfaceFlinger
將Surface
扔給WindowManager
威恼。
SurfaceFlinger
的作用就是合成Layer
。
Layer
與Surface
的關(guān)系如下:
Layer = Surface + SurfaceControl
這里其實(shí)關(guān)于surface和layer的解釋并不直觀蚂子。
Android opengl es
的資料遠(yuǎn)不如opengl
多(特別是在我并不想看源碼的情況下)沃测。可以參考opengl
相關(guān)書籍食茎。很多東西找不到詳細(xì)的資料解釋,原因是代碼很多地方并不是原創(chuàng)而是移植馏谨,因此只有使用類文檔别渔,原理并不詳細(xì)。有時(shí)候看技術(shù)文檔惧互,一層層地減少信息量哎媚,到了后來都是精簡版的信息,會(huì)對理解造成阻礙喊儡,甚至有時(shí)候會(huì)造成錯(cuò)誤理解拨与。
Layer
的合成過程這個(gè)過程類似于《OpenGL超級寶典》中的將繪圖坐標(biāo)映射到窗口,而繪圖坐標(biāo)艾猜,就類似于Surface
买喧,SurfaceControl
類似下面的映射的選擇,控制如何映射(例如存在2種不同的映射匆赃,具體如何映射是需要定義的淤毛,window
相關(guān)數(shù)據(jù)例如屏幕大小等在發(fā)送給SurfaceFlinger
后,還需要一個(gè)對象去控制映射)算柳,然后就是繪圖坐標(biāo)進(jìn)行映射低淡,這個(gè)過程相當(dāng)于SurfaceFlinger 使用Surface
+ SurfaceControl
去合成 Layer
,最終顯示在窗口需要的圖形就是 Layer
瞬项。
出處:OpenGL超級寶典第五版第一章 3D圖形和opengl簡介
Surface contain BufferQueue
Surface
包含BufferQueue
蔗蹋。
實(shí)際上Surface
就類似opengl
中的幀緩沖區(qū)對象(FBO:FramebufferObject
)的概念。
由于譯本可能會(huì)導(dǎo)致理解上存在一定誤差囱淋,建議中英文對照去看猪杭。網(wǎng)上均可下載。
例如當(dāng)時(shí)看這本書的時(shí)候绎橘,不太能理解緩沖區(qū)的概念胁孙,后面一看buffer
這個(gè)名詞就比較好理解了唠倦。
FBO
是包含了buffer
的一個(gè)Object
,并不占用存儲(chǔ)空間涮较,真正占用存儲(chǔ)空間的是buffer
稠鼻,這個(gè)buffer
存儲(chǔ)了可以渲染的數(shù)據(jù)(例如RGB
或YUV
等數(shù)據(jù)),這個(gè)Object
在Android
中定義為Surface
類狂票,Surface
存儲(chǔ)的buffer
是GraphicBuffer
,GraphicBuffer
存儲(chǔ)在BufferQueue
中候齿。如果想深入了解
GraphicBuffer
可以參考這篇:Android P 圖像顯示系統(tǒng)(二)GraphicBuffer和Gralloc分析關(guān)于
BufferQueue
可以參考:深入淺出Android BufferQueueBufferQueue分析:Buffer隊(duì)列
我并沒有怎么看懂,粗略看了下闺属,C++看的我頭痛慌盯。回想起來跟之前的一個(gè)做c++的同事聯(lián)調(diào)代碼的時(shí)候?qū)Ψ奖硎緅ava看得也很頭痛掂器。語言有時(shí)候真的是很大障礙亚皂,至少對我來說是這樣。
出處:https://source.android.com/devices/graphics/arch-bq-gralloc
使用方創(chuàng)建并擁有 BufferQueue 數(shù)據(jù)結(jié)構(gòu)国瓮,并且可存在于與其生產(chǎn)方不同的進(jìn)程中灭必。當(dāng)生產(chǎn)方需要緩沖區(qū)時(shí),它會(huì)通過調(diào)用dequeueBuffer()
從 BufferQueue 請求一個(gè)可用的緩沖區(qū)乃摹,并指定緩沖區(qū)的寬度禁漓、高度、像素格式和使用標(biāo)記孵睬。然后播歼,生產(chǎn)方填充緩沖區(qū)并通過調(diào)用queueBuffer()
將緩沖區(qū)返回到隊(duì)列。接下來掰读,使用方通過acquireBuffer()
獲取該緩沖區(qū)并使用該緩沖區(qū)的內(nèi)容秘狞。當(dāng)使用方操作完成后,它會(huì)通過調(diào)用releaseBuffer()
將該緩沖區(qū)返回到隊(duì)列磷支。同步框架可控制緩沖區(qū)在 Android 圖形管道中移動(dòng)的方式谒撼。
BufferQueue 的一些特性(例如可以容納的最大緩沖區(qū)數(shù))由生產(chǎn)方和使用方聯(lián)合決定。但是雾狈,BufferQueue 會(huì)根據(jù)需要分配緩沖區(qū)廓潜。除非特性發(fā)生變化,否則將會(huì)保留緩沖區(qū)善榛;例如辩蛋,如果生產(chǎn)方請求具有不同大小的緩沖區(qū),則系統(tǒng)會(huì)釋放舊的緩沖區(qū)移盆,并根據(jù)需要分配新的緩沖區(qū)悼院。
BufferQueue 永遠(yuǎn)不會(huì)復(fù)制緩沖區(qū)內(nèi)容,因?yàn)橐苿?dòng)如此多的數(shù)據(jù)是非常低效的操作咒循。相反据途,緩沖區(qū)始終通過句柄進(jìn)行傳遞绞愚。
出處:https://source.android.com/devices/graphics/arch-sh
用于顯示 Surface 的 BufferQueue 通常配置為三重緩沖。緩沖區(qū)是按需分配的颖医,因此位衩,如果生產(chǎn)方足夠緩慢地生成緩沖區(qū)(例如在 60 fps 的顯示屏上以 30 fps 的速度進(jìn)行緩沖),隊(duì)列中可能只有兩個(gè)分配的緩沖區(qū)熔萧。按需分配緩沖區(qū)有助于最大限度地減少內(nèi)存消耗糖驴。您可以看到與 dumpsys SurfaceFlinger 輸出中每個(gè)層級相關(guān)的緩沖區(qū)的摘要。
簡單來說就是:Buffer
隊(duì)列中存在一個(gè)或多個(gè)buffer
(按需分配佛致,通常是三重緩沖)贮缕,所謂3重緩沖,就是使用3個(gè)buffer去存儲(chǔ)和處理數(shù)據(jù)俺榆,2重緩沖就是使用2個(gè)buffer感昼。BufferQueue
通過handle
進(jìn)行傳遞buffer中的內(nèi)容而不是copy(類似于handle+MessageQueue)。
下面來看看surface的雙緩沖罐脊。
//frameworks\native\libs\gui\Surface.cpp
status_t Surface::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
if (mLockedBuffer != 0) {
ALOGE("Surface::lock failed, already locked");
return INVALID_OPERATION;
}
if (!mConnectedToCpu) {
int err = Surface::connect(NATIVE_WINDOW_API_CPU);
if (err) {
return err;
}
// we're intending to do software rendering from this point
setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
}
ANativeWindowBuffer* out;
int fenceFd = -1;
status_t err = dequeueBuffer(&out, &fenceFd);
ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
if (err == NO_ERROR) {
sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
const Rect bounds(backBuffer->width, backBuffer->height);
Region newDirtyRegion;
if (inOutDirtyBounds) {
newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
newDirtyRegion.andSelf(bounds);
} else {
newDirtyRegion.set(bounds);
}
// figure out if we can copy the frontbuffer back
const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
const bool canCopyBack = (frontBuffer != 0 &&
backBuffer->width == frontBuffer->width &&
backBuffer->height == frontBuffer->height &&
backBuffer->format == frontBuffer->format);
if (canCopyBack) {
// copy the area that is invalid and not repainted this round
const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
if (!copyback.isEmpty()) {
copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);
}
} else {
// if we can't copy-back anything, modify the user's dirty
// region to make sure they redraw the whole buffer
newDirtyRegion.set(bounds);
mDirtyRegion.clear();
Mutex::Autolock lock(mMutex);
for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
mSlots[i].dirtyRegion.clear();
}
}
{ // scope for the lock
Mutex::Autolock lock(mMutex);
int backBufferSlot(getSlotFromBufferLocked(backBuffer.get()));
if (backBufferSlot >= 0) {
Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion);
mDirtyRegion.subtract(dirtyRegion);
dirtyRegion = newDirtyRegion;
}
}
mDirtyRegion.orSelf(newDirtyRegion);
if (inOutDirtyBounds) {
*inOutDirtyBounds = newDirtyRegion.getBounds();
}
void* vaddr;
status_t res = backBuffer->lockAsync(
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
newDirtyRegion.bounds(), &vaddr, fenceFd);
ALOGW_IF(res, "failed locking buffer (handle = %p)",
backBuffer->handle);
if (res != 0) {
err = INVALID_OPERATION;
} else {
mLockedBuffer = backBuffer;
outBuffer->width = backBuffer->width;
outBuffer->height = backBuffer->height;
outBuffer->stride = backBuffer->stride;
outBuffer->format = backBuffer->format;
outBuffer->bits = vaddr;
}
}
return err;
}
以下出處:顯示緩沖區(qū)的作用
status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
{
......
android_native_buffer_t* out;
// 分配新的內(nèi)存空間并將其加入緩沖隊(duì)列抑诸,返回給out
status_t err = dequeueBuffer(&out);
if (err == NO_ERROR) {
// 從剛才得到的buffer創(chuàng)建GraphicBuffer對象,
// 該對象是用來更新顯示的緩沖區(qū)爹殊,叫做背景緩沖區(qū)。
// 重畫動(dòng)作在背景緩沖區(qū)進(jìn)行奸绷。
sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
// 鎖定這片內(nèi)存
err = lockBuffer(backBuffer.get());
if (err == NO_ERROR) {
const Rect bounds(backBuffer->width, backBuffer->height);
const Region boundsRegion(bounds);
Region scratch(boundsRegion);
// newDirtyRegion是需要重畫的區(qū)域
Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch);
newDirtyRegion &= boundsRegion;
// 已經(jīng)顯示出來的frontBuffer叫做前景緩沖區(qū)
// 判斷是否需要拷貝frontBuffer到backBuffer
const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
const bool canCopyBack = (frontBuffer != 0 &&
backBuffer->width == frontBuffer->width &&
backBuffer->height == frontBuffer->height &&
backBuffer->format == frontBuffer->format &&
!(mFlags & ISurfaceComposer::eDestroyBackbuffer));
mDirtyRegion = newDirtyRegion;
// 如果需要做拷貝動(dòng)作梗夸,則將frontBuffer中非newDirtyRegion區(qū)域
// 拷貝到backBuffer中
if (canCopyBack) {
const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));
if (!copyback.isEmpty())
copyBlt(backBuffer, frontBuffer, copyback);
} else {
// 如果不需要拷貝,則重畫整個(gè)區(qū)域
newDirtyRegion = boundsRegion;
}
mOldDirtyRegion = newDirtyRegion;
// 鎖定將要畫圖的緩沖區(qū)号醉,并返回一個(gè)地址給調(diào)用者
void* vaddr;
status_t res = backBuffer->lock(
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
newDirtyRegion.bounds(), &vaddr);
// 返回給SurfaceInfo參數(shù)other
mLockedBuffer = backBuffer;
other->w = backBuffer->width;
other->h = backBuffer->height;
other->s = backBuffer->stride;
other->usage = backBuffer->usage;
other->format = backBuffer->format;
other->bits = vaddr;
}
}
mApiLock.unlock();
return err;
}
從注釋中大致可以了解lock函數(shù)中做了些什么操作反症。surface的雙緩沖,關(guān)鍵在于Surface.cpp中的lock函數(shù)中的操作畔派。
用文字表述流程可以參考下面铅碍,這就是雙緩沖的具體過程∠咭可以對照代碼多看幾遍胞谈。
前景就是已經(jīng)顯示的,后景是未顯示的憨愉。如果一個(gè)buffer已顯示烦绳,那么它就是frontBuffer ,如果未顯示那就是backBuffer配紫,frontBuffer 和backBuffer在使用過程中是會(huì)翻轉(zhuǎn)的径密。
出處:SurfaceView 的雙緩沖
系統(tǒng)先從 buffer 池中 dequeueBuffer 出來一個(gè)可用的 out,然后將 out 賦給 backBuffer躺孝。mPostedBuffer 為已經(jīng)顯示的 buffer享扔,將 mPostedBuffer 的內(nèi)容賦給 frontBuffer
例如底桂,存在一個(gè)地址為0-8的空間。
執(zhí)行
lock
函數(shù)過程中惧眠,假設(shè)此時(shí)顯示的buffer
數(shù)據(jù)為0010籽懦,待顯示的數(shù)據(jù)為0001。如下所示锉试。即backBuffer
為0001猫十,frontBuffer
為0010。此時(shí)為0-3顯示front呆盖,4-7顯示back拖云。這里的數(shù)據(jù)是隨便寫的。實(shí)際數(shù)據(jù)是使用Canvas应又、OpenGL ES 或 Vulkan等去生成宙项。
出處:https://source.android.com/devices/graphics#image_stream_producers
應(yīng)用開發(fā)者可通過三種方式將圖像繪制到屏幕上:使用 Canvas、OpenGL ES 或 Vulkan株扛。無論開發(fā)者使用什么渲染 API尤筐,一切內(nèi)容都會(huì)渲染到Surface
。
一般使用Activity
顯示View
是通過Canvas
去產(chǎn)生圖像數(shù)據(jù)洞就。
可參考這篇:探究Android View 繪制流程盆繁,Canvas 的由來
View的繪制過程中會(huì)調(diào)用這句生成canvas。
final DisplayListCanvas canvas = renderNode.start(width, height);
然后會(huì)進(jìn)入c++的領(lǐng)域旬蟋,最后應(yīng)該會(huì)進(jìn)入BufferQueue油昂,供surface.cpp調(diào)用。大致流程就這樣倾贰。
關(guān)于渲染等概念冕碟,建議通讀OpenGL
與計(jì)算機(jī)圖形學(xué)等相關(guān)書籍,簡單了解計(jì)算機(jī)圖形是如何到屏幕上的匆浙。
當(dāng)backbuffer
經(jīng)SurfaceFlinger
合成Layer
后安寺,back和front就進(jìn)行了交換,0-3顯示front首尼,4-7顯示back挑庶。
當(dāng)再產(chǎn)生新的數(shù)據(jù)時(shí),例如0011饰恕,賦值給back挠羔。
然后再合成Layer顯示后再交換。
然后再次產(chǎn)生新數(shù)據(jù)0111埋嵌,函數(shù)中會(huì)執(zhí)行賦值給back破加。
然后在backbuffer顯示后,front和back再次進(jìn)行交換雹嗦。
數(shù)據(jù)變化為:0001 0010 -> 0001 0011 -> 0111 0011范舀,這就是雙緩沖的簡化版流程合是。
使用了2個(gè)buffer,當(dāng)一個(gè)空間用于合成圖形時(shí)锭环,另一個(gè)空間用于接收產(chǎn)生的數(shù)據(jù)聪全,作用是改善卡頓。
但是僅僅這樣還不夠完善辅辩,因此還需要使用垂直同步(VSync)难礼,具體原因可參考這篇Android圖形顯示系統(tǒng)(一)
。
SurfaceFlinger
與BufferQueue
的關(guān)系:SurfaceFlinger
將buffer
和window
相關(guān)數(shù)據(jù)玫锋,合成一個(gè)Layer
蛾茉,而這個(gè)buffer
是SurfaceFlinger
從BufferQueue
獲取(調(diào)用dequeueBuffer獲取buffer)撩鹿,然后放到backBuffer中的backBuffer谦炬。
后記:這篇文章雖然字?jǐn)?shù)不多,但是其實(shí)花了好幾天才寫完节沦,看一篇文章雖然很快键思,但是真正理解有時(shí)候并沒有沒有那么容易。部分知識(shí)點(diǎn)其實(shí)還是存疑甫贯,例如具體運(yùn)行過程吼鳞,BufferQueue的運(yùn)行原理等。但是由于這篇文的初衷是surface的雙緩沖叫搁,以及surface到底是什么赖条,并且由于c++的代碼我很難深入看下去,因此沒有深入下去常熙。最后,由于本人知識(shí)局限性碱茁,內(nèi)容可能會(huì)存在錯(cuò)誤裸卫,如果發(fā)現(xiàn)了,希望能指出纽竣,防止誤導(dǎo)他人墓贿。
參考鏈接:
AndroidO 下圖形顯示框架變化介紹
https://source.android.com/devices/graphics/arch-sh
SurfaceView 的雙緩沖
深入淺出Android BufferQueue
深入理解Android:卷1_8.4.5 lockCanvas和unlockCanvasAndPost分析
SurfaceView的雙緩沖機(jī)制
顯示緩沖區(qū)的作用
Android_GDI基本框架and Surface Flinger
隊(duì)列
Android圖形顯示系統(tǒng)(一)
Android graphics 學(xué)習(xí)-生產(chǎn)者、消費(fèi)者蜓氨、BufferQueue介紹