轉載自http://blog.csdn.net/windskier/article/details/7030732
這周抽空研究了一下SurfaceFlinger,發現真正複雜的並不是SurfaceFlinger本身,而是android的display顯示系統,網上關於這部分的介紹有不少,本不打算寫的,但是發現還是記錄一下研究代碼的過程比較好,一是能夠幫助自己理清思路,另一個原因就是以後當這塊內容忘記的時候,能快速的通過這個記錄撿起來。
一. android顯示系統的建立
SurfaceFlinger對於顯示的管理是通過一個或多個GraphicPlane對象(目前android只實現了一個)來管理的,
@SurfaceFlinger.h
GraphicPlane mGraphicPlanes[1];
其實,GraphicPlane類只是一個wrapper層,目的是當android支持多個顯示系統時,通過該類來管裏各自的圖形系統,顯示系統真正的初始化工作是通過DisplayHardware類來初始化底層圖形系統的管理與顯示的。真正的圖形顯示系統的初始化在init()@DisplayHardware.cpp
1. FrameBuffer的建立
framebuffer,確切的是說是linux下的framebuffer,,它是linux圖形顯示系統中一個與圖形硬件無關的抽象層,user完全不用考慮我們的硬件設備,而僅僅使用framebuffer就可以實現對屏幕的操作。
android的framebuffer並沒有被SurfaceFlinger直接使用,而是在framebuffer外做了一層包裝,這個包裝就是FramebufferNativeWindow,我們來看一下FramebufferNativeWindow的創建過程。
我們的framebuffer是由一個設備符fbDev來表示的,它是FramebufferNativeWindow的一個成員,我們來分析一下對fbDev的處理過程。
1.1. fbDev設備符
1.1.1 gralloc library
在這之前,先介紹一下gralloc library,它的形態如grallocBOARDPLATFORM.so, BOARDPLATFORM可以從屬性ro.board.platform中獲得,這篇文章中我們以Qualcomm msmx7x30爲例,也就是gralloc.msm7x30.so中,它的源路徑在hardware/msm7k/libgralloc-qsd8k。
framebuffer的初始化需要通過HAL gralloc.msm7x30.so 來完成與底層硬件驅動的適配,但是gralloc library並不是平臺無關的,不同的vendor可能會實現自己的gralloc library,因此爲了保證在創建framebuffer時能夠平臺無關,android只能是動態的判斷並使用當前的gralloc library,android通過從gralloc library中再抽象出一個hw_module_t結構來供使用,它爲framebuffer的初始化提供了需要的gralloc.msm7x30.so業務。因此通過這個hw_module_t結構我們就不需要知道當前系統使用的到底是哪個gralloc library。按規定,所有gralloc library中的這個結構體被命名爲HAL_MODULE_INFO_SYM(HMI)。當前分析的系統中,HAL_MODULE_INFO_SYM在hardware/msm7k/libgralloc-qsd8k/galloc.cpp。
1.1.2 打開fbDev設備符
下面看如何打開 打開fbDev設備符。通過HAL_MODULE_INFO_SYM提供的gralloc.msm7x30.so的接口我們調用到了fb_device_open()@hardware/msm7k/libgralloc-qsd8kframebuffer.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)) {
alloc_device_t* gralloc_device;
status = gralloc_open(module, &gralloc_device);
/* 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;
private_module_t* m = (private_module_t*)module;
status = mapFrameBuffer(m);
}
在這個函數中,主要爲fbDev設備符指定一個fb_context_t實例,並通過函數mapFrameBuffer()對設備節點/dev/graphics/fb0進行操作,操作的目的有:
1.獲得屏幕設備的信息,並將屏幕信息保存在HAL_MODULE_INFO_SYM(上面代碼中的module)中。
2. 向/dev/graphics/fb0請求page flip模式,page flip模式需要至少2個屏幕大小的buffer,page flip模式在後面介紹。目前android系統中設置爲2個屏幕大小的buffer。當然屏幕設備可能不支持page flip模式。
mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp
/*
* Request NUM_BUFFERS screens (at lest 2 for page flipping)
*/
info.yres_virtual = info.yres * NUM_BUFFERS;
uint32_t flags = PAGE_FLIP;
if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {
info.yres_virtual = info.yres;
flags &= ~PAGE_FLIP;
LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
}
3. 映射屏幕設備緩存區給fbDev設備符。
mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp
/*
* map the framebuffer
*/
int err;
size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);
module->framebuffer = new private_handle_t(dup(fd), fbSize,
private_handle_t::PRIV_FLAGS_USES_PMEM);
module->numBuffers = info.yres_virtual / info.yres;
module->bufferMask = 0;
void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (vaddr == MAP_FAILED) {
LOGE("Error mapping the framebuffer (%s)", strerror(errno));
return -errno;
}
module->framebuffer->base = intptr_t(vaddr);
memset(vaddr, 0, fbSize);
1.2 grDev設備符
在爲framebuffer,也就是FramebufferNativeWindow申請內存之前,我們還要介紹一個概念,就是grDev設備符。它雖然也叫設備符,但是它和具體的設備沒有直接關係,我們看它的類型就是知道了alloc_device_t,沒錯,grDev設備符就是爲了FramebufferNativeWindow管理內存使用的。爲FramebufferNativeWindow提供了申請/釋放內存的接口。
1.3 FramebufferNativeWindow內存管理
sp<NativeBuffer> buffers[2];
1.3.1 屏幕設備支持page filp模式
// create a "fake" handles for it
intptr_t vaddr = intptr_t(m->framebuffer->base);
private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size,
private_handle_t::PRIV_FLAGS_USES_PMEM |
private_handle_t::PRIV_FLAGS_FRAMEBUFFER);
// find a free slot
for (uint32_t i=0 ; i<numBuffers ; i++) {
if ((bufferMask & (1LU<<i)) == 0) {
m->bufferMask |= (1LU<<i);
break;
}
vaddr += bufferSize;
}
hnd->base = vaddr;
hnd->offset = vaddr - intptr_t(m->framebuffer->base);
*pHandle = hnd;
1.3.2 屏幕設備不支持page flip模式
gralloc_alloc_framebuffer_locked()@hardware/msm7k/libgralloc-qsd8k/gpu.cpp
const uint32_t bufferMask = m->bufferMask;
const uint32_t numBuffers = m->numBuffers;
const size_t bufferSize = m->finfo.line_length * m->info.yres;
if (numBuffers == 1) {
// If we have only one buffer, we never use page-flipping. Instead,
// we return a regular buffer which will be memcpy'ed to the main
// screen when post is called.
int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D;
return gralloc_alloc_buffer(bufferSize, newUsage, pHandle);
}
2. 打開Overlay
if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
overlay_control_open(module, &mOverlayEngine);
}
3. 選擇OpenGL ES library(也即軟/硬件加速)
0 0 android
0 1 adreno200
因此只要我們的移動設備芯片集成了GPU,並提供了對應的GL圖形庫,那麼我們就可以在我們的工程中device目錄下的egl.cfg文件中加入類似上面的配置,那麼我們的系統就會支持硬件加速。
libGLESv1_CM_adreno200.so
libGLESv2_adreno200.so
libEGL_adreno200.so
那麼假如我們的系統中軟硬件加速都支持了,那麼我們從代碼來看能不能讓用戶自由的選擇加速類型,我們帶着問題來研究一下代碼。
3.1 OpenGL初始化
frameworks/base/opengl/libs/EGL/egl.cpp
static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
enum {
IMPL_HARDWARE = 0,
IMPL_SOFTWARE,
IMPL_NUM_IMPLEMENTATIONS
};
gEGLImpl[IMPL_HARDWARE]中保存着硬件圖形設備的OpenGL api地址,從
libGLESv1_CM_adreno200.so
libGLESv2_adreno200.so
libEGL_adreno200.so
這3個庫中獲得;gEGLImpl[IMPL_SOFTWARE]中保存着軟件的OpenGL api地址,從libGLES_android.so中獲取。
3.2 EGL和GLES api
enum {
EGL = 0x01,
GLESv1_CM = 0x02,
GLESv2 = 0x04
};
load_driver()@frameworks\base\opengl\libs\EGL\Loader.cpp
3.3.1 系統默認pixel format
if(info.bits_per_pixel == 32) {
/*
* Explicitly request RGBA_8888
*/
/* Note: the GL driver does not have a r=8 g=8 b=8 a=0 config, so if we do
* not use the MDP for composition (i.e. hw composition == 0), ask for
* RGBA instead of RGBX. */
if (property_get("debug.sf.hw", property, NULL) > 0 && atoi(property) == 0)
module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888;
else if(property_get("debug.composition.type", property, NULL) > 0 && (strncmp(property, "mdp", 3) == 0))
module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888;
else
module->fbFormat = HAL_PIXEL_FORMAT_RGBA_8888;
} else {
/*
* Explicitly request 5/6/5
*/
module->fbFormat = HAL_PIXEL_FORMAT_RGB_565;
}
目前的移動設備都是真彩色,所以這裏我們認爲我們的屏幕設備支持的是HAL_PIXEL_FORMAT_RGBA_8888。
3.3.2 config初始化
所有的OpenGL庫提供的config,同樣需要將軟硬兩種模式的各自的OpenGL config提取出來供系統使用,如同OpenGL api地址一樣。OpenGL config提取出來後保存在另外一個全局變量static egl_display_t gDisplay[NUM_DISPLAYS];
// EGLDisplay are global, not attached to a given thread
const unsigned int NUM_DISPLAYS = 1;
中,不同於gEGLImpl分開保存軟硬件api,所有的config,不論軟硬件的,均保存在gDisplay[0],因爲所有的config是以屏幕區分的,同一塊屏幕應該保存同一份config信息。
// sort our configurations so we can do binary-searches
qsort( dp->configs,
dp->numTotalConfigs,
sizeof(egl_config_t), cmp_configs);
最終,上述代碼會將gDisplay[0].config中的配置按照先硬件的,後軟件的規則做一個總體的排序。
3.3.3 config選擇
3.3.3.1 滿足屬性要求
// initialize EGL
EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE, 0,
EGL_NONE
};
3.3.3.2 滿足RGBA要求
static GGLFormat const gPixelFormatInfos[] =
{ // Alpha Red Green Blue
{ 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
{ 4, 32, {{32,24, 8, 0, 16, 8, 24,16 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_8888
android會根據pixelflinger的pixel format信息,去和openGL的config比較,得到想要的config。
EGLConfig* const configs = (EGLConfig*)malloc(sizeof(EGLConfig)*numConfigs);
if (eglChooseConfig(dpy, attrs, configs, numConfigs, &n) == EGL_FALSE) {
free(configs);
return BAD_VALUE;
}
const int fbSzA = fbFormatInfo.getSize(PixelFormatInfo::INDEX_ALPHA);
const int fbSzR = fbFormatInfo.getSize(PixelFormatInfo::INDEX_RED);
const int fbSzG = fbFormatInfo.getSize(PixelFormatInfo::INDEX_GREEN);
const int fbSzB = fbFormatInfo.getSize(PixelFormatInfo::INDEX_BLUE);
int i;
EGLConfig config = NULL;
for (i=0 ; i<n ; i++) {
EGLint r,g,b,a;
EGLConfig curr = configs[i];
eglGetConfigAttrib(dpy, curr, EGL_RED_SIZE, &r);
eglGetConfigAttrib(dpy, curr, EGL_GREEN_SIZE, &g);
eglGetConfigAttrib(dpy, curr, EGL_BLUE_SIZE, &b);
eglGetConfigAttrib(dpy, curr, EGL_ALPHA_SIZE, &a);
if (fbSzA <= a && fbSzR <= r && fbSzG <= g && fbSzB <= b) {
config = curr;
break;
}
}
4. 創建main surface
5. 創建 OpenGL ES 上下文
An OpenGL context represents many things. A context stores all of the state associated with this instance of OpenGL. It represents the (potentially visible) default framebufferthat rendering commands will draw to when not drawing to a framebuffer object. Think of a context as an object that holds all of OpenGL; when a context is destroyed, OpenGL is destroyed.
http://www.opengl.org/wiki/OpenGL_context
具體的創建過程專業術語太多,也沒有仔細研究不再介紹。
6. 綁定context和surface
6.1 多線程支持
ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific();
if (gl) {
egl_context_t* c = egl_context_t::context(gl);
if (c->flags & egl_context_t::IS_CURRENT) {
if (current != gl) {
// it is an error to set a context current, if it's already
// current to another thread
return -1;
}
} else {
if (current) {
// mark the current context as not current, and flush
glFlush();
egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
}
}
if (!(c->flags & egl_context_t::IS_CURRENT)) {
// The context is not current, make it current!
setGlThreadSpecific(gl);
c->flags |= egl_context_t::IS_CURRENT;
}
// cur_c has to be valid here (but could be terminated)
if (ctx != EGL_NO_CONTEXT) {
setGlThreadSpecific(c->cnx->hooks[c->version]);
setContext(ctx);
_c.acquire();
} else {
setGlThreadSpecific(&gHooksNoContext);
setContext(EGL_NO_CONTEXT);
}
儘管openGL 實現了多線程的支持,目前我從代碼中別沒有找到多線程的使用。
6.2 設置surface和context之間的關係
// Unbind the context from this thread
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
這麼做的目的應該是支持多display系統中的特殊處理,目的是當系統有多個display系統的話,那麼surfaceflinger就會去定義多個DisplayHardware對象,那麼爲了保證下一個DisplayHardware對象的創建不受影響,在當前的DisplayHardware創建完成後,將context從當前的進程中unbind掉。
// initialize primary screen
// (other display should be initialized in the same manner, but
// asynchronously, as they could come and go. None of this is supported
// yet).
const GraphicPlane& plane(graphicPlane(dpy));
const DisplayHardware& hw = plane.displayHardware();
const uint32_t w = hw.getWidth();
const uint32_t h = hw.getHeight();
const uint32_t f = hw.getFormat();
hw.makeCurrent();
下圖爲這個圖形系統的類圖結構。
前一篇文章介紹了android的顯示系統,這篇文章中,我們把視角往上層移動一下,研究一下framework是如何與surfaceflinger進行業務交互的。如何創建surface,如何顯示窗口等等,所有的這一切都是通過系統服務WindowManagerService與surfaceflinger來進行的。
android中的Surface機制這一塊代碼寫的比較難理解,光叫Surface的類就有3個,因此本篇文章從兩部分來分析,首先,想要理解Surface機制,還是需要首先理清各個類之間的關係。其次,在理解了整個Surface機制的類關係之後,到時我們再結合前一篇文章中對顯示系統的介紹,研究一下一個Surface是如何和顯示系統建立起聯繫來的,這個聯繫主要是指Surface的顯示buffer的存儲管理。在下篇文章中,再分析SurfaceFlinger是如何將已經存儲了窗口圖形數據的Surface
Buffer顯示到顯示系統中。。
1. Surface機制的靜態關係
將這一部分叫做Surface機制,是有別於SurfaceFlinger而言的,android的圖形系統中,作爲C/S模型兩端的WMS和SurfaceFlinger是圖形系統業務的核心,但是不把WMS和SurfaceFlinger中間的這層聯繫搞清楚的話,是很難理解整個圖形系統的,在本文中我將兩者之間的這個聯繫關係稱之爲Surface機制,它的主要任務就是創建一個Surface,ViewRoot在這個Surface上描繪當前的窗口,SurfaceFlinger將這個Surface flinger(扔)給顯示系統將其呈現在硬件設備上。其實這裏這個Surface在不同的模塊中是以不同的形態存在的,唯一不變的就是其對應的顯示Buffer。
1.1 ViewRoot和WMS共享Surface
我們知道每個Activity都會有一個ViewRoot作爲Activity Window與WMS交互的接口,ViewRoot會繪製整個Activity的窗口View到Surface上,因此我們在ViewRoot中就有了創建Surface的需求。看一下代碼中的Surface的定義:
relayoutWindow()@ViewRoot.java
private final Surface mSurface = new Surface();
Surface()@Surface.java
public Surface() {
if (DEBUG_RELEASE) {
mCreationStack = new Exception();
}
mCanvas = new CompatibleCanvas();
}
由上面可以看出在ViewRoot中定義的Surface只是一個空殼,那麼真正的Surface是在哪裏被初始化的呢?大管家WMS中!當ViewRoot請求WMS relayout時,會將ViewSurface中的Surface交給WMS初始化。在WMS中,對應每個WindowState對象,在relayout窗口時,同樣會創建一個Surface,wms中的這個Surface會真正的初始化,然後再將這個WMS Surface複製給ViewRoot中的Surface。這麼實現的目的就是保證ViewRoot和WMS共享同一個Surface。ViewRoot對Surface進行繪製,WMS對這個Surface進行初始化及管理。很和諧!
relayoutWindow()@ViewRoot.java
int relayoutResult = sWindowSession.relayout(
mWindow, params,
(int) (mView.mMeasuredWidth * appScale + 0.5f),
(int) (mView.mMeasuredHeight * appScale + 0.5f),
viewVisibility, insetsPending, mWinFrame,
mPendingContentInsets, mPendingVisibleInsets,
mPendingConfiguration, mSurface);
relayoutWindow()@WindowManagerService.java
Surface surface = win.createSurfaceLocked();
if (surface != null) {
outSurface.copyFrom(surface);
win.mReportDestroySurface = false;
win.mSurfacePendingDestroy = false;
if (SHOW_TRANSACTIONS) Slog.i(TAG,
" OUT SURFACE " + outSurface + ": copied");
} else {
1.2 SurfaceSession
SurfaceSession可以認爲是創建Surface過程中,WMS和SurfaceFlinger之間的會話層,通過這個SurfaceSession實現了Surface的創建。
SurfaceSession是JAVA層的概念,@SurfaceSession.java。它對應的native實體是一個SurfaceComposerClient對象。
SurfaceComposerClient通過ComposerService類來獲得SurfaceFlinger的IBinder接口,但是光獲得SurfaceFlinger的IBinder接口是不夠的,要想請求SurfaceFlinger創建一個Surface,還需要向SurfaceFlinger獲得一個IBinder接口ISurfaceComposerClient,通過這個ISurfaceComposerClient來請求SurfaceFlinger創建一個Surface,爲什麼這麼繞呢,爲什麼不直接讓SurfaceFlinger創建Surface呢?
站在SurfaceFlinger的角度來考慮,對於SurfaceFlinger來說,可能有多個Client來請求SurfaceFlinger的業務,每個Client可能會請求SurfaceFlinger創建多個Surface,那麼SurfaceFlinger本地需要提供一套機制來保存每個client請求創建的Surface,SurfaceFlinger通過爲每個client創建一個Client對象實現這個機制,並將這個Client的IBinder接口ISurfaceComposerClient返給SurfaceComposerClient對象。SurfaceComposerClient對象在通過ISurfaceComposerClient去請求創建Surface。
@SurfaceFlinger.h
class Client : public BnSurfaceComposerClient
@SurfaceComposerClient.cpp
void SurfaceComposerClient::onFirstRef()
{
sp<ISurfaceComposer> sm(getComposerService());
if (sm != 0) {
sp<ISurfaceComposerClient> conn = sm->createConnection();
if (conn != 0) {
mClient = conn;
Composer::addClient(this);
mPrebuiltLayerState = new layer_state_t;
mStatus = NO_ERROR;
}
}
}
下圖描述了整個SurfaceSession的內部結構與工作流程。
其中藍色箭頭是SurfaceComposerClient通過ComposerService獲得SurfaceFlinger的IBinder接口ISurfaceComposer過程;
紅色箭頭表示SurfaceComposerClient通過IPC請求SurfaceFlinger創建Client的過程,並獲得Client的IBinder接口ISurfaceComposerClient;
綠色箭頭表示SurfaceComposerClient通過IPC請求Client創建Surface。
1.3 Surface的形態
上一節我們分析了SurfaceSession的靜態結構,得知Surface的創建過程是通過SurfaceSession這個中間會話層去請求SurfaceFlinger去創建的,並且這篇文章中,我們說了半天Surface了,那麼究竟我們要創建的Surface究竟是什麼樣的一個東西呢,它的具體形態是什麼呢?這一小節我們就來分析以下Surface的形態。
1.3.1 client端Surface的形態
首先,我們看一下Surface在WMS中定義的代碼
createSurfaceLocked()@WindowManagerService.java
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
mAttrs.getTitle().toString(),
0, w, h, mAttrs.format, flags);
我們可以看到,它將SurfaceSession對象當作參數傳遞給了Surface的構造函數。往下看Surface的構造函數。
@Surface.java
public Surface(SurfaceSession s,
int pid, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
if (DEBUG_RELEASE) {
mCreationStack = new Exception();
}
mCanvas = new CompatibleCanvas();
init(s,pid,null,display,w,h,format,flags);
}
這個構造函數,不同於我們在ViewRoot中看到的Surface的構造函數,這個構造函數並不是一個空殼,它做了本地實體的初始化工作,因此這個Surface纔是一個真正的Suface。
Native 函數init回調到SurfaceComposerClient的createSurface()函數,往下的過程在上一節的圖中描述的很清楚,流程就不介紹了,同時我們先不管SurfaceFlinger爲SurfaceComposerClient創建的Surface到底是一個什麼東西,我們先看看SurfaceComposerClient爲WMS創建的是一個什麼東西?
@SurfaceComposerClient.cpp
sp<SurfaceControl> SurfaceComposerClient::createSurface(
int pid,
const String8& name,
DisplayID display,
uint32_t w,
uint32_t h,
PixelFormat format,
uint32_t flags)
{
sp<SurfaceControl> result;
if (mStatus == NO_ERROR) {
ISurfaceComposerClient::surface_data_t data;
sp<ISurface> surface = mClient->createSurface(&data, pid, name,
display, w, h, format, flags);
if (surface != 0) {
result = new SurfaceControl(this, surface, data, w, h, format, flags);
}
}
return result;
}
從上面的代碼我們可以看出,SurfaceComposerClient爲WMS返回的是一個SurfaceControl對象,這個SurfaceControl對象包含了surfaceFlinger爲SurfaceComposerClient創建的surface,這個surfaceFlinge創建的Surface在Client端的形態爲ISurface。這個過程下面分析SurfaceFlinger端的Surface形態時會看到。
SurfaceControl類中還有一個非常重要的成員,它的類型也叫做Surface,定義在frameworks/base/libs/surfaceflinger/Surface.h。這個Surface提供了顯示Buffer的管理。在文章的後面再介紹。
@frameworks/base/libs/surfaceflinger_client/Surface.cpp
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
mSurfaceData = new Surface(const_cast<SurfaceControl*>(this));
}
return mSurfaceData;
}
1.3.2 SurfaceFlinger端Surface形態
sp<Layer> normalLayer;
switch (flags & eFXSurfaceMask) {
case eFXSurfaceNormal:
if (UNLIKELY(flags & ePushBuffers)) {
layer = createPushBuffersSurface(client, d, w, h, flags);
} else {
normalLayer = createNormalSurface(client, d, w, h, flags, format);
layer = normalLayer;
}
break;
case eFXSurfaceBlur:
layer = createBlurSurface(client, d, w, h, flags);
break;
case eFXSurfaceDim:
layer = createDimSurface(client, d, w, h, flags);
break;
}
if (layer != 0) {
layer->initStates(w, h, flags);
layer->setName(name);
ssize_t token = addClientLayer(client, layer);
surfaceHandle = layer->getSurface();
if (surfaceHandle != 0) {
params->token = token;
params->identity = surfaceHandle->getIdentity();
params->width = w;
params->height = h;
params->format = format;
if (normalLayer != 0) {
Mutex::Autolock _l(mStateLock);
mLayerMap.add(surfaceHandle->asBinder(), normalLayer);
}
}
當client請求SurfaceFlinger創建Surface時,SurfaceFlinger首先根據WMS提供的窗口的屬性來一個命名爲Layer概念的對象,然後再根據Layer創建它的子類對象LayerBaseClient::Surface。此時第三個名爲Surface類出現了,下一節我們來介紹一下這個Layer的概念。
1.4 Layer
1.4.1 Layer的分類
目前,android中有4中Layer類型,如上圖所示。
1. Layer, 普通的Layer,它爲每個Client端請求的Surface創建顯示Buffer。
2. LayerBuffer,這種Layer它並不會創建顯示Buffer,它只是使用已有的Buffer作爲顯示Buffer,如Camera的preview;
3. LayerBlur,這種Layer也不會創建顯示Buffer,它只是將通過這個Layer將原來FrameBuffer上的數據進行模糊處理;
4. LayerDim,這種Layer也不會創建顯示Buffer,它只是將通過這個Layer將原來FrameBuffer上的數據進行暗淡處理;
從這中Layer看出,我們分析的重點就是第一種Layer,下面我們着重分析一下普通的Layer。Layer的具體業務我們在下一篇文章中分析
1.4.2 Layer的管理
上文我們在分析SurfaceSession的時候,也分析過,一個Client可能會創建多個Surface,也就是要創建多個Layer,那麼SurfaceFlinger端如何管理這個寫個Layer呢?SurfaceFlinger維護了2個Vector來管理Layer。
第一種方式,我們知道SurfaceFlinger會爲每個SurfaceSession創建一個Client對象,這第一種方式就是將所有爲某一個SurfacSession創建的Layer保存在它對應的Client對象中。
SurfaceFlinger::createSurface()@SurfaceFlinger.cpp
ssize_t token = addClientLayer(client, layer);
第二種方式,將所有的創建的普通的Layer保存起來,以便Client Surface在請求實現Buffer時能夠辨識Client Surface對應的Layer。
SurfaceFlinger::createSurface()@SurfaceFlinger.cpp
mLayerMap.add(surfaceHandle->asBinder(), normalLayer);
2. Surface 顯示Buffer的存儲管理
在前文介紹Client端的Surface形態的內容時,我們提到SurfaceControl中還會維護一個名爲Surface對象,它定義在 frameworks/base/libs/surfaceflinger/Surface.h中,它負責向LayerBaseClient::Surface請求顯示Buffer,同時將顯示Buffer交給JAVA Surface的Canvas去繪製窗口,我們稱這個Surface爲Client Surface。
2.1 窗口繪製
draw()@ViewRoot.java
Canvas canvas;
try {
int left = dirty.left;
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
canvas = surface.lockCanvas(dirty);
if (left != dirty.left || top != dirty.top || right != dirty.right ||
bottom != dirty.bottom) {
mAttachInfo.mIgnoreDirtyState = true;
}
// TODO: Do this in native
canvas.setDensity(mDensity);
上面的代碼顯示,JAVA Surface 會lock canvas。而Client Surface的創建就在這個過程中,即下面代碼中的第一行getSurface().我們先不管Client Surface的創建,先看看Canvas是如何與Client Surface的顯示Buffer關聯的。
static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
{
const sp<Surface>& surface(getSurface(env, clazz));
if (!Surface::isValid(surface))
return 0;
SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas);
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);
SkRegion clipReg;
if (dirtyRegion.isRect()) { // very common case
const Rect b(dirtyRegion.getBounds());
clipReg.setRect(b.left, b.top, b.right, b.bottom);
} else {
size_t count;
Rect const* r = dirtyRegion.getArray(&count);
while (count) {
clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op);
r++, count--;
}
}
nativeCanvas->clipRegion(clipReg);
int saveCount = nativeCanvas->save();
env->SetIntField(clazz, so.saveCount, saveCount);
if (dirtyRect) {
const Rect& bounds(dirtyRegion.getBounds());
env->SetIntField(dirtyRect, ro.l, bounds.left);
env->SetIntField(dirtyRect, ro.t, bounds.top);
env->SetIntField(dirtyRect, ro.r, bounds.right);
env->SetIntField(dirtyRect, ro.b, bounds.bottom);
}
return canvas;
}
上面的代碼,我們可以看出,Canvas的Bitmap設備的設置了Client Surface的顯示Buffer爲其Bitmap pixel存儲空間。
bitmap.setPixels(info.bits);
這樣Canvas的繪製空間就有了。下一步就該繪製窗口了。
draw()@ViewRoot.java
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired
? DisplayMetrics.DENSITY_DEVICE : 0);
mView.draw(canvas);
}
其中ViewRoot中的mView爲整個窗口的DecorView。
2.2 Client Surface的初始化
Client Surface的創建是從ViewRoot首次Lock canvas時進行的,這麼做的目的可能也是爲了節約空間,減少不必要的開支。
Client Surface的初始化和顯示Buffer的管理過程比較複雜,下圖給出了這一部分的一個靜態結構圖,有些東西從圖上表現不出來,下面我簡單的介紹一下。
2.2.1 SharedClient
SharedClient是這一部分實現的關鍵所在,它並不是一個每個Client Surface創建時都會被創建的,整個系統中只有一個SharedClient對象,並且它是在共享內存上創建的,下面代碼中可以看出,UserClient在初始化時,提供了一個MemoryHeapBase來供SharedClient創建,MemoryHeapBase是創建的共享內存。
@SurfaceFlinger.cpp
UserClient::UserClient(const sp<SurfaceFlinger>& flinger)
: ctrlblk(0), mBitmap(0), mFlinger(flinger)
{
const int pgsize = getpagesize();
const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));
mCblkHeap = new MemoryHeapBase(cblksize, 0,
"SurfaceFlinger Client control-block");
ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());
if (ctrlblk) { // construct the shared structure in-place.
new(ctrlblk) SharedClient;
}
}
SharedClient對象的主要目的其實很簡單,就是爲系統提供了SharedBufferStack::NUM_LAYERS_MAX(GB上爲31)個SharedBufferStack。也就是目前系統同時支持31個Client Surface的創建。關於SharedBufferStack下面再做介紹。
爲什麼需要將SharedClient設計爲共享內存呢?每個Client Surface需要的SharedBufferStack寄存在SharedClient中,而對於每個SharedBufferStack,一方面,Client Surface需要對它進行一些區域尺寸等的設置;另一方面,在render時,Layer需要獲得當前Client Surfce對應的SharedBufferStack中獲得區域尺寸等設置信息。
class [email protected]
SharedBufferStack surfaces[ SharedBufferStack::NUM_LAYERS_MAX ];
2.2.2 SharedBufferStack
status_t setDirtyRegion(int buffer, const Region& reg);
status_t setCrop(int buffer, const Rect& reg);
status_t setTransform(int buffer, uint8_t transform);
volatile int32_t head; // server's current front buffer
volatile int32_t available; // number of dequeue-able buffers
這幾個變量的值來確定Client Surface中GraphicBuffer的索引,其中SharedBufferClient::tail記錄的是BackBuffer的索引;SharedBufferStack::head記錄的是FrontBuffer的索引。
這裏將Client Surface的GraphicBuffer的創建過程以時序圖的形式展現出來。
這裏需要注意的是,Client Surface的2個GraphicBuffer只有在lock()時纔會去創建,而不是在Client Surface被創建的時候創建的。
上一篇文章介紹了整個Surface機制(我是這麼稱呼的,主要是Surface的建立,Surface的顯示存儲的管理),同時我們也介紹過了整個顯示系統,那麼這篇文章就介紹一下SurfaceFlinger 這個核心服務層的機制。
從代碼中我們可以看出SurfaceFlinger 是一個thread,運行在system_server進程中,並且其threadLoop()方法的返回值爲true,因此它是一個循環的loop。這樣保證了SurfaceFlinger業務的循環週期性。
首先,先來個綜述,下圖是我總結的一個SurfaceFlinger結構的概括圖:
1. SurfaceFlinger的同步
SurfaceFlinger 並不是時時刻刻都在執行業務中,當WMS請求SurfaceFlinger創建Surface,或者WMS對Surface進行屬性設置時,我們希望此時的SurfaceFlinger並不進行顯示操作,以保證對Surface的線程保護,因此SurfaceFlinger 的loop中實現了同步機制。
- waitForEvent();
waitForEvent();
主要的同步情況有如下幾種,當然也有其他一些要求SurfaceFlinger同步的情況,不夠對於研究SurfaceFlinger就不太重要了
1. 創建Surface同步
假如當前只有一個Client,比如WMS請求SufaceFlinger創建一個Surface,那麼此時應該保持SurfaceFlinger loop處在block狀態,因爲這個過程涉及到對一些成員變量的處理,爲了保證同步而需要hold住整個loop。
2. 設置Surface屬性或SurfaceFlinger屬性同步
創建完Surface之後,WMS會請求SurfaceFlinger對其Layer進行屬性設置或者對SurfaceFlinger的屬性進行設置,如上面概括圖中SurfaceComposerClient中的函數接口。
3. Surface繪製同步
當ViewRoot對Surface進行繪製時,同樣需要將SurfaceFlinger hold住,當整個窗口繪製完成之後,再向SurfaceFlinger發送signal信號。如下面時序圖所示。
4. freeze/unfreeze同步
當每個Activity啓動的時候,AMS都會請求WMS freeze整個屏幕,當Activity啓動之後,再unfreeze整個屏幕,我猜測這麼做的目的是爲了保證在Activity以及Activity的窗口在創建過程中,對Activity窗口的Surface進行的線程保護,以免出現屏幕的閃爍等用戶體驗較差的現象。
2. Layer存儲
在SurfaceFlinger中,Layer是怎麼樣存儲的呢?所有的Layer,不論是那個Client創建的Layer,均保存在一個名爲layersSortedByZ的變量中,也就是說WMS請求創建的Surface的Layer和其他Client請求創建的Layer都保存在layersSortedByZ中,但是layersSortedByZ保存過程中則遵守一定的規則。下面代碼中的do_compare揭示了這個規則。
@SurfaceFlinger.h
class LayerVector : public SortedVector< sp<LayerBase> > {
public:
LayerVector() { }
LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { }
virtual int do_compare(const void* lhs, const void* rhs) const {
const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs));
const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs));
// sort layers by Z order
uint32_t lz = l->currentState().z;
uint32_t rz = r->currentState().z;
// then by sequence, so we get a stable ordering
return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence);
}
};
每次向layersSortedByZ中添加新的Layer,都會做一次排序,按照規則將其放在合適的位置。
1. 首先,按照Layer的Z-order值來排序,Z-order值小的,放在layersSortedByZ低索引值位置;
2. 其次,如果兩個Layer Z-order值相同,sequence值小的,放在layersSortedByZ低索引值位置;
Z-order值如何確定?
WMS根據不同的Window Type來確定Z-order值,Z-order = LAYER*TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET。
根據下面代碼中的不同的Window Type的LAYER值,可以確定Z-order值,例如TYPE_APPLICATION窗口,其
Z-order = 2*10000+1000 = 21000。
@PhoneWindowManager.java
// wallpaper is at the bottom, though the window manager may move it.
static final int WALLPAPER_LAYER = 2;
static final int APPLICATION_LAYER = 2;
static final int PHONE_LAYER = 3;
static final int SEARCH_BAR_LAYER = 4;
static final int STATUS_BAR_PANEL_LAYER = 5;
static final int SYSTEM_DIALOG_LAYER = 6;
// toasts and the plugged-in battery thing
static final int TOAST_LAYER = 7;
static final int STATUS_BAR_LAYER = 8;
// SIM errors and unlock. Not sure if this really should be in a high layer.
static final int PRIORITY_PHONE_LAYER = 9;
// like the ANR / app crashed dialogs
static final int SYSTEM_ALERT_LAYER = 10;
// system-level error dialogs
static final int SYSTEM_ERROR_LAYER = 11;
// on-screen keyboards and other such input method user interfaces go here.
static final int INPUT_METHOD_LAYER = 12;
// on-screen keyboards and other such input method user interfaces go here.
static final int INPUT_METHOD_DIALOG_LAYER = 13;
// the keyguard; nothing on top of these can take focus, since they are
// responsible for power management when displayed.
static final int KEYGUARD_LAYER = 14;
static final int KEYGUARD_DIALOG_LAYER = 15;
// things in here CAN NOT take focus, but are shown on top of everything else.
static final int SYSTEM_OVERLAY_LAYER = 16;
static final int SECURE_SYSTEM_OVERLAY_LAYER = 17;
sequence值如何確定?
sequence值是根據Layer的創建的順序來維護這個序列值,下面代碼中的LayerBase的構造函數中的sequence值,每創建一個Layer,sSequence加一賦值給sequence。
@LayerBase.cpp
int32_t LayerBase::sSequence = 1;
LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
: dpy(display), contentDirty(false),
sequence(uint32_t(android_atomic_inc(&sSequence))),
mFlinger(flinger),
mNeedsFiltering(false),
mOrientation(0),
mLeft(0), mTop(0),
mTransactionFlags(0),
mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
mInvalidate(0)
{
const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
mFlags = hw.getFlags();
mBufferCrop.makeInvalid();
mBufferTransform = 0;
}
3. 屬性更新
這一節的所描述的實現都在函數handleTransactionLocked()中。
從上面概括圖中可以看出,WMS可以對SurfaceFlinger進行屬性設置,也可以對當前的Surface對應的Layer進行屬性設置,因此handleTransactionLocked()函數就是對SurfaceFlinger屬性和設置了新屬性的Layer的屬性更新。
enum {
eTransactionNeeded = 0x01,
eTraversalNeeded = 0x02
};
SurfaceFlinger根據這個枚舉值來確定handleTransactionLocked()需要更新SurfaceFlinger屬性還是layer屬性。
如果SurfaceFlinger屬性被設置了新內容,則SurfaceFlinger會記錄標誌eTransactionNeeded;如果layer屬性被設置了新內容,那麼
SurfaceFlinger會記錄標誌eTraversalNeeded。handleTransactionLocked()通過記錄的標誌來執行各自的屬性得更新。‘
這裏提到的屬性的更新,主要是看SurfaceFlinger或者laye新設置的屬性與舊的屬性相比,哪些屬性做了修改,然後
記錄下來,在接下來的SurfaceFlinger loop中使用新的屬性來顯示圖形。
類SurfaceFlinger 和Layer中各自定義了兩個屬性的變量,其中mCurrentState爲新設置屬性,mDrawingState爲顯示圖形時用到的屬性,一般爲舊屬性。不過類SurfaceFlinger 和Layer分別定義了不同的State類。
State mCurrentState;
State mDrawingState;
4. 圖形緩存
這一部分的的實現在函數handlePageFlip()中。
有這麼一種可能,當前顯示到顯示設備上的layer不止一個,而且layer是按照Z-Order的順序來疊加到OpenGL的surface上的,那麼這就需要layer的Z-Order值和座標來確定每個layer能夠被顯示的區域。
4.1 page flip
前面一篇文章中介紹過,每個surface均有2個buffer供使用,一個作爲FronteBuffer供SurfaceFlinger去顯示,另外一個作爲BackBuffer供ViewRoot去繪製窗口。因此在顯示各個layer之前,我們需要做一個page flip過程,將當前的已經繪製了應用窗口的BackBuffer選擇爲FrontBuffer,用於顯示;將之前的已經顯示完成的FrontBuffer在重置爲BackBuffer供ViewRoot去繪製。
而實現這個page flip的過程很簡單
lockPageFlip()@Layer.cpp
ssize_t buf = lcblk->retireAndLock();
SharedBufferServer::RetireUpdate::operator()@SharedBufferStack.cpp
head = (head + 1) % numBuffers;
4.2 紋理初始化
爲每個Buffer的紋理進行初始化,爲當前的紋理創建一個EGLImageKHR,將當前的Buffer最爲該EGLImageKHR的源。這樣OpenGL就可以進行紋理映射。
lockPageFlip()@Layer.cpp
/* a buffer was posted, so we need to call reloadTexture(), which
* will update our internal data structures (eg: EGLImageKHR or
* texture names). we need to do this even if mPostedDirtyRegion is
* empty -- it's orthogonal to the fact that a new buffer was posted,
* for instance, a degenerate case could be that the user did an empty
* update but repainted the buffer with appropriate content (after a
* resize for instance).
*/
reloadTexture( mPostedDirtyRegion );
4.3 計算顯示區域
通過layer的疊加,我們可以計算出總的顯示區域以及每個layer需要顯示的區域,它的實現在computeVisibleRegions()函數中。這個函數主要計算了layer疊加後的總的顯示區域,以及每個layer需要顯示的區域。整個的計算過程比較簡單,只是需要注意不透明區域的處理,computeVisibleRegions()需要計算出一個不透明區域,通過這個不透明區域驗證WMS提供給layer的區域是否正確。即下面代碼中的mWormholeRegion計算,mWormholeRegion爲屏幕區域減去不透明區域,正常情況mWormholeRegion應該爲空,即不透明區域範圍應該爲屏幕區域,如果不透明區域小雨屏幕區域,那麼說明當前的應用程序出現了設置的錯誤。今天有個網友就出現了這個問題。
handlePageFlip()
const Region screenRegion(hw.bounds());
if (visibleRegions) {
Region opaqueRegion;
computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
/*
* rebuild the visible layer list
*/
mVisibleLayersSortedByZ.clear();
const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
size_t count = currentLayers.size();
mVisibleLayersSortedByZ.setCapacity(count);
for (size_t i=0 ; i<count ; i++) {
if (!currentLayers[i]->visibleRegionScreen.isEmpty())
mVisibleLayersSortedByZ.add(currentLayers[i]);
}
mWormholeRegion = screenRegion.subtract(opaqueRegion);
mVisibleRegionsDirty = false;
}
在computeVisibleRegions()疊加計算總的顯示範圍,layer的計算順序從上到下的過程計算的,也就是先計算Z-Order值較大的,顯示在最上層的layer開始往下計算。這麼做的好處就是能夠很好的計算出不透明區域的範圍。
在SurfaceFlinger的區域相互之間的操作處理如下:
4.4 圖形緩存
前面選擇了FrontBuffer、初始化了紋理、計算了layer的顯示區域,那麼下一步就該將Buffer內容進行圖形處理並保存到OpenGL緩存中。
調用每個layer的draw函數來進行這個操作。如下面代碼所示。具體的圖形處理過程很複雜,完全交給OpenGL去處理,這裏我們就不去關心了。我們只需要知道最終經過圖形處理的內容會被緩存到OpenGL的緩存區中。
void SurfaceFlinger::composeSurfaces(const Region& dirty)
{
if (UNLIKELY(!mWormholeRegion.isEmpty())) {
// should never happen unless the window manager has a bug
// draw something...
drawWormhole();
}
const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<LayerBase>& layer(layers[i]);
const Region clip(dirty.intersect(layer->visibleRegionScreen));
if (!clip.isEmpty()) {
layer->draw(clip);
}
}
}
從前面的顯示系統中,介紹過,Surface的緩存Buffer就是FramebufferNativeWindow中定義的2個Buffer,如果/dev/fb0讀取設備信息,如果設備支持page flip,那麼Surface的緩存Buffer即從/dev/fb0設備中申請;如果不支持,我們則需要從/dev/pmem中申請,同時/dev/fb0還會提供一個Buffer以便圖形最終的顯示。
/dev/fb0不支持page flip模式
/dev/fb0支持page flip模式
5. 圖形顯示
當圖形內容被緩存到frameBuffer中後,最後的一步就是圖形顯示。代碼中很明確就是SurfaceFlinger loop中的postFramebuffer()函數了。
這個函數最終回調到OpenGL的eglSwapBuffers()函數,這個函數主要有2個步驟(由於硬件加速代碼不可見,我們仍然以軟件加速爲例)
1. 顯示當前緩存buffer中內容;
首先,將原來的屏幕上的內容與最新需要顯示的內容進行區域相減,將原來的內容copy到當前的緩存buffer中;
EGLBoolean egl_window_surface_v2_t::swapBuffers()@frameworks\base\opengl\libagl\egl.cpp
/*
* Handle eglSetSwapRectangleANDROID()
* We copyback from the front buffer
*/
if (!dirtyRegion.isEmpty()) {
dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
if (previousBuffer) {
const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
if (!copyBack.isEmpty()) {
void* prevBits;
if (lock(previousBuffer,
GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {
// copy from previousBuffer to buffer
copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);
unlock(previousBuffer);
}
}
}
oldDirtyRegion = dirtyRegion;
}
其次,如果當前的緩存buffer是申請自/dev/fb0,那麼直接去顯示這個緩存區中內容;如果緩存buffer是申請自/dev/pmem,那麼需要將緩存buffer中內容拷貝到/dev/fb0 buffer中去,其結構如上一節所示。
2. 對2個緩存buffer進行page flip(swap)操作。
通過 queueBuffer()操作將將當前Buffer交還給FramebufferNativeWindow,同時調用fb_post進行圖形顯示。然後通過dequeueBuffer()操作獲得另外一個FramebufferNativeWindow的緩存Buffer,實現page
flip(swap)操作。
至此,整個的SurfaceFlinger的機制就分析完了。