在前文中,我們分析瞭SurfaceFlinger服務的啟動過程。SurfaceFlinger服務在啟動的過程中,會對系統的硬件幀緩沖區進行初始化。由於系統的硬件幀緩沖區一般隻有一個,並且不是誰都可以隨便訪問的,因此,它就需要由一個服務來統一管理。在Android系統中,這個服務便是SurfaceFlinger。在本文中,我們就詳細分析SurfaceFlinger服務是如何管理系統的硬件幀緩沖區的。
從前面Android系統Surface機制的SurfaceFlinger服務簡要介紹和學習計劃一文可以知道,SurfaceFlinger服務通過一個GraphicPlane對象來描述系統的顯示屏,即系統的硬件幀緩沖區。GraphicPlane類內部聚合瞭一個DisplayHardware對象,通過這個DisplayHardware對象就可以訪問系統的硬件幀緩沖區。DisplayHardware類內部又包含瞭一個FramebufferNativeWindow對象,這個FramebufferNativeWindow對象才是真正用來描述系統的硬件幀緩沖區的。FramebufferNativeWindow類的作用類似於在前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一文中所介紹的Surface類,它是連接OpenGL庫和Android的UI系統的一個橋梁,OpenGL庫就是通過這個橋梁來將Android系統的UI渲染到硬件幀緩沖區中去的。GraphicPlane、DisplayHardware和FramebufferNativeWindow這三個類的關系如圖1所示。
圖1 GraphicPlane、DisplayHardware和FramebufferNativeWindow的類關系圖
接下來,我們就分別介紹GraphicPlane、DisplayHardware和FramebufferNativeWindow這三個類的實現,以便可以理解SurfaceFlinger服務是如何通過它們來管理系統的硬件幀緩沖區的。
從前面Android系統Surface制的SurfaceFlinger服務的啟動過程分析一文可以知道,SurfaceFlinger服務在啟動的過程中,會對系統的硬件幀緩沖區進行初始化,如下所示:
[cpp]
status_t SurfaceFlinger::readyToRun()
{
……
// we only support one display currently
int dpy = 0;
{
// initialize the main display
GraphicPlane& plane(graphicPlane(dpy));
DisplayHardware* const hw = new DisplayHardware(this, dpy);
plane.setDisplayHardware(hw);
}
……
// 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();
……
hw.makeCurrent();
……
}
這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
這個代碼段首先創建瞭一個DisplayHardware對象,用來初始化編號為0的GraphicPlane對象,接著再將這個DisplayHardware對象設置為系統當前活動的DisplayHardware對象,這就相當於是將編號為0的GraphicPlane對象所描述的顯示屏設置為系統當前活動的顯示屏。
接下來,我們就首先分析編號為0的GraphicPlane對象的初始化過程,接著再分析DisplayHardware對象的創建過程。
編號為0的GraphicPlane對象的初始化過程主要是調用GraphicPlane類的成員函數setDisplayHardware來實現的,如下所示:
void GraphicPlane::setDisplayHardware(DisplayHardware *hw)
{
mHw = hw;
// initialize the display orientation transform.
// it's a constant that should come from the display driver.
int displayOrientation = ISurfaceComposer::eOrientationDefault;
char property[PROPERTY_VALUE_MAX];
if (property_get("ro.sf.hwrotation", property, NULL) > 0) {
//displayOrientation
switch (atoi(property)) {
case 90:
displayOrientation = ISurfaceComposer::eOrientation90;
break;
case 270:
displayOrientation = ISurfaceComposer::eOrientation270;
break;
}
}
const float w = hw->getWidth();
const float h = hw->getHeight();
GraphicPlane::orientationToTransfrom(displayOrientation, w, h,
&mDisplayTransform);
if (displayOrientation & ISurfaceComposer::eOrientationSwapMask) {
mDisplayWidth = h;
mDisplayHeight = w;
} else {
mDisplayWidth = w;
mDisplayHeight = h;
}
setOrientation(ISurfaceComposer::eOrientationDefault);
}
這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
函數首先設置顯示屏的初始大小和旋轉方向。GraphicPlane類有三個成員變量mDisplayWidth、mDisplayHeight和mDisplayTransform,前兩者的類型為float,分別用描述顯示屏的初始寬度和高度,而後者的類型為Transform,用來描述顯示屏的初始旋轉方向。Transform類的作用是來描述變換矩陣,以便後面在渲染UI時可以用來動態地計算顯示屏的旋轉方向。
顯示屏的初始化寬度和高度是由參數hw所描述的一個DisplayHardware對象來描述的,而顯示屏的初始旋轉方向則是由名稱為“ro.sf.hwrotation”的系統屬性來決定的。如果沒有設置名稱為“ro.sf.hwrotation”的系統屬性,那麼顯示屏的旋轉方向就為默認方向,即ISurfaceComposer::eOrientationDefault。
獲得瞭顯示屏的初始化寬度w、高度h和旋轉方向displayOrientation之後,函數接著就調用GraphicPlane類的靜態成員函數orientationToTransfrom來將它們構造成一個變換矩陣,並且保存在GraphicPlane類的成員變量mDisplayTransform中。
函數接下來繼續判斷顯示屏的初始化旋轉方向是否將初始化寬度和高度值翻轉瞭。如果翻轉瞭,那麼就需要相互調換GraphicPlane類的成員變量mDisplayWidth和mDisplayHeight的值,以便可以正確地反映顯示屏的初始化寬度和高度。
註意,顯示屏的初始寬度、高度和旋轉方向一經初始化之後,就會保持不變,以後顯示屏的實際旋轉方向計算都是要在此基礎上進行計算的,即要在變換矩陣mDisplayTransform的基礎上進行計算。從這裡還可以看出,通過設置名稱為“ro.sf.hwrotation”的系統屬性的值,就可以設置系統顯示屏的初始化旋轉方向,以便匹配實際的硬件幀緩沖區的旋轉方向。
函數最後調用GraphicPlane類的成員函數setOrientation來設備顯示屏的實際度度、高度以及旋轉方向,如下所示:
[cpp]
status_t GraphicPlane::setOrientation(int orientation)
{
// If the rotation can be handled in hardware, this is where
// the magic should happen.
const DisplayHardware& hw(displayHardware());
const float w = mDisplayWidth;
const float h = mDisplayHeight;
mWidth = int(w);
mHeight = int(h);
Transform orientationTransform;
GraphicPlane::orientationToTransfrom(orientation, w, h,
&orientationTransform);
if (orientation & ISurfaceComposer::eOrientationSwapMask) {
mWidth = int(h);
mHeight = int(w);
}
mOrientation = orientation;
mGlobalTransform = mDisplayTransform * orientationTransform;
return NO_ERROR;
}
這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
參數orientation的值等於ISurfaceComposer::eOrientationDefault,即SurfaceFlinger服務在初始化系統顯示屏時,會將它的旋轉方向設置為默認值,以後再根據實際情況來做調整。
GraphicPlane類有三個成員變量mWidth、mHeight和mOrientation,它們的類型均為int,分別用來描述顯示屏的實際寬度、高度和旋轉方向。與成員變量mDisplayWidth、mDisplayHeight和mDisplayTransform所描述的顯示屏初始化寬度、高度和旋轉方向一經初始化後就保持不變不同,mWidth、mHeight和mOrientation這三個成員變量是會動態變化的。例如,當顯示屏由LANDSCAPE變為PORTRAIT模式時,mWidth、mHeight和mOrientation這三個成員變量就會相應地發生改變。
函數首先將顯示屏的實際寬度mWidth、高度mHeight和旋轉方向mOrientation設置為顯示屏的初始寬度mDisplayWidth、高度mDisplayHeight以及參數orientation所描述的旋轉方向,接著再調用GraphicPlane類的靜態成員函數orientationToTransfrom來將它們構造成一個變換矩陣orientationTransform。
函數接著判斷顯示屏的實際旋轉方向orientation是否將原來的實際寬度和高度值翻轉瞭。如果翻轉瞭,那麼就需要相互調換GraphicPlane類的成員變量mWidth和mHeight的值,以便可以正確地反映顯示屏的實際寬度和高度。
函數最後將用來描述顯示屏的初始化旋轉方向的變換矩陣mDisplayTransform和用來描述顯示屏的實際旋轉方向的變換矩陣orientationTransform相乘,就可以得到一個全局變換矩陣,並且保存在GraphicPlane類的成員變量mGlobalTransform中。這樣以後渲染UI時,對於一個任意的點向量,隻要將它乘以全局變換矩陣mDisplayTransform,那麼就可以得到它所描述的實際位置。
至此,編號為0的GraphicPlane對象的初始化過程就完成瞭,以後SurfaceFlinger服務就可以調用它的成員函數displayHardware來獲得它內部的一個DisplayHardware對象,如下所示:
[cpp]
const DisplayHardware& GraphicPlane::displayHardware() const {
return *mHw;
}
這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
接下來,回到前面SurfaceFlinger類的成員函數readyToRun中,我們通過DisplayHardware對象的創建過程來分析DisplayHardware類的實現。
在創建DisplayHardware對象的過程中,會調用DisplayHardware類的構造函數,如下所示:
[cpp]
DisplayHardware::DisplayHardware(
const sp<SurfaceFlinger>& flinger,
uint32_t dpy)
: DisplayHardwareBase(flinger, dpy),
mFlags(0)
{
init(dpy);
}
這個函數定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。
從這裡可以看出,DisplayHardware類是從DisplayHardwareBase類繼承下來的。接下來,我們首先繼續分析一個DisplayHardware對象的初始化過程,接著再分析這個DisplayHardware對象的父對象DisplayHardwareBase的初始化過程,以便可以瞭解DisplayHardwareBase類的實現。
一個DisplayHardware對象的初始化過程是通過調用DisplayHardware類的成員函數init來實現的。DisplayHardware類的成員函數init的實現比較長,我們分段來閱讀:
[cpp]
void DisplayHardware::init(uint32_t dpy)
{
mNativeWindow = new FramebufferNativeWindow();
framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
mDpiX = mNativeWindow->xdpi;
mDpiY = mNativeWindow->ydpi;
mRefreshRate = fbDev->fps;
mOverlayEngine = NULL;
hw_module_t const* module;
if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
overlay_control_open(module, &mOverlayEngine);
}
這段代碼首先是創建瞭一個FramebufferNativeWindow對象,並且保存在DisplayHardware類的成員變量mNativeWindow中,用來管理硬件幀緩沖區。有瞭這個FramebufferNativeWindow對象之後,就可以通過它裡面的一個fb設備來獲得硬件幀緩沖區的點密度以及刷新頻率等信息。後面我們再詳細分析FramebufferNativeWindow類的實現。
這段代碼接著再加載HAL層中的overlay模塊,目的是要打開系統的overlay設備。在Android系統中,我們可以將overlay看作是一種特殊的Surface,一般用來顯示視頻。在這一系列文章中,我們暫時不關心overlay設備的實現。
我們接著往下閱讀代碼:
[cpp]
EGLint w, h, dummy;
EGLint numConfigs=0;
EGLSurface surface;
EGLContext context;
// initialize EGL
EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE, 0,
EGL_NONE
};
// debug: disable h/w rendering
char property[PROPERTY_VALUE_MAX];
if (property_get("debug.sf.hw", property, NULL) > 0) {
if (atoi(property) == 0) {
LOGW("H/W composition disabled");
attribs[2] = EGL_CONFIG_CAVEAT;
attribs[3] = EGL_SLOW_CONFIG;
}
}
這段代碼主要用來設置一個EGL屬性數組attribs,以便接下來可以根據這個屬性數組的值來正確的初始化EGL庫。
我們接著往下閱讀代碼:
[cpp]
// TODO: all the extensions below should be queried through
// eglGetProcAddress().
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, NULL, NULL);
eglGetConfigs(display, NULL, 0, &numConfigs);
EGLConfig config;
status_t err = EGLUtils::selectConfigForNativeWindow(
display, attribs, mNativeWindow.get(), &config);
LOGE_IF(err, "couldn't find an EGLConfig matching the screen format");
這段代碼首先調用eglGetDisplay和eglInitialize函數來獲得和初始化OpengGL庫的默認顯示屏,接著再調用EGLUtils::selectConfigForNativeWindow函數來獲得前面所創建的一個FramebufferNativeWindow對象所描述的系統主繪圖表面的配置信息,並且保存在EGLConfig對象config。有瞭這些配置信息之後,接下來就可以在硬件幀緩沖區上面創建系統的主繪圖表面。
我們接著往下閱讀代碼:
[cpp]
EGLint r,g,b,a;
eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
if (mNativeWindow->isUpdateOnDemand()) {
mFlags |= PARTIAL_UPDATES;
}
if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) {
if (dummy == EGL_SLOW_CONFIG)
mFlags |= SLOW_CONFIG;
}
這段代碼主要是用來獲得系統主繪圖表面的一些屬性,例如,四個顏色分量R、G、B和A的大小,以及是否支持部分更新、是否使用慢渲染方式等。
我們接著往下閱讀代碼:
[cpp]
/*
* Create our main surface
*/
surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
eglQuerySurface(display, surface, EGL_WIDTH, &mWidth);
eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);
if (mFlags & PARTIAL_UPDATES) {
// if we have partial updates, we definitely don't need to
// preserve the backbuffer, which may be costly.
eglSurfaceAttrib(display, surface,
EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
}
if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) {
if (dummy == EGL_BUFFER_PRESERVED) {
mFlags |= BUFFER_PRESERVED;
}
}
這段代碼首先調用函數eglCreateWindowSurface來創建系統的主繪圖表面。系統的主繪圖表面是直接在硬件幀緩沖區上創建的,用來渲染系統的UI,即負責合成和渲染所有應用程序的UI。
這段代碼接著還獲得系統的主繪圖表面的寬度和高度,分別保存在並且保存在DisplayHardware類的成員變量mWidth和mHeight中。
這段代碼最後還判斷硬件幀緩沖區是否支持部分更新。如果支持的話,就會在調用函數eglSwapBuffers來渲染系統的UI時,不保留硬件幀緩沖區的後端圖形緩沖區的內容,因為這樣做是有代價的。如果不支持的話,那麼就會將DisplayHardware類的成員變量mFlags的BUFFER_PRESERVED位設置為1,以表示在調用函數eglSwapBuffers來渲染系統的UI時,需要保留硬件幀緩沖區的後端圖形緩沖區的內容。
我們接著往下閱讀代碼:
[cpp]
/* Read density from build-specific ro.sf.lcd_density property
* except if it is overridden by qemu.sf.lcd_density.
*/
if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) {
if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
LOGW("ro.sf.lcd_density not defined, using 160 dpi by default.");
strcpy(property, "160");
}
} else {
/* for the emulator case, reset the dpi values too */
mDpiX = mDpiY = atoi(property);
}
mDensity = atoi(property) * (1.0f/160.0f);
這段代碼用來設備系統的主繪圖表面的點密度信息。系統的主繪圖表面的點密度信息可以通過名稱為“qemu.sf.lcd_density”或者“ro.sf.lcd_density”的系統屬性來配置。如果沒有配置,那麼默認值就為160dpi。
我們接著往下閱讀代碼:
[cpp]
/*
* Create our OpenGL ES context
*/
EGLint contextAttributes[] = {
#ifdef EGL_IMG_context_priority
#ifdef HAS_CONTEXT_PRIORITY
#warning "using EGL_IMG_context_priority"
EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG,
#endif
#endif
EGL_NONE, EGL_NONE
};
context = eglCreateContext(display, config, NULL, contextAttributes);
mDisplay = display;
mConfig = config;
mSurface = surface;
mContext = context;
mFormat = fbDev->format;
mPageFlipCount = 0;
這段代碼主要是調用函數eglCreateContext來創建系統的主繪圖表面的上下文。有瞭這個上下文之後,OpenGL庫就能夠在前面所創建的系統主繪圖表面上渲染系統的UI瞭。
我們接著往下閱讀代碼:
[cpp]
/*
* Gather OpenGL ES extensions
*/
eglMakeCurrent(display, surface, surface, context);
GLExtensions& extensions(GLExtensions::getInstance());
extensions.initWithGLStrings(
glGetString(GL_VENDOR),
glGetString(GL_RENDERER),
glGetString(GL_VERSION),
glGetString(GL_EXTENSIONS),
eglQueryString(display, EGL_VENDOR),
eglQueryString(display, EGL_VERSION),
eglQueryString(display, EGL_EXTENSIONS));
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);
#ifdef EGL_ANDROID_swap_rectangle
if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) {
if (eglSetSwapRectangleANDROID(display, surface,
0, 0, mWidth, mHeight) == EGL_TRUE) {
// This could fail if this extension is not supported by this
// specific surface (of config)
mFlags |= SWAP_RECTANGLE;
}
}
// when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE
// choose PARTIAL_UPDATES, which should be more efficient
if (mFlags & PARTIAL_UPDATES)
mFlags &= ~SWAP_RECTANGLE;
#endif
這段代碼主要用來檢查系統的主繪圖表面是否支持EGL_ANDROID_swap_rectangle擴展屬性。如果支持的話,那麼每次在調用函數eglSwapBuffers來渲染UI時,都需要把舊的臟區域減去新的臟區域。註意,如果系統的主繪圖表面同時支持EGL_ANDROID_swap_rectangle擴展屬性以及部分更新屬性,那麼將會優先使用部分更新屬性,因為使用部分更新屬性性能會更好一些。
我們接著往下閱讀最後一段代碼:
[cpp]
LOGI("EGL informations:");
LOGI("# of configs : %d", numConfigs);
LOGI("vendor : %s", extensions.getEglVendor());
LOGI("version : %s", extensions.getEglVersion());
LOGI("extensions: %s", extensions.getEglExtension());
LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
LOGI("OpenGL informations:");
LOGI("vendor : %s", extensions.getVendor());
LOGI("renderer : %s", extensions.getRenderer());
LOGI("version : %s", extensions.getVersion());
LOGI("extensions: %s", extensions.getExtension());
LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
LOGI("flags = %08x", mFlags);
// Unbind the context from this thread
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
這段代碼首先調用日志接口LOGI來顯示系統的主繪圖表面的屬性信息,接著最調用函數eglMakeCurrent來取消 設置OpenGL庫在當前線程的繪圖表面以及繪圖上下文。
從這裡就可以看出,一個DisplayHardware對象在初始化完成之後,它還不能直接用來渲染系統的UI,因為它所初始化的的繪圖表面以及繪圖上下文並沒有作為當前線程的繪圖表面以及繪圖上下文。這是由於SurfaceFlinger服務可以同時支持多個DisplayHardware對象,即同時支持多個顯示屏造成的。
從前面SurfaceFlinger類的成員函數readyToRun可以知道,當前正在初始化的DisplayHardware對象的編號為0,並且它是在SurfaceFlinger服務的UI渲染線程中創建的,為瞭可以將它設置系統的主顯示屏,即主繪圖表面,SurfaceFlinger類的成員函數readyToRun接下來還會調用它的成員函數makeCurrent來將它所裡面的繪圖表面以及繪圖上下文設置為SurfaceFlinger服務的UI渲染線程的繪圖表面以及繪圖上下文。
DisplayHardware類的成員函數makeCurrent的實現如下所示:
[cpp]
void DisplayHardware::makeCurrent() const
{
eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
}
這個函數定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。
DisplayHardware類的成員函數makeCurrent的實現很簡單,它隻是通過調用函數eglMakeCurrent來將前面已經創建好的繪圖表面以及繪圖上下文設置為當前線程的繪圖表面以及繪圖上下文,即設置為SurfaceFlinger服務的UI渲染線程的繪圖表面以及繪圖上下文。
系統的硬件幀緩沖區在初始化完成之後,SurfaceFlinger服務以後就可以調用用來描述它的一個DisplayHardware對象的成員函數flip來在它上面渲染系統的UI瞭,這個成員函數的實現如下所示:
[cpp]
void DisplayHardware::flip(const Region& dirty) const
{
checkGLErrors();
EGLDisplay dpy = mDisplay;
EGLSurface surface = mSurface;
#ifdef EGL_ANDROID_swap_rectangle
if (mFlags & SWAP_RECTANGLE) {
const Region newDirty(dirty.intersect(bounds()));
const Rect b(newDirty.getBounds());
eglSetSwapRectangleANDROID(dpy, surface,
b.left, b.top, b.width(), b.height());
}
#endif
if (mFlags & PARTIAL_UPDATES) {
mNativeWindow->setUpdateRectangle(dirty.getBounds());
}
mPageFlipCount++;
eglSwapBuffers(dpy, surface);
checkEGLErrors("eglSwapBuffers");
// for debugging
//glClearColor(1,0,0,0);
//glClear(GL_COLOR_BUFFER_BIT);
}
這個函數定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。
這個函數主要就是調用OpenGL庫中的函數eglSwapBuffers來將系統的UI渲染到系統的主繪圖表面上去的,即渲染到系統的硬件幀緩沖區上去的。在渲染之前,函數會首先判斷系統的主繪圖表面是否支持EGL_ANDROID_swap_rectangle擴展屬性和部分更新屬性。如果支持EGL_ANDROID_swap_rectangle擴展屬性,即DisplayHardware類的成員變量mFlags的SWAP_RECTANGLE位等於1,那麼就需要調用函數eglSetSwapRectangleANDROID來設置要渲染的區域,即舊的臟區域減去新的臟區域所得到的區域。如果支持部分更新屬性,即DisplayHardware類的成員變量mFlags的PARTIAL_UPDATES位等於1,那麼就需要調用DisplayHardware類的成員變量mNativeWindow所描述的一個本地窗口的成員函數setUpdateRectangle來設置要更新的那一部分區域。
DisplayHardware類的成員函數flip在調用函數eglSwapBuffers來渲染UI之前,實際上需要通過其成員變量mNativeWindow所描述的一個本地窗口(FramebufferNativeWindow)來獲得一個空閑的圖形緩沖區,然後才可以將UI數據寫入到這個空閑的圖形緩沖區中去,最後再渲染到硬件幀緩沖區中去。前面提到,FramebufferNativeWindow類的作用類似於在前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一文中所介紹的Surface類,不過它裡面所維護的圖形緩沖區是直接在硬件幀緩沖區上創建的,後面我們在分析FramebufferNativeWindow類的實現時,再詳細分析。
至此,我們就分析完成DisplayHardware類的實現瞭,接下來我們還需要繼續介紹它的父類DisplayHardwareBase的實現,以便可以瞭解DisplayHardware類的另外一個作用,即它還會創建一個線程來監控硬件幀緩沖區的睡眠和喚醒事件。分析完成DisplayHardwareBase類的實現之後,我們最後再分析FramebufferNativeWindow類的實現。
前面在分析DisplayHardware類的實現時提到,DisplayHardware對象在創建的過程中,會對其父類對象DisplayHardwareBase進行初始化,因此,接下來我們就從DisplayHardwareBase對象的初始化過程入手,來分析DisplayHardwareBase類的實現。不過,在分析DisplayHardwareBase類的實現之前,我們首先看看它的類關系圖,如圖2所示。
圖2 DisplayHardwareBase類關系圖
DisplayHardwareBase類一方面用來控制SurfaceFlinger服務當前是否能夠訪問顯示屏。當顯示屏處於喚醒狀態時,DisplayHardwareBase類的成員變量mScreenAcquired的值就會等於1,表示SurfaceFlinger服務就可以訪問顯示屏;而當顯示屏處於睡眠狀態時,DisplayHardwareBase類的成員變量mScreenAcquired的值就會等於0,表示SurfaceFlinger服務不可以訪問顯示屏。
顯示屏的喚醒/睡眠狀態切換是由內核來通知DisplayHardwareBase類的,因此,DisplayHardwareBase類會通過一個線程來監控顯示屏的喚醒/睡眠狀態切換。這個線程是通過DisplayHardwareBase類的成員變量mDisplayEventThread來描述的。DisplayHardwareBase類的成員變量mDisplayEventThread所描述的線程的類型要麼是DisplayEventThread,要麼是ConsoleManagerThread,這兩者均是從DisplayEventThreadBase類繼續下來的,而後者又是從Thread類繼承下來的。當硬件幀緩沖區的控制臺被打開時,DisplayHardwareBase類的成員變量mDisplayEventThread所描述的線程的類型就是DisplayEventThread;當硬件幀緩沖區的控制臺沒有被打開時,DisplayHardwareBase類的成員變量mDisplayEventThread所描述的線程的類型就是ConsoleManagerThread。這裡我們隻考慮硬件幀緩沖區的控制臺被打開的情況。
用來監控顯示屏喚醒/睡眠狀態切換的線程是在DisplayHardwareBase對象的初始化過程中創建的,它運行起來之後,就會在一個無限循環中不斷地監控顯示屏喚醒/睡眠狀態切換事件。為瞭方便描述,我們將這個線程稱為控制臺事件監控線程。DisplayEventThreadBase類的成員變量mFlinger指向瞭SurfaceFlinger服務,一旦控制臺事件監控線程監控到顯示屏發生喚醒/睡眠狀態切換,那麼就會通過它來通知SurfaceFlinger服務。
控制臺事件監控線程的運行過程大概上這樣的。在每一次循環中,控制臺事件監控線程首先監控顯示屏是否要進入睡眠狀態瞭。如果是的話,那麼該線程就會通過DisplayEventThreadBase類的成員變量mFlinger來通知SurfaceFlinger服務,並且等待SurfaceFlinger服務處理完成這個通知。SurfaceFlinger服務一旦處理完成顯示屏進入睡眠狀態的事件,它就會調用DisplayHardwareBase類的成員函數releaseScreen來將其成員變量mScreenAcquired的值設置為0,表示它目前不可以訪問顯示屏。控制臺事件監控線程接下來就會等待顯示屏被喚醒過來。一旦顯示屏被喚醒過來,那麼該線程就會通過DisplayEventThreadBase類的成員變量mFlinger來通知SurfaceFlinger服務。SurfaceFlinger服務得到這個通知之後,就會調用DisplayHardwareBase類的成員函數acquireScreen來將其成員變量mScreenAcquired的值設置為1,表示它目前不可以訪問顯示屏。在下一篇文章分析SurfaceFlinger服務的線程模型時,我們再詳細分析這個過程。
DisplayHardwareBase類另一方面用來控制SurfaceFlinger服務當前是否能夠在顯示屏上渲染UI。當系統的其它組件請求SurfaceFlinger服務關閉顯示屏時,SurfaceFlinger服務就會調用DisplayHardwareBase類的成員函數setCanDraw來將其成員變量mCanDraw的值設置為0;而當系統的其它組件請求SurfaceFlinger服務打開顯示屏時,SurfaceFlinger服務就會調用DisplayHardwareBase類的成員函數setCanDraw來將其成員變量mCanDraw的值設置為1。隻有當DisplayHardwareBase類的成員變量mScreenAcquired和mCanDraw的值均等於1時,SurfaceFlinger服務才可以在顯示屏上渲染系統的UI。為瞭方便SurfaceFlinger服務判斷它當前是否可以在顯示屏上渲染系統的UI,DisplayHardwareBase類提供瞭另外一個成員函數canDraw。當DisplayHardwareBase類的成員函數canDraw的返回值等於true時,就表示SurfaceFlinger服務可以在顯示屏上渲染系統的UI,否則就不可以。
瞭解瞭DisplayHardwareBase類的作用之後,接下來我們就從它的構造函數開始分析它的初始化過程。
DisplayHardwareBase類的構造函數的實現如下所示:
[cpp]
DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
uint32_t displayIndex)
: mCanDraw(true), mScreenAcquired(true)
{
mDisplayEventThread = new DisplayEventThread(flinger);
if (mDisplayEventThread->initCheck() != NO_ERROR) {
// fall-back on the console
mDisplayEventThread = new ConsoleManagerThread(flinger);
}
}
這個函數定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp中。
函數首先創建一個類型為DisplayEventThread的線程。如果這個線程能夠通過初始化檢查,即DisplayEventThread類的成員函數initCheck的返回值等於NO_ERROR,那麼SurfaceFlinger服務就會使用這個類型為DisplayEventThread的線程來監控顯示屏的睡眠/喚醒狀態切換事件,否則的話,函數接下來就會創建另外一個類型為ConsoleManagerThread的線程來監控顯示屏的睡眠/喚醒狀態切換事件。
DisplayEventThread類的成員函數initCheck的實現如下所示:
[cpp]
status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
{
return (((access(kSleepFileName, R_OK) == 0 &&
access(kWakeFileName, R_OK) == 0) ||
(access(kOldSleepFileName, R_OK) == 0 &&
access(kOldWakeFileName, R_OK) == 0)) &&
access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT;
}
這個函數定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp中。
kSleepFileName、kWakeFileName、kOldSleepFileName、kOldWakeFileName和kFbconSysDir是五個字符串常量,它們的定義如下所示:
[cpp]
static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep";
static char const * kWakeFileName = "/sys/power/wait_for_fb_wake";
static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep";
static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake";
// This dir exists if the framebuffer console is present, either built into
// the kernel or loaded as a module.
static char const * const kFbconSysDir = "/sys/class/graphics/fbcon";
當硬件幀緩沖區的控制臺被打開時,幀緩沖區驅動程序就創建一個/sys/class/graphics/fbcon目錄,以及創建/sys/power/wait_for_fb_sleep和/sys/power/wait_for_fb_wake或者/sys/android_power/wait_for_fb_sleep和/sys/android_power/wait_for_fb_wake文件,用來通知用戶空間顯示屏即將要進入睡眠/喚醒狀態瞭。
回到DisplayHardwareBase類的構造函數中,最終創建出來的線程對象保存在其成員變量mDisplayEventThread中。DisplayHardwareBase類的mDisplayEventThread是一個類型為DisplayEventThreadBase的強指針,從前面Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析一文可以知道,當一個強指針第一次引用一個對象的時候,這個對象的成員函數onFirstRef就會被調用,因此,接下來我們就繼續分析DisplayEventThreadBase類的成員函數onFirstRef的實現,看看它在裡面做瞭一件什麼事情。
DisplayEventThreadBase類的成員函數onFirstRef的實現如下所示:
[cpp]
class DisplayHardwareBase
{
……
private:
class DisplayEventThreadBase : public Thread {
……
public:
……
virtual void onFirstRef() {
run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
}
……
};
……
};
這個函數定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h中。
DisplayEventThreadBase類的成員函數onFirstRef主要就調用父類Thread的成員函數run來創建一個名稱為“DisplayEventThread”的線程,用來監控顯示屏的睡眠/喚醒狀態切換事件。這個線程在創建完成之後,首先會調用DisplayEventThread類的成員函數readyToRun來執行一些初始化操作,接下來不斷地循環調用DisplayEventThread類的成員函數threadLoop來監控顯示屏的睡眠/喚醒狀態切換事件。接下來,我們就主要分析這個線程的初始化操作,即DisplayEventThread類的成員函數readyToRun的實現,在接下來的一篇文章中分析SurfaceFlinger服務的線程模型時,再詳細分析這個線程監控顯示屏的睡眠/喚醒狀態切換事件的過程,即DisplayEventThread類的成員函數threadLoop的實現。
DisplayEventThread類的成員函數readyToRun的實現如下所示:
[cpp]
status_t DisplayHardwareBase::DisplayEventThread::readyToRun()
{
if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) {
if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) {
LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName);
return NO_INIT;
}
kSleepFileName = kOldSleepFileName;
kWakeFileName = kOldWakeFileName;
}
return NO_ERROR;
}
這個函數定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp中。
DisplayEventThread類的成員函數readyToRun的實現很簡單,它首先判斷/sys/power/wait_for_fb_sleep和/sys/power/wait_for_fb_wake文件是否存在。如果存在的話,那麼就通過它們來監控顯示屏的睡眠/喚醒狀態切換事件,否則的話,就通過/sys/android_power/wait_for_fb_sleep和/sys/android_power/wait_for_fb_wake文件來監控顯示屏的睡眠/喚醒狀態切換事件。如果這四個文件都不存在,那麼就說明硬件幀緩沖區的控制臺沒有被打開瞭,這時候就不能使用類型為DisplayEventThread的線程來監控顯示屏的睡眠/喚醒狀態切換事件。
至此,我們就分析完成DisplayHardwareBase類的實現瞭,接下來我們繼續分析FramebufferNativeWindow類的實現,以便可以瞭解它是如何管理硬件幀緩沖區的。
在分析FramebufferNativeWindow類的實現之前,我們首先看看它的類關系圖,如圖3所示。
圖3 FramebufferNativeWindow類關系
前面提到,FramebufferNativeWindow類與在前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一文中提到的Surface類的作用是類似的。FramebufferNativeWindow類一方面用來在OpenGL庫和Android本地窗口系統之間建立連接,這樣,我們就可以使用它的成員函數dequeueBuffer來為OpenGL庫分配空閑圖形緩沖區,以及使用它的成員函數queueBuffer來將OpenGL已經填充好UI數據的圖形緩沖區渲染到硬件幀緩沖區中去。FramebufferNativeWindow類另一方面還繼承瞭LightRefBase類,因此,從前面Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析一文可以知道,FramebufferNativeWindow類對象可以結合Android系統的輕量級指針sp來使用,以便可以自動維護生命周期。
FramebufferNativeWindow類與Surface類又有不同的地方。從前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一文可以知道,Surface類使用的圖形緩沖區一般是在匿名共享內存中分配的,並且是由SurfaceFlinger服務來負責分配,然後再傳遞給應用程序進程使用的,而FramebufferNativeWindow類使用的圖形緩沖區是直接在硬件幀緩沖區分配的,並且它可以直接將這些圖形緩沖區渲染到硬件幀緩沖區中去。從前面Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一文可以知道,要從硬件幀緩沖區中分配和渲染圖形緩沖區,就必須要將HAL層中的Gralloc模塊加載到當前的進程空間來,並且打開裡面的gralloc設備和fb設備,其中,gralloc設備用來分配圖形緩沖區,而fb設備用來渲染圖形緩沖區。因此,FramebufferNativeWindow類包含瞭一個類型的alloc_device_t*的成員變量grDev和一個類型為framebuffer_device_t*的成員變量fbDev,它們分別指向HAL層中的Gralloc模塊的gralloc設備和fb設備。
FramebufferNativeWindow類在內部還包含瞭一個類型為sp<NativeBuffer>的數組buffers,用來描述OpenGL庫可以使用的圖形緩沖區,數組的大小等於NUM_FRAME_BUFFERS,即等於硬件幀緩沖區能夠提供的圖形緩沖區的個數。例如,在Android 2.3系統中,硬件幀緩沖區能夠提供的圖形緩沖區的個數等於2,這意味著Android系統可以使用雙緩沖區技術來渲染系統的UI。由於OpenGL庫所使用的圖形緩沖區必須要實現android_native_buffer_t接口,因此,NativeBuffer類繼承瞭android_native_buffer_t類。此外,NativeBuffer類還繼承瞭LightRefBase類,因此,它的對象就和FramebufferNativeWindow類對象一樣,可以結合Android系統的輕量級指針sp來使用,以便可以自動維護生命周期。
瞭解瞭FramebufferNativeWindow類的作用之後,接下來我們就從它的構造函數開始分析它的實現,即分析它的類對象的創建過程。從前面DisplayHardware類的成員函數init的實現可以知道,FramebufferNativeWindow對象是在DisplayHardware對象初始化的過程中創建的,並且包含在DisplayHardware對象內部中,用來管理硬件幀緩沖區。
FramebufferNativeWindow類的構造函數定義在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中,它的實現比較長,我們分段來閱讀:
[cpp]
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);
LOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
err = gralloc_open(module, &grDev);
LOGE_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
mNumBuffers = NUM_FRAME_BUFFERS;
mNumFreeBuffers = NUM_FRAME_BUFFERS;
mBufferHead = mNumBuffers-1;
這段代碼首先調用函數hw_get_module來將HAL層中的Gralloc模塊加載到當前進程來,並且調用函數framebuffer_open和gralloc_open分別打開Gralloc模塊中的fb設備和gralloc設備。這三個過程的實現可以參考前面Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一文。Gralloc模塊中的fb設備和gralloc設備打開之後,就分別保存在FramebufferNativeWindow類的成員變量fbDev和grDev中。
這段代碼接著判斷硬件幀緩沖區是否支持部區更新UI。如果支持的話,那麼從Gralloc模塊中打開的fb設備的成員函數setUpdateRect就不等於0。這時候這段代碼就會FramebufferNativeWindow類的成員變量mUpdateOnDemand的值設置為true。
這段代碼最後還會將FramebufferNativeWindow類的成員變量mNumBuffers和mNumFreeBuffers的值均設置為NUM_FRAME_BUFFERS,它們分別用來描述硬件幀緩沖區可以提供的圖形緩沖區的個數,以及當前可用的空閑圖形緩沖區的個數。此外,FramebufferNativeWindow類的成員變量mBufferHead的值還會被設置為(NUM_FRAME_BUFFERS – 1),表示下一個可用的空閑圖形緩沖區在FramebufferNativeWindow類的成員變量buffers所描述的一個圖形緩沖區數組的位置。
我們接著往下閱讀代碼:
[cpp]
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);
LOGE_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;
}
}
這段代碼用來創建FramebufferNativeWindow類的成員變量buffers所描述的一個圖形緩沖區數組。每一個圖形緩沖區都使用一個NativeBuffer對象來描述,並且這些圖形緩沖區都是通過調用HAL層中的Gralloc模塊的gralloc設備的成員函數alloc來分配的。註意,在分配圖形緩沖區時,指定的標志,即第5個參數的值為GRALLOC_USAGE_HW_FB。這意味著FramebufferNativeWindow類所管理的圖形緩沖區都是直接在硬件幀緩沖區上分配的,而不是在匿名共享內存中分配的。
我們接著往下閱讀代碼:
[cpp]
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;
se {
LOGE("Couldn't get gralloc module");
這段代碼主要就是用來設置FramebufferNativeWindow的父類ANativeWindow的成員變量flags、xdpi、ydpi、minSwapInternal和maxSwapInterval的值,以便OpenGL庫可以知道系統當前所使用的硬件幀緩沖區的一些屬性,例如,點密度、緩沖區數據交換時間間隔等信息。這些成員變量的具體含義可以參考前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一文所提到的Surface類的初始化過程。
我們接著往下閱讀代碼:
[cpp]
ANativeWindow::setSwapInterval = setSwapInterval;
ANativeWindow::dequeueBuffer = dequeueBuffer;
ANativeWindow::lockBuffer = lockBuffer;
ANativeWindow::queueBuffer = queueBuffer;
ANativeWindow::query = query;
ANativeWindow::perform = perform;
}
這段代碼用來設置FramebufferNativeWindow的父類ANativeWindow的成員函數setSwapInterval、dequeueBuffer、lockBuffer、queueBuffer、query和perform,它們都是OpenGL的回調接口,分別指向FramebufferNativeWindow類的靜態成員函數setSwapInterval、dequeueBuffer、lockBuffer、queueBuffer、query和perform。與前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一文所提到的Surface類一樣,我們隻關註FramebufferNativeWindow類的靜態成員函數dequeueBuffer和queueBuffer的實現,因為它們負責用來為OpenGL庫分配和渲染圖形緩沖區。
FramebufferNativeWindow類的成員函數dequeueBuffer的實現如下所示:
[cpp]
int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window,
android_native_buffer_t** buffer)
{
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;
GraphicLog& logger(GraphicLog::getInstance());
logger.log(GraphicLog::SF_FB_DEQUEUE_BEFORE, index);
// wait for a free buffer
while (!self->mNumFreeBuffers) {
self->mCondition.wait(self->mutex);
}
// get this buffer
self->mNumFreeBuffers–;
self->mCurrentBufferIndex = index;
*buffer = self->buffers[index].get();
logger.log(GraphicLog::SF_FB_DEQUEUE_AFTER, index);
return 0;
}
這個函數定義在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中。
參數window雖然是一個類型為ANativeWindow的指針,但是它指向的實際上是一個FramebufferNativeWindow對象,這個FramebufferNativeWindow對象是在DisplayHardware類的成員函數init中創建的,因此,函數在開始的地方就可以將它轉換一個FramebufferNativeWindow對象self。
有瞭FramebufferNativeWindow對象self之後,我們就可以在它內部的圖形緩沖區數組buffers中獲取下一個空閑圖形緩沖區。前面提到,下一個空閑圖形緩沖區的在數組buffer中的位置就保存在FramebufferNativeWindow對象self的成員變量mBufferHead中。因此,函數就可以將FramebufferNativeWindow對象self的成員變量mBufferHead的值取出來保存在變量index中,以便接下來可以從FramebufferNativeWindow對象self內部的圖形緩沖區數組buffers中取出一個圖形緩沖區。此外,函數還需要將FramebufferNativeWindow對象self的成員變量mBufferHead增加1,以便它可以指向下一個空閑的圖形緩沖區。註意,FramebufferNativeWindow對象self內部的圖形緩沖區數組buffers是循環使用的,因此,在將它的成員變量mBufferHead增加1之後,要判斷它的值是否已經大於等於數組的大小,如果大於等於的話,就需要將它的值設置為0,即繞回到前面去。
從前面的分析可以知道,FramebufferNativeWindow對象self內部可用的空閑圖形緩沖區的個數保存在其成員變量mNumFreeBuffers中,因此,當這個成員變量的值等於0的時候,就表示FramebufferNativeWindow對象self沒有空閑的空閑圖形緩沖區可用,這時候當前線程就會通過FramebufferNativeWindow對象self的成員變量mCondition所描述的一個條件變量進入到睡眠等待狀態,直到有可用的空閑圖形緩沖區為止。什麼時候FramebufferNativeWindow對象self內部才會有可用的空閑圖形緩沖區呢?當OpenGL庫請求FramebufferNativeWindow對象self將一個圖形緩沖區的內容渲染到硬件幀緩沖區之後,FramebufferNativeWindow對象self就會獲得一個可用的空閑圖形緩沖區瞭,後面我們分析FramebufferNativeWindow類的成員函數queueBuffer的實現時就會看到這個邏輯。
一旦FramebufferNativeWindow對象self內部有可用的空閑圖形緩沖區,那麼函數就會將這個空閑圖形緩沖區就會返回給OpenGL庫,即保存在輸出參數buffer,並且將FramebufferNativeWindow對象self內部可用的空閑圖形緩沖區的個數減1,以及將OpenGL庫當前正前正在使用的圖形緩沖區在數組buffers中的位置保存在FramebufferNativeWindow對象self的成員變量mCurrentBufferIndex中。
至此,我們就分析完成FramebufferNativeWindow類的成員函數dequeueBuffer的實現瞭,接下來我們繼續分析FramebufferNativeWindow類的成員函數queueBuffer的實現,如下所示:
[cpp]
int FramebufferNativeWindow::queueBuffer(ANativeWindow* window,
android_native_buffer_t* buffer)
{
FramebufferNativeWindow* self = getSelf(window);
Mutex::Autolock _l(self->mutex);
framebuffer_device_t* fb = self->fbDev;
buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle;
const int index = self->mCurrentBufferIndex;
GraphicLog& logger(GraphicLog::getInstance());
logger.log(GraphicLog::SF_FB_POST_BEFORE, index);
int res = fb->post(fb, handle);
logger.log(GraphicLog::SF_FB_POST_AFTER, index);
self->front = static_cast<NativeBuffer*>(buffer);
self->mNumFreeBuffers++;
self->mCondition.broadcast();
return res;
}
這個函數定義在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中。
參數window指向的實際上也是一個FramebufferNativeWindow對象,這個FramebufferNativeWindow對象是在DisplayHardware類的成員函數init中創建的,因此,函數在開始的地方同樣是先將它轉換一個FramebufferNativeWindow對象self。
參數buffer指向的是一個實際類型為NativeBuffer的圖形緩沖區,這個圖形緩沖區是在FramebufferNativeWindow類的成員函數dequeueBuffer中分配的,如前所述。
FramebufferNativeWindow類的成員函數queueBuffer目標就是要將參數buffer所描述的圖形緩沖區渲染到硬件幀緩沖區中去,因此,我們就需要獲得FramebufferNativeWindow對象self的成員變量fbDev所描述的一個fb設備。有瞭這個fb設備之後, 我們就可以調用它的成員函數post來將參數buffer所描述的圖形緩沖區渲染到硬件幀緩沖區中去,這個過程可以參考Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一文。
參數buffer所描述的圖形緩沖區被渲染到硬件幀緩沖區中去之後,它就變成一個空閑的圖形緩沖區瞭,因此,我們就需要將它返回給FramebufferNativeWindow對象self內部的圖形緩沖區數組buffers中去,並且將可用的空閑圖形緩沖區的個數增加1,最後通過FramebufferNativeWindow對象self的成員變量mCondition所描述的一個條件變量將前面正在等待從FramebufferNativeWindow對象self內部分配空閑圖形緩的線程喚醒。
至此,FramebufferNativeWindow類的成員函數queueBuffer的實現就分析完成瞭,FramebufferNativeWindow類的實現也分析完成瞭。通過GraphicPlane、DisplayHardware和FramebufferNativeWindow這三個類的實現,我們就可以知道SurfaceFlinger服務是如何管理系統的顯示屏,即系統的硬件幀緩沖區的瞭。
從SurfaceFlinger服務創建一個DisplayHardwareBase對象來管理系統的顯示屏的過程可以知道,這個DisplayHardwareBase對象會創建一個控制臺事件監控線程來監控硬件幀緩沖區的睡眠/喚醒狀態切換事件,而從前面Android系統Surface制的SurfaceFlinger服務的啟動過程分析一文又可以知道,System進程在啟動SurfaceFlinger服務過程中,又會創建一個Binder線程池,以及為SurfaceFlinger服務創建一個UI渲染線程,這樣在SurfaceFlinger服務中,就存在三種不同類型的線程,在接下來的一篇文章中,我們就將分析詳細SurfaceFlinger服務的線程模型,以便最後我們就可以更好地分析SurfaceFlinger服務的實現,敬請關註!