在前文中,我們分析了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] view plaincopy
- 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;
- }
- }
- 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
- 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;
這段代碼主要用來檢查系統的主繪圖表面是否支援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);
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());
- 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);
這個函數主要就是調用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;
這個函數定義在檔案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)
- 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);
- 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)
- buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle;
- const int index = self->mCurrentBufferIndex;
- 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服務的實作,敬請關注!