在前文中,我們分析了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服務在啟動的過程中,會對系統的硬件幀緩沖區進行初始化,如下所示:
- 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來設備顯示屏的實際度度、高度以及旋轉方向,如下所示:
- 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時,對於一個任意的點向量,只要將它乘以全局變換矩陣mGlobalTransform,那么就可以得到它所描述的實際位置。
至此,編號為0的GraphicPlane對象的初始化過程就完成了,以后SurfaceFlinger服務就可以調用它的成員函數displayHardware來獲得它內部的一個DisplayHardware對象,如下所示:
- const DisplayHardware& GraphicPlane::displayHardware() const {
- return *mHw;
- }
這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
接下來,回到前面SurfaceFlinger類的成員函數readyToRun中,我們通過DisplayHardware對象的創建過程來分析DisplayHardware類的實現。
在創建DisplayHardware對象的過程中,會調用DisplayHardware類的構造函數,如下所示:
- 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的實現比較長,我們分段來閱讀:
- 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設備的實現。
我們接着往下閱讀代碼:
- 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庫。
我們接着往下閱讀代碼:
- // 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。有了這些配置信息之后,接下來就可以在硬件幀緩沖區上面創建系統的主繪圖表面。
我們接着往下閱讀代碼:
- 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的大小,以及是否支持部分更新、是否使用慢渲染方式等。
我們接着往下閱讀代碼:
- /*
- * 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時,不保留后端圖形緩沖區的內容,因為保留是有代價的。如果不支持的話,那么就會就會調用函數eglQuerySurface來檢查在調用函數eglSwapBuffers來渲染系統的UI時是否需要保留后端圖形緩沖區的內容。如果需要的話,那么就會將DisplayHardware類的成員變量mFlags的BUFFER_PRESERVED位設置為1。在保留后端圖形緩沖區的內容的情況下,系統就可以支持僅僅渲染那些需要更新的臟區域,這些區域可以是不規則的。然而,實現不規則區域部分更新功能是有代價的,因為每次在渲染UI時,都要將后端圖形緩沖區的內容拷貝回那些不在那些需要更新的區域中去,這會導致性能低下。因此,系統一般都不支持不規則區域部分更新功能。
我們接着往下閱讀代碼:
- /* 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。
我們接着往下閱讀代碼:
- /*
- * 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了。
我們接着往下閱讀代碼:
- /*
- * 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時,都會使用軟件的方式來支持部分更新區域功能,即:先得到不在新臟區域里面的那部分舊臟區域的內容,然后再將得到的這部分舊臟區域的內容拷貝回到要渲染的新圖形緩沖區中去,這要求每次在渲染UI時,都要將被渲染的圖形緩沖區以及對應的臟區域保存下來。注意,如果系統的主繪圖表面同時支持EGL_ANDROID_swap_rectangle擴展屬性以及部分更新屬性,那么將會優先使用部分更新屬性,因為后者是直接在硬件上支持部分更新,因而性能會更好。
我們接着往下閱讀最后一段代碼:
- 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的實現如下所示:
- 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了,這個成員函數的實現如下所示:
- 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來設置要渲染的區域,以便在渲染UI時,可以通過軟件的方式來支持部分更新。如果硬件幀緩沖區直接支持部分更新屬性,即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類的構造函數的實現如下所示:
- 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的實現如下所示:
- 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是五個字符串常量,它們的定義如下所示:
- 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的實現如下所示:
- 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的實現如下所示:
- 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中,它的實現比較長,我們分段來閱讀:
- 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所描述的一個圖形緩沖區數組的位置。
我們接着往下閱讀代碼:
- 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類所管理的圖形緩沖區都是直接在硬件幀緩沖區上分配的,而不是在匿名共享內存中分配的。
我們接着往下閱讀代碼:
- 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類的初始化過程。
我們接着往下閱讀代碼:
- 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的實現如下所示:
- 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的實現,如下所示:
- 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服務的實現,敬請關注!
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!