android Gui系統之SurfaceFlinger(1)---SurfaceFlinger概論
GUI 是任何系統都很重要的一塊。
android GUI大體分為4大塊。
1)SurfaceFlinger
2)WMS
3)View機制
4)InputMethod
這塊內容非常之多,但是理解後,可以觸類旁通,其實現在主流的系統,包括andorid,ios在構架上,都是有很多相識之處。
我們先來講SurfaceFlinger
1.OpenGL & OpenGL ES
OPenGL ES 是android系統繪畫的基礎。關於OpenGL部分,可以百度瞭解下。
先來看一個OpenGL & SurfaceFlinger之間的框架圖:
從底層往上看:
1)linux核心提供統一的裝置驅動,/dev/graphics/fb*
2) Android HAL 提供2個介面 Gralloc & fb
fb 負責開啟framebuffer,提供介面操作。gralloc負責管理幀緩衝區的分配和釋放。
composer是HAL中另一個重要的功能,它主要是給廠商定製UI合成。SurfaceFlinger中負責HWComposer會用到這個功能。
而且關鍵是HWComposer還負責產生VSync訊號,這是本期SurfaceFlinger的重點。
3)由於OpenGL是一套通用的庫(大部分就是介面),所以它需要一個本地的實現。andorid平臺OpenGL有2個本地視窗,FrameBufferNativeWindow & Surface。
4)OpenGL可以有軟體 或者依託於硬體實現,具體的執行狀態,就是由EGL來配置。
5)SurfaceFlinger持有一個成員陣列mDisplays來支援各種顯示裝置。DisplayDevices在初始化的時候呼叫EGL來搭建OpenGL的環境。
2.Android的硬體介面HAL
HAL需要滿足android系統和廠商的要求
2.1硬體介面的抽象
從面向物件角度來講,介面的概念就是由C++非常容易實現,但是HAL很多程式碼是C語言描述的。
這就需要一種技巧來實現面向物件。
定義一種結構,子類的成員變數第一個型別是父類的結構就可以了。抽象方法可以用函式指標來實現。
其實這個就是C++多型實現的基本原理,具體可參考《深入理解C++物件模型》
2.2介面的穩定性
Android已經把各個硬體都介面都統一定義在:
libhardware/include/hardware/ 具體程式碼可以參考:https://github.com/CyanogenMod/android_hardware_libhardware/tree/cm-12.0/include/hardware
3.Android顯示裝置:Gralloc & FrameBuffer
FrameBuffer是linux環境下顯示裝置的統一介面。從而讓使用者裝置不需要做太多的操作,就可以適配多種顯示裝置。
FramwBuffer本質上就是一套介面。android系統不會直接操作顯示驅動,而通過HAL層來封裝。而HAL中操作驅動的模組就是
gralloc。
3.1Gralloc模組的載入
gralloc通過FrameBufferNativeWindow 來載入的:
FramebufferNativeWindow::FramebufferNativeWindow()
: BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
{
hw_module_t const* module;
if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
int stride;
int err;
int i;
err = framebuffer_open(module, &fbDev);
ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
err = gralloc_open(module, &grDev);
ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));
// bail out if we can't initialize the modules
if (!fbDev || !grDev)
return;
mUpdateOnDemand = (fbDev->setUpdateRect != 0);
// initialize the buffer FIFO
if(fbDev->numFramebuffers >= MIN_NUM_FRAME_BUFFERS &&
fbDev->numFramebuffers <= MAX_NUM_FRAME_BUFFERS){
mNumBuffers = fbDev->numFramebuffers;
} else {
mNumBuffers = MIN_NUM_FRAME_BUFFERS;
}
mNumFreeBuffers = mNumBuffers;
mBufferHead = mNumBuffers-1;
/*
* This does not actually change the framebuffer format. It merely
* fakes this format to surfaceflinger so that when it creates
* framebuffer surfaces it will use this format. It's really a giant
* HACK to allow interworking with buggy gralloc+GPU driver
* implementations. You should *NEVER* need to set this for shipping
* devices.
*/
#ifdef FRAMEBUFFER_FORCE_FORMAT
*((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT;
#endif
for (i = 0; i < mNumBuffers; i++)
{
buffers[i] = new NativeBuffer(
fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
}
for (i = 0; i < mNumBuffers; i++)
{
err = grDev->alloc(grDev,
fbDev->width, fbDev->height, fbDev->format,
GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);
ALOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s",
i, fbDev->width, fbDev->height, strerror(-err));
if (err)
{
mNumBuffers = i;
mNumFreeBuffers = i;
mBufferHead = mNumBuffers-1;
break;
}
}
const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags;
const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
const_cast<int&>(ANativeWindow::minSwapInterval) =
fbDev->minSwapInterval;
const_cast<int&>(ANativeWindow::maxSwapInterval) =
fbDev->maxSwapInterval;
} else {
ALOGE("Couldn't get gralloc module");
}
ANativeWindow::setSwapInterval = setSwapInterval;
ANativeWindow::dequeueBuffer = dequeueBuffer;
ANativeWindow::queueBuffer = queueBuffer;
ANativeWindow::query = query;
ANativeWindow::perform = perform;
ANativeWindow::dequeueBuffer_DEPRECATED = dequeueBuffer_DEPRECATED;
ANativeWindow::lockBuffer_DEPRECATED = lockBuffer_DEPRECATED;
ANativeWindow::queueBuffer_DEPRECATED = queueBuffer_DEPRECATED;
}
我們繼續深入看:
galloc的父類,最終是:
libhardware\include\hardware\hardware.h
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
只有一個open方法,也就是所有的廠商都需要實現開啟裝置的方法。
看下fb的開啟的程式碼:
libhardware\modules\gralloc\framebuffer.cpp
int fb_device_open(hw_module_t const* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {
/* initialize our state here */
fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = fb_close;
dev->device.setSwapInterval = fb_setSwapInterval;
dev->device.post = fb_post;
dev->device.setUpdateRect = 0;
private_module_t* m = (private_module_t*)module;
status = mapFrameBuffer(m);
if (status >= 0) {
int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
int format = (m->info.bits_per_pixel == 32)
? (m->info.red.offset ? HAL_PIXEL_FORMAT_BGRA_8888 : HAL_PIXEL_FORMAT_RGBX_8888)
: HAL_PIXEL_FORMAT_RGB_565;
const_cast<uint32_t&>(dev->device.flags) = 0;
const_cast<uint32_t&>(dev->device.width) = m->info.xres;
const_cast<uint32_t&>(dev->device.height) = m->info.yres;
const_cast<int&>(dev->device.stride) = stride;
const_cast<int&>(dev->device.format) = format;
const_cast<float&>(dev->device.xdpi) = m->xdpi;
const_cast<float&>(dev->device.ydpi) = m->ydpi;
const_cast<float&>(dev->device.fps) = m->fps;
const_cast<int&>(dev->device.minSwapInterval) = 1;
const_cast<int&>(dev->device.maxSwapInterval) = 1;
*device = &dev->device.common;
}
}
return status;
}
首先check裝置名是否正確。
分配dev的空間,這是一個殼。
然後初始化dev。
提供fb的核心介面
記憶體對映
status = mapFrameBuffer(m);
然後是建立殼 & 核心間的關係。
這樣就打開了fb裝置。
在回到FrameBufferNativeWindow 可以看到:
err = framebuffer_open(module, &fbDev);
ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
err = gralloc_open(module, &grDev);
ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));
fb開啟的驅動資訊在fbDev,gralloc開啟的資訊在grDev中。
fbDev負責的是主螢幕,grDev負責圖形緩衝去的分配和釋放。
所以FrameBufferNativeWindow控制這SurfaceFlinger的基礎。
4.FrameBufferNativeWindow
4.1FramebufferNativeWindow
在OpenGL中,我們不斷提及本地視窗的概念,在Android中,native window一共由2個。
一個是面向管理者(SurfaceFlinger)的 FramebufferNativeWindow
另一個是面像APP的,surface。
先來看第一種:
首先看下定義的地方:
class FramebufferNativeWindow
: public ANativeObjectBase<
ANativeWindow,
FramebufferNativeWindow,
LightRefBase<FramebufferNativeWindow> >
{
ANativeWindow是什麼東西?
ANativeWindow是OpenGL 在android平臺的顯示型別。
所以FramebufferNativeWindow就是一種Open GL可以顯示的型別。
FramebufferNativeWindow的建構函式上面已經貼出來了,進一步分析如下:
1)載入module,上面已經分析過了。
2)開啟fb & gralloc,也已經分析過了。
3)根據fb的裝置屬性,獲得buffer數。這個buffer後面會解釋。
4)給每個buffer初始化,並分配空間。這裡new NativeBuffer只是指定buffer的型別,或者分配了一個指標,但是沒有分配記憶體,所以還需要alloc操作。
5)為本地視窗屬性賦值。
目前buffer預設值是在2~3,後面會介紹3緩衝技術,就會用到3個buffer。
雙緩衝技術:
把一組圖畫,畫到螢幕上,畫圖是需要時間的,如果時間間隔比較長,圖片就是一個一個的畫在螢幕的,看上去就會卡。
如果先把圖片放在一個緩衝buffer中,待全部畫好後,把buffer直接顯示在螢幕上,這就是雙緩衝技術。
4.2dequeuebuffer
int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd)
{
FramebufferNativeWindow* self = getSelf(window);
Mutex::Autolock _l(self->mutex);
framebuffer_device_t* fb = self->fbDev;
int index = self->mBufferHead++;
if (self->mBufferHead >= self->mNumBuffers)
self->mBufferHead = 0;
// wait for a free non-front buffer
while (self->mNumFreeBuffers < 2) {
self->mCondition.wait(self->mutex);
}
ALOG_ASSERT(self->buffers[index] != self->front);
// get this buffer
self->mNumFreeBuffers--;
self->mCurrentBufferIndex = index;
*buffer = self->buffers[index].get();
*fenceFd = -1;
return 0;
}
程式碼不多,但是卻是核心功能,通過它來獲取一塊可渲染的buffer。
1)獲取FramebufferNativeWindow物件。為什麼沒有使用this 而是使用了傳入ANativeWindow的方式,此處我們並不關心。
2)獲得一個Autolock的鎖,函式結束,自動解鎖。
3)獲取mBufferHead變數,這裡自增,也就是使用下一個buffer,一共只有3個,(原因上面已經解釋),所以迴圈取值。
4)如果沒有可用的緩衝區,等待bufferqueue釋放。一旦獲取後,可用buffer就自減
5.Surface
Surface是另一個本地視窗,主要和app這邊互動。注意:app層java程式碼無法直接呼叫surface,只是概念上surface屬於app這一層的。
首先Surface是ANativeWindow的一個子類。
可以推測,surface需要解決如下幾個問題:
1)面向上層(java層)提供畫板。由誰來分配這塊記憶體
2)與SurfaceFlinger是什麼關係
Surface::Surface(
const sp<IGraphicBufferProducer>& bufferProducer,
bool controlledByApp)
sp<IGraphicBufferProducer>& bufferProducer 是分配surface記憶體的。它到底是什麼呢?
先來看看從ViewRootImpl到獲取surface的過程。
ViewRootImpl持有一個java層的surface物件,開始是空的。
後續的流程見上面的流程圖。也就是-說ViewRootImpl持有的surface物件,最終是對SurfaceComposerClient的建立的surface的一個“引用”。
由此分析可以看到 一個ISurfaceClient->ISurfaceComposerClient->IGraphicBufferProducer.當然binder需要一個實名的server來註冊。
在ServiceManager中可以看到,這些服務查詢的是“SurfaceFlinger”。
也就是,這些東東都是SurfaceFlinger的內容。
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(),
SurfaceFlinger是BnSurfaceComposer的一個子類。也就是ISurfaceComposer的一個實現。
surface雖然是為app層服務的,但是本質上還是由SurfaceFlinger來管理的。
SurfaceFlinger怎麼建立和管理surface,需要通過BufferQueue,將在下一篇討論。
參考:
《深入理解android核心設計思想》 林學森