Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析
? ?在前文中,我們分析了SurfaceFlinger服務的啟動過程。SurfaceFlinger服務在啟動的過程中,會對系統(tǒng)的硬件幀緩沖區(qū)進行初始化。由于系統(tǒng)的硬件幀緩沖區(qū)一般只有一個,并且不是誰都可以隨便訪問的,因此,它就需要由一個服務來統(tǒng)一管理。在Android系統(tǒng)中,這個服務便是SurfaceFlinger。在本文中,我們就詳細分析SurfaceFlinger服務是如何管理系統(tǒng)的硬件幀緩沖區(qū)的。
?? ? ? ?從前面Android系統(tǒng)Surface機制的SurfaceFlinger服務簡要介紹和學習計劃一文可以知道,SurfaceFlinger服務通過一個GraphicPlane對象來描述系統(tǒng)的顯示屏,即系統(tǒng)的硬件幀緩沖區(qū)。GraphicPlane類內(nèi)部聚合了一個DisplayHardware對象,通過這個DisplayHardware對象就可以訪問系統(tǒng)的硬件幀緩沖區(qū)。DisplayHardware類內(nèi)部又包含了一個FramebufferNativeWindow對象,這個FramebufferNativeWindow對象才是真正用來描述系統(tǒng)的硬件幀緩沖區(qū)的。FramebufferNativeWindow類的作用類似于在前面Android應用程序請求SurfaceFlinger服務創(chuàng)建Surface的過程分析一文中所介紹的Surface類,它是連接OpenGL庫和Android的UI系統(tǒng)的一個橋梁,OpenGL庫就是通過這個橋梁來將Android系統(tǒng)的UI渲染到硬件幀緩沖區(qū)中去的。GraphicPlane、DisplayHardware和FramebufferNativeWindow這三個類的關(guān)系如圖1所示。
圖1 GraphicPlane、DisplayHardware和FramebufferNativeWindow的類關(guān)系圖
?? ? ? 接下來,我們就分別介紹GraphicPlane、DisplayHardware和FramebufferNativeWindow這三個類的實現(xiàn),以便可以理解SurfaceFlinger服務是如何通過它們來管理系統(tǒng)的硬件幀緩沖區(qū)的。
?? ? ? 從前面Android系統(tǒng)Surface制的SurfaceFlinger服務的啟動過程分析一文可以知道,SurfaceFlinger服務在啟動的過程中,會對系統(tǒng)的硬件幀緩沖區(qū)進行初始化,如下所示:
?
[cpp]?view plaincopy?? ? ??這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
?
?? ? ? 這個代碼段首先創(chuàng)建了一個DisplayHardware對象,用來初始化編號為0的GraphicPlane對象,接著再將這個DisplayHardware對象設(shè)置為系統(tǒng)當前活動的DisplayHardware對象,這就相當于是將編號為0的GraphicPlane對象所描述的顯示屏設(shè)置為系統(tǒng)當前活動的顯示屏。
?? ? ? 接下來,我們就首先分析編號為0的GraphicPlane對象的初始化過程,接著再分析DisplayHardware對象的創(chuàng)建過程。
?? ? ??編號為0的GraphicPlane對象的初始化過程主要是調(diào)用GraphicPlane類的成員函數(shù)setDisplayHardware來實現(xiàn)的,如下所示:
?
[cpp]?view plaincopy?? ? ? 這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
?
?? ? ? 函數(shù)首先設(shè)置顯示屏的初始大小和旋轉(zhuǎn)方向。GraphicPlane類有三個成員變量mDisplayWidth、mDisplayHeight和mDisplayTransform,前兩者的類型為float,分別用描述顯示屏的初始寬度和高度,而后者的類型為Transform,用來描述顯示屏的初始旋轉(zhuǎn)矩陣。Transform類的作用是來描述變換矩陣,以便后面在渲染UI時可以用來動態(tài)地計算顯示屏的大小和旋轉(zhuǎn)方向等。
?? ? ? 顯示屏的初始化寬度和高度是由參數(shù)hw所描述的一個DisplayHardware對象來描述的,而顯示屏的初始旋轉(zhuǎn)方向則是由名稱為“ro.sf.hwrotation”的系統(tǒng)屬性來決定的。如果沒有設(shè)置名稱為“ro.sf.hwrotation”的系統(tǒng)屬性,那么顯示屏的旋轉(zhuǎn)方向就為默認方向,即ISurfaceComposer::eOrientationDefault。
?? ? ? 獲得了顯示屏的初始化寬度w、高度h和旋轉(zhuǎn)方向displayOrientation之后,函數(shù)接著就調(diào)用GraphicPlane類的靜態(tài)成員函數(shù)orientationToTransfrom來將它們構(gòu)造成一個變換矩陣,并且保存在GraphicPlane類的成員變量mDisplayTransform中。
?? ? ? 函數(shù)接下來繼續(xù)判斷顯示屏的初始化旋轉(zhuǎn)方向是否將初始化寬度和高度值翻轉(zhuǎn)了。如果翻轉(zhuǎn)了,那么就需要相互調(diào)換GraphicPlane類的成員變量mDisplayWidth和mDisplayHeight的值,以便可以正確地反映顯示屏的初始化寬度和高度。
?? ? ? 注意,顯示屏的初始寬度、高度和旋轉(zhuǎn)方向一經(jīng)初始化之后,就會保持不變,以后顯示屏的實際旋轉(zhuǎn)方向計算都是要在此基礎(chǔ)上進行計算的,即要在變換矩陣mDisplayTransform的基礎(chǔ)上進行計算。從這里還可以看出,通過設(shè)置名稱為“ro.sf.hwrotation”的系統(tǒng)屬性的值,就可以設(shè)置系統(tǒng)顯示屏的初始化旋轉(zhuǎn)方向,以便匹配實際的硬件幀緩沖區(qū)的旋轉(zhuǎn)方向。
?? ? ? 函數(shù)最后調(diào)用GraphicPlane類的成員函數(shù)setOrientation來設(shè)備顯示屏的實際度度、高度以及旋轉(zhuǎn)方向,如下所示:
?
[cpp]?view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
?
?? ? ? ?參數(shù)orientation的值等于ISurfaceComposer::eOrientationDefault,即SurfaceFlinger服務在初始化系統(tǒng)顯示屏時,會將它的旋轉(zhuǎn)方向設(shè)置為默認值,以后再根據(jù)實際情況來做調(diào)整。
?? ? ? GraphicPlane類有三個成員變量mWidth、mHeight和mOrientation,它們的類型均為int,分別用來描述顯示屏的實際寬度、高度和旋轉(zhuǎn)方向。與成員變量mDisplayWidth、mDisplayHeight和mDisplayTransform所描述的顯示屏初始化寬度、高度和旋轉(zhuǎn)矩陣一經(jīng)初始化后就保持不變不同,mWidth、mHeight和mOrientation這三個成員變量是會動態(tài)變化的。例如,當顯示屏由LANDSCAPE變?yōu)镻ORTRAIT模式時,mWidth、mHeight和mOrientation這三個成員變量就會相應地發(fā)生改變。
?? ? ? 函數(shù)首先將顯示屏的實際寬度mWidth、高度mHeight和旋轉(zhuǎn)方向mOrientation設(shè)置為顯示屏的初始寬度mDisplayWidth、高度mDisplayHeight以及參數(shù)orientation所描述的旋轉(zhuǎn)方向,接著再調(diào)用GraphicPlane類的靜態(tài)成員函數(shù)orientationToTransfrom來將它們構(gòu)造成一個變換矩陣orientationTransform。
?? ? ? 函數(shù)接著判斷顯示屏的實際旋轉(zhuǎn)方向orientation是否將原來的實際寬度和高度值翻轉(zhuǎn)了。如果翻轉(zhuǎn)了,那么就需要相互調(diào)換GraphicPlane類的成員變量mWidth和mHeight的值,以便可以正確地反映顯示屏的實際寬度和高度。
?? ? ? 函數(shù)最后將用來描述顯示屏的初始化旋轉(zhuǎn)方向的變換矩陣mDisplayTransform和用來描述顯示屏的實際旋轉(zhuǎn)方向的變換矩陣orientationTransform相乘,就可以得到一個全局變換矩陣,并且保存在GraphicPlane類的成員變量mGlobalTransform中。這樣以后渲染UI時,對于一個任意的點向量,只要將它乘以全局變換矩陣mGlobalTransform,那么就可以得到它所描述的實際位置。
?? ? ? 至此,編號為0的GraphicPlane對象的初始化過程就完成了,以后SurfaceFlinger服務就可以調(diào)用它的成員函數(shù)displayHardware來獲得它內(nèi)部的一個DisplayHardware對象,如下所示:
?
[cpp]?view plaincopy?? ? ?這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
?
?? ? ?接下來,回到前面SurfaceFlinger類的成員函數(shù)readyToRun中,我們通過DisplayHardware對象的創(chuàng)建過程來分析DisplayHardware類的實現(xiàn)。
?? ? ?在創(chuàng)建DisplayHardware對象的過程中,會調(diào)用DisplayHardware類的構(gòu)造函數(shù),如下所示:
?
[cpp]?view plaincopy?? ? ? 這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。
?
?? ? ? 從這里可以看出,DisplayHardware類是從DisplayHardwareBase類繼承下來的。接下來,我們首先繼續(xù)分析一個DisplayHardware對象的初始化過程,接著再分析這個DisplayHardware對象的父對象DisplayHardwareBase的初始化過程,以便可以了解DisplayHardwareBase類的實現(xiàn)。
?? ? ??一個DisplayHardware對象的初始化過程是通過調(diào)用DisplayHardware類的成員函數(shù)init來實現(xiàn)的。DisplayHardware類的成員函數(shù)init的實現(xiàn)比較長,我們分段來閱讀:
?
[cpp]?view plaincopy?? ? ? ?這段代碼首先是創(chuàng)建了一個FramebufferNativeWindow對象,并且保存在DisplayHardware類的成員變量mNativeWindow中,用來管理硬件幀緩沖區(qū)。有了這個FramebufferNativeWindow對象之后,就可以通過它里面的一個fb設(shè)備來獲得硬件幀緩沖區(qū)的點密度以及刷新頻率等信息。后面我們再詳細分析FramebufferNativeWindow類的實現(xiàn)。
?
?? ? ? ?這段代碼接著再加載HAL層中的overlay模塊,目的是要打開系統(tǒng)的overlay設(shè)備。在Android系統(tǒng)中,我們可以將overlay看作是一種特殊的Surface,一般用來顯示視頻。在這一系列文章中,我們暫時不關(guān)心overlay設(shè)備的實現(xiàn)。
?? ? ? ?我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼主要用來設(shè)置一個EGL屬性數(shù)組attribs,以便接下來可以根據(jù)這個屬性數(shù)組的值來正確的初始化EGL庫。
?
?? ? ? ?我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼首先調(diào)用eglGetDisplay和eglInitialize函數(shù)來獲得和初始化OpengGL庫的默認顯示屏,接著再調(diào)用EGLUtils::selectConfigForNativeWindow函數(shù)來獲得前面所創(chuàng)建的一個FramebufferNativeWindow對象所描述的系統(tǒng)主繪圖表面的配置信息,并且保存在EGLConfig對象config。有了這些配置信息之后,接下來就可以在硬件幀緩沖區(qū)上面創(chuàng)建系統(tǒng)的主繪圖表面。
?? ? ? ?我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼主要是用來獲得系統(tǒng)主繪圖表面的一些屬性,例如,四個顏色分量R、G、B和A的大小,以及是否支持部分更新、是否使用慢渲染方式等。
?
?? ? ? ?我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼首先調(diào)用函數(shù)eglCreateWindowSurface來創(chuàng)建系統(tǒng)的主繪圖表面。系統(tǒng)的主繪圖表面是直接在硬件幀緩沖區(qū)上創(chuàng)建的,用來渲染系統(tǒng)的UI,即負責合成和渲染所有應用程序的UI。
?
?? ? ? ?這段代碼接著還獲得系統(tǒng)的主繪圖表面的寬度和高度,分別保存在并且保存在DisplayHardware類的成員變量mWidth和mHeight中。
?? ? ? ?這段代碼最后還判斷硬件幀緩沖區(qū)是否支持部分更新。如果支持的話,就會在調(diào)用函數(shù)eglSwapBuffers來渲染系統(tǒng)的UI時,不保留后端圖形緩沖區(qū)的內(nèi)容,因為保留是有代價的。如果不支持的話,那么就會就會調(diào)用函數(shù)eglQuerySurface來檢查在調(diào)用函數(shù)eglSwapBuffers來渲染系統(tǒng)的UI時是否需要保留后端圖形緩沖區(qū)的內(nèi)容。如果需要的話,那么就會將DisplayHardware類的成員變量mFlags的BUFFER_PRESERVED位設(shè)置為1。在保留后端圖形緩沖區(qū)的內(nèi)容的情況下,系統(tǒng)就可以支持僅僅渲染那些需要更新的臟區(qū)域,這些區(qū)域可以是不規(guī)則的。然而,實現(xiàn)不規(guī)則區(qū)域部分更新功能是有代價的,因為每次在渲染UI時,都要將后端圖形緩沖區(qū)的內(nèi)容拷貝回那些不在那些需要更新的區(qū)域中去,這會導致性能低下。因此,系統(tǒng)一般都不支持不規(guī)則區(qū)域部分更新功能。
?? ? ? ?我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼用來設(shè)備系統(tǒng)的主繪圖表面的點密度信息。系統(tǒng)的主繪圖表面的點密度信息可以通過名稱為“qemu.sf.lcd_density”或者“ro.sf.lcd_density”的系統(tǒng)屬性來配置。如果沒有配置,那么默認值就為160dpi。
?? ? ? ?我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼主要是調(diào)用函數(shù)eglCreateContext來創(chuàng)建系統(tǒng)的主繪圖表面的上下文。有了這個上下文之后,OpenGL庫就能夠在前面所創(chuàng)建的系統(tǒng)主繪圖表面上渲染系統(tǒng)的UI了。
?
?? ? ? ?我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼主要用來檢查系統(tǒng)的主繪圖表面是否支持EGL_ANDROID_swap_rectangle擴展屬性。如果支持的話,那么每次在調(diào)用函數(shù)eglSwapBuffers來渲染UI時,都會使用軟件的方式來支持部分更新區(qū)域功能,即:先得到不在新臟區(qū)域里面的那部分舊臟區(qū)域的內(nèi)容,然后再將得到的這部分舊臟區(qū)域的內(nèi)容拷貝回到要渲染的新圖形緩沖區(qū)中去,這要求每次在渲染UI時,都要將被渲染的圖形緩沖區(qū)以及對應的臟區(qū)域保存下來。注意,如果系統(tǒng)的主繪圖表面同時支持EGL_ANDROID_swap_rectangle擴展屬性以及部分更新屬性,那么將會優(yōu)先使用部分更新屬性,因為后者是直接在硬件上支持部分更新,因而性能會更好。
?
?? ? ? ?我們接著往下閱讀最后一段代碼:
?
[cpp]?view plaincopy?? ? ? 這段代碼首先調(diào)用日志接口LOGI來顯示系統(tǒng)的主繪圖表面的屬性信息,接著最調(diào)用函數(shù)eglMakeCurrent來取消 設(shè)置OpenGL庫在當前線程的繪圖表面以及繪圖上下文。
?
?? ? ? 從這里就可以看出,一個DisplayHardware對象在初始化完成之后,它還不能直接用來渲染系統(tǒng)的UI,因為它所初始化的的繪圖表面以及繪圖上下文并沒有作為當前線程的繪圖表面以及繪圖上下文。這是由于SurfaceFlinger服務可以同時支持多個DisplayHardware對象,即同時支持多個顯示屏造成的。
?? ? ? 從前面SurfaceFlinger類的成員函數(shù)readyToRun可以知道,當前正在初始化的DisplayHardware對象的編號為0,并且它是在SurfaceFlinger服務的UI渲染線程中創(chuàng)建的,為了可以將它設(shè)置系統(tǒng)的主顯示屏,即主繪圖表面,SurfaceFlinger類的成員函數(shù)readyToRun接下來還會調(diào)用它的成員函數(shù)makeCurrent來將它所里面的繪圖表面以及繪圖上下文設(shè)置為SurfaceFlinger服務的UI渲染線程的繪圖表面以及繪圖上下文。
?? ? ??DisplayHardware類的成員函數(shù)makeCurrent的實現(xiàn)如下所示:
?
[cpp]?view plaincopy?? ? ? 這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。
?
?? ? ??DisplayHardware類的成員函數(shù)makeCurrent的實現(xiàn)很簡單,它只是通過調(diào)用函數(shù)eglMakeCurrent來將前面已經(jīng)創(chuàng)建好的繪圖表面以及繪圖上下文設(shè)置為當前線程的繪圖表面以及繪圖上下文,即設(shè)置為SurfaceFlinger服務的UI渲染線程的繪圖表面以及繪圖上下文。
?? ? ? 系統(tǒng)的硬件幀緩沖區(qū)在初始化完成之后,SurfaceFlinger服務以后就可以調(diào)用用來描述它的一個DisplayHardware對象的成員函數(shù)flip來在它上面渲染系統(tǒng)的UI了,這個成員函數(shù)的實現(xiàn)如下所示:
?
[cpp]?view plaincopy?? ? ? 這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。
?
?? ? ? 這個函數(shù)主要就是調(diào)用OpenGL庫中的函數(shù)eglSwapBuffers來將系統(tǒng)的UI渲染到系統(tǒng)的主繪圖表面上去的,即渲染到系統(tǒng)的硬件幀緩沖區(qū)上去的。在渲染之前,函數(shù)會首先判斷系統(tǒng)的主繪圖表面是否支持EGL_ANDROID_swap_rectangle擴展屬性和部分更新屬性。如果支持EGL_ANDROID_swap_rectangle擴展屬性,即DisplayHardware類的成員變量mFlags的SWAP_RECTANGLE位等于1,那么就需要調(diào)用函數(shù)eglSetSwapRectangleANDROID來設(shè)置要渲染的區(qū)域,以便在渲染UI時,可以通過軟件的方式來支持部分更新。如果硬件幀緩沖區(qū)直接支持部分更新屬性,即DisplayHardware類的成員變量mFlags的PARTIAL_UPDATES位等于1,那么就需要調(diào)用DisplayHardware類的成員變量mNativeWindow所描述的一個本地窗口的成員函數(shù)setUpdateRectangle來設(shè)置要更新的那一部分區(qū)域。
?? ? ? ?DisplayHardware類的成員函數(shù)flip在調(diào)用函數(shù)eglSwapBuffers來渲染UI之前,實際上需要通過其成員變量mNativeWindow所描述的一個本地窗口(FramebufferNativeWindow)來獲得一個空閑的圖形緩沖區(qū),然后才可以將UI數(shù)據(jù)寫入到這個空閑的圖形緩沖區(qū)中去,最后再渲染到硬件幀緩沖區(qū)中去。前面提到,FramebufferNativeWindow類的作用類似于在前面Android應用程序請求SurfaceFlinger服務創(chuàng)建Surface的過程分析一文中所介紹的Surface類,不過它里面所維護的圖形緩沖區(qū)是直接在硬件幀緩沖區(qū)上創(chuàng)建的,后面我們在分析FramebufferNativeWindow類的實現(xiàn)時,再詳細分析。
?? ? ? ?至此,我們就分析完成DisplayHardware類的實現(xiàn)了,接下來我們還需要繼續(xù)介紹它的父類DisplayHardwareBase的實現(xiàn),以便可以了解DisplayHardware類的另外一個作用,即它還會創(chuàng)建一個線程來監(jiān)控硬件幀緩沖區(qū)的睡眠和喚醒事件。分析完成DisplayHardwareBase類的實現(xiàn)之后,我們最后再分析FramebufferNativeWindow類的實現(xiàn)。
?? ? ? ?前面在分析DisplayHardware類的實現(xiàn)時提到,DisplayHardware對象在創(chuàng)建的過程中,會對其父類對象DisplayHardwareBase進行初始化,因此,接下來我們就從DisplayHardwareBase對象的初始化過程入手,來分析DisplayHardwareBase類的實現(xiàn)。不過,在分析DisplayHardwareBase類的實現(xiàn)之前,我們首先看看它的類關(guān)系圖,如圖2所示。
圖2 DisplayHardwareBase類關(guān)系圖
?? ? ? ?DisplayHardwareBase類一方面用來控制SurfaceFlinger服務當前是否能夠訪問顯示屏。當顯示屏處于喚醒狀態(tài)時,DisplayHardwareBase類的成員變量mScreenAcquired的值就會等于1,表示SurfaceFlinger服務就可以訪問顯示屏;而當顯示屏處于睡眠狀態(tài)時,DisplayHardwareBase類的成員變量mScreenAcquired的值就會等于0,表示SurfaceFlinger服務不可以訪問顯示屏。
?? ? ? ?顯示屏的喚醒/睡眠狀態(tài)切換是由內(nèi)核來通知DisplayHardwareBase類的,因此,DisplayHardwareBase類會通過一個線程來監(jiān)控顯示屏的喚醒/睡眠狀態(tài)切換。這個線程是通過DisplayHardwareBase類的成員變量mDisplayEventThread來描述的。DisplayHardwareBase類的成員變量mDisplayEventThread所描述的線程的類型要么是DisplayEventThread,要么是ConsoleManagerThread,這兩者均是從DisplayEventThreadBase類繼續(xù)下來的,而后者又是從Thread類繼承下來的。當硬件幀緩沖區(qū)的控制臺被打開時,DisplayHardwareBase類的成員變量mDisplayEventThread所描述的線程的類型就是DisplayEventThread;當硬件幀緩沖區(qū)的控制臺沒有被打開時,DisplayHardwareBase類的成員變量mDisplayEventThread所描述的線程的類型就是ConsoleManagerThread。這里我們只考慮硬件幀緩沖區(qū)的控制臺被打開的情況。
?? ? ? ?用來監(jiān)控顯示屏喚醒/睡眠狀態(tài)切換的線程是在DisplayHardwareBase對象的初始化過程中創(chuàng)建的,它運行起來之后,就會在一個無限循環(huán)中不斷地監(jiān)控顯示屏喚醒/睡眠狀態(tài)切換事件。為了方便描述,我們將這個線程稱為控制臺事件監(jiān)控線程。DisplayEventThreadBase類的成員變量mFlinger指向了SurfaceFlinger服務,一旦控制臺事件監(jiān)控線程監(jiān)控到顯示屏發(fā)生喚醒/睡眠狀態(tài)切換,那么就會通過它來通知SurfaceFlinger服務。
?? ? ??控制臺事件監(jiān)控線程的運行過程大概上這樣的。在每一次循環(huán)中,控制臺事件監(jiān)控線程首先監(jiān)控顯示屏是否要進入睡眠狀態(tài)了。如果是的話,那么該線程就會通過DisplayEventThreadBase類的成員變量mFlinger來通知SurfaceFlinger服務,并且等待SurfaceFlinger服務處理完成這個通知。SurfaceFlinger服務一旦處理完成顯示屏進入睡眠狀態(tài)的事件,它就會調(diào)用DisplayHardwareBase類的成員函數(shù)releaseScreen來將其成員變量mScreenAcquired的值設(shè)置為0,表示它目前不可以訪問顯示屏??刂婆_事件監(jiān)控線程接下來就會等待顯示屏被喚醒過來。一旦顯示屏被喚醒過來,那么該線程就會通過DisplayEventThreadBase類的成員變量mFlinger來通知SurfaceFlinger服務。SurfaceFlinger服務得到這個通知之后,就會調(diào)用DisplayHardwareBase類的成員函數(shù)acquireScreen來將其成員變量mScreenAcquired的值設(shè)置為1,表示它目前可以訪問顯示屏。在下一篇文章分析SurfaceFlinger服務的線程模型時,我們再詳細分析這個過程。
?? ? ? ?DisplayHardwareBase類另一方面用來控制SurfaceFlinger服務當前是否能夠在顯示屏上渲染UI。當系統(tǒng)的其它組件請求SurfaceFlinger服務關(guān)閉顯示屏時,SurfaceFlinger服務就會調(diào)用DisplayHardwareBase類的成員函數(shù)setCanDraw來將其成員變量mCanDraw的值設(shè)置為0;而當系統(tǒng)的其它組件請求SurfaceFlinger服務打開顯示屏時,SurfaceFlinger服務就會調(diào)用DisplayHardwareBase類的成員函數(shù)setCanDraw來將其成員變量mCanDraw的值設(shè)置為1。只有當DisplayHardwareBase類的成員變量mScreenAcquired和mCanDraw的值均等于1時,SurfaceFlinger服務才可以在顯示屏上渲染系統(tǒng)的UI。為了方便SurfaceFlinger服務判斷它當前是否可以在顯示屏上渲染系統(tǒng)的UI,DisplayHardwareBase類提供了另外一個成員函數(shù)canDraw。當DisplayHardwareBase類的成員函數(shù)canDraw的返回值等于true時,就表示SurfaceFlinger服務可以在顯示屏上渲染系統(tǒng)的UI,否則就不可以。
?? ? ? 了解了DisplayHardwareBase類的作用之后,接下來我們就從它的構(gòu)造函數(shù)開始分析它的初始化過程。
?? ? ? ?DisplayHardwareBase類的構(gòu)造函數(shù)的實現(xiàn)如下所示:
?
[cpp]?view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp中。
?
?? ? ? ?函數(shù)首先創(chuàng)建一個類型為DisplayEventThread的線程。如果這個線程能夠通過初始化檢查,即DisplayEventThread類的成員函數(shù)initCheck的返回值等于NO_ERROR,那么SurfaceFlinger服務就會使用這個類型為DisplayEventThread的線程來監(jiān)控顯示屏的睡眠/喚醒狀態(tài)切換事件,否則的話,函數(shù)接下來就會創(chuàng)建另外一個類型為ConsoleManagerThread的線程來監(jiān)控顯示屏的睡眠/喚醒狀態(tài)切換事件。
?? ? ? ?DisplayEventThread類的成員函數(shù)initCheck的實現(xiàn)如下所示:
?
[cpp]?view plaincopy?? ? ? 這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp中。
?
?? ? ??kSleepFileName、kWakeFileName、kOldSleepFileName、kOldWakeFileName和kFbconSysDir是五個字符串常量,它們的定義如下所示:
?
[cpp]?view plaincopy?? ? ??當硬件幀緩沖區(qū)的控制臺被打開時,幀緩沖區(qū)驅(qū)動程序就創(chuàng)建一個/sys/class/graphics/fbcon目錄,以及創(chuàng)建/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文件,用來通知用戶空間顯示屏即將要進入睡眠/喚醒狀態(tài)了。
?
?? ? ? 回到DisplayHardwareBase類的構(gòu)造函數(shù)中,最終創(chuàng)建出來的線程對象保存在其成員變量mDisplayEventThread中。DisplayHardwareBase類的mDisplayEventThread是一個類型為DisplayEventThreadBase的強指針,從前面Android系統(tǒng)的智能指針(輕量級指針、強指針和弱指針)的實現(xiàn)原理分析一文可以知道,當一個強指針第一次引用一個對象的時候,這個對象的成員函數(shù)onFirstRef就會被調(diào)用,因此,接下來我們就繼續(xù)分析DisplayEventThreadBase類的成員函數(shù)onFirstRef的實現(xiàn),看看它在里面做了一件什么事情。
?? ? ??DisplayEventThreadBase類的成員函數(shù)onFirstRef的實現(xiàn)如下所示:
?
[cpp]?view plaincopy?? ? ??這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h中。
?
?? ? ??DisplayEventThreadBase類的成員函數(shù)onFirstRef主要就調(diào)用父類Thread的成員函數(shù)run來創(chuàng)建一個名稱為“DisplayEventThread”的線程,用來監(jiān)控顯示屏的睡眠/喚醒狀態(tài)切換事件。這個線程在創(chuàng)建完成之后,首先會調(diào)用DisplayEventThread類的成員函數(shù)readyToRun來執(zhí)行一些初始化操作,接下來不斷地循環(huán)調(diào)用DisplayEventThread類的成員函數(shù)threadLoop來監(jiān)控顯示屏的睡眠/喚醒狀態(tài)切換事件。接下來,我們就主要分析這個線程的初始化操作,即DisplayEventThread類的成員函數(shù)readyToRun的實現(xiàn),在接下來的一篇文章中分析SurfaceFlinger服務的線程模型時,再詳細分析這個線程監(jiān)控顯示屏的睡眠/喚醒狀態(tài)切換事件的過程,即DisplayEventThread類的成員函數(shù)threadLoop的實現(xiàn)。
?? ? ?DisplayEventThread類的成員函數(shù)readyToRun的實現(xiàn)如下所示:
?
[cpp]?view plaincopy?? ? ??這個函數(shù)定義在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp中。
?
?? ? ??DisplayEventThread類的成員函數(shù)readyToRun的實現(xiàn)很簡單,它首先判斷/sys/power/wait_for_fb_sleep和/sys/power/wait_for_fb_wake文件是否存在。如果存在的話,那么就通過它們來監(jiān)控顯示屏的睡眠/喚醒狀態(tài)切換事件,否則的話,就通過/sys/android_power/wait_for_fb_sleep和/sys/android_power/wait_for_fb_wake文件來監(jiān)控顯示屏的睡眠/喚醒狀態(tài)切換事件。如果這四個文件都不存在,那么就說明硬件幀緩沖區(qū)的控制臺沒有被打開了,這時候就不能使用類型為DisplayEventThread的線程來監(jiān)控顯示屏的睡眠/喚醒狀態(tài)切換事件。
?? ? ? 至此,我們就分析完成DisplayHardwareBase類的實現(xiàn)了,接下來我們繼續(xù)分析FramebufferNativeWindow類的實現(xiàn),以便可以了解它是如何管理硬件幀緩沖區(qū)的。
?? ? ??在分析FramebufferNativeWindow類的實現(xiàn)之前,我們首先看看它的類關(guān)系圖,如圖3所示。
圖3?FramebufferNativeWindow類關(guān)系
?? ? ? ?前面提到,FramebufferNativeWindow類與在前面Android應用程序請求SurfaceFlinger服務創(chuàng)建Surface的過程分析一文中提到的Surface類的作用是類似的。FramebufferNativeWindow類一方面用來在OpenGL庫和Android本地窗口系統(tǒng)之間建立連接,這樣,我們就可以使用它的成員函數(shù)dequeueBuffer來為OpenGL庫分配空閑圖形緩沖區(qū),以及使用它的成員函數(shù)queueBuffer來將OpenGL已經(jīng)填充好UI數(shù)據(jù)的圖形緩沖區(qū)渲染到硬件幀緩沖區(qū)中去。FramebufferNativeWindow類另一方面還繼承了LightRefBase類,因此,從前面Android系統(tǒng)的智能指針(輕量級指針、強指針和弱指針)的實現(xiàn)原理分析一文可以知道,FramebufferNativeWindow類對象可以結(jié)合Android系統(tǒng)的輕量級指針sp來使用,以便可以自動維護生命周期。
?? ? ? ?FramebufferNativeWindow類與Surface類又有不同的地方。從前面Android應用程序請求SurfaceFlinger服務創(chuàng)建Surface的過程分析一文可以知道,Surface類使用的圖形緩沖區(qū)一般是在匿名共享內(nèi)存中分配的,并且是由SurfaceFlinger服務來負責分配,然后再傳遞給應用程序進程使用的,而FramebufferNativeWindow類使用的圖形緩沖區(qū)是直接在硬件幀緩沖區(qū)分配的,并且它可以直接將這些圖形緩沖區(qū)渲染到硬件幀緩沖區(qū)中去。從前面Android幀緩沖區(qū)(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現(xiàn)原理分析一文可以知道,要從硬件幀緩沖區(qū)中分配和渲染圖形緩沖區(qū),就必須要將HAL層中的Gralloc模塊加載到當前的進程空間來,并且打開里面的gralloc設(shè)備和fb設(shè)備,其中,gralloc設(shè)備用來分配圖形緩沖區(qū),而fb設(shè)備用來渲染圖形緩沖區(qū)。因此,FramebufferNativeWindow類包含了一個類型的alloc_device_t*的成員變量grDev和一個類型為framebuffer_device_t*的成員變量fbDev,它們分別指向HAL層中的Gralloc模塊的gralloc設(shè)備和fb設(shè)備。
?? ? ??FramebufferNativeWindow類在內(nèi)部還包含了一個類型為sp<NativeBuffer>的數(shù)組buffers,用來描述OpenGL庫可以使用的圖形緩沖區(qū),數(shù)組的大小等于NUM_FRAME_BUFFERS,即等于硬件幀緩沖區(qū)能夠提供的圖形緩沖區(qū)的個數(shù)。例如,在Android 2.3系統(tǒng)中,硬件幀緩沖區(qū)能夠提供的圖形緩沖區(qū)的個數(shù)等于2,這意味著Android系統(tǒng)可以使用雙緩沖區(qū)技術(shù)來渲染系統(tǒng)的UI。由于OpenGL庫所使用的圖形緩沖區(qū)必須要實現(xiàn)android_native_buffer_t接口,因此,NativeBuffer類繼承了android_native_buffer_t類。此外,NativeBuffer類還繼承了LightRefBase類,因此,它的對象就和FramebufferNativeWindow類對象一樣,可以結(jié)合Android系統(tǒng)的輕量級指針sp來使用,以便可以自動維護生命周期。
?? ? ??了解了FramebufferNativeWindow類的作用之后,接下來我們就從它的構(gòu)造函數(shù)開始分析它的實現(xiàn),即分析它的類對象的創(chuàng)建過程。從前面DisplayHardware類的成員函數(shù)init的實現(xiàn)可以知道,FramebufferNativeWindow對象是在DisplayHardware對象初始化的過程中創(chuàng)建的,并且包含在DisplayHardware對象內(nèi)部中,用來管理硬件幀緩沖區(qū)。
?? ? ? ?FramebufferNativeWindow類的構(gòu)造函數(shù)定義在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中,它的實現(xiàn)比較長,我們分段來閱讀:
?
[cpp]?view plaincopy?? ? ? ?這段代碼首先調(diào)用函數(shù)hw_get_module來將HAL層中的Gralloc模塊加載到當前進程來,并且調(diào)用函數(shù)framebuffer_open和gralloc_open分別打開Gralloc模塊中的fb設(shè)備和gralloc設(shè)備。這三個過程的實現(xiàn)可以參考前面Android幀緩沖區(qū)(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現(xiàn)原理分析一文。Gralloc模塊中的fb設(shè)備和gralloc設(shè)備打開之后,就分別保存在FramebufferNativeWindow類的成員變量fbDev和grDev中。
?
?? ? ? ?這段代碼接著判斷硬件幀緩沖區(qū)是否支持部區(qū)更新UI。如果支持的話,那么從Gralloc模塊中打開的fb設(shè)備的成員函數(shù)setUpdateRect就不等于0。這時候這段代碼就會FramebufferNativeWindow類的成員變量mUpdateOnDemand的值設(shè)置為true。
?? ? ? ?這段代碼最后還會將FramebufferNativeWindow類的成員變量mNumBuffers和mNumFreeBuffers的值均設(shè)置為NUM_FRAME_BUFFERS,它們分別用來描述硬件幀緩沖區(qū)可以提供的圖形緩沖區(qū)的個數(shù),以及當前可用的空閑圖形緩沖區(qū)的個數(shù)。此外,FramebufferNativeWindow類的成員變量mBufferHead的值還會被設(shè)置為(NUM_FRAME_BUFFERS - 1),表示下一個可用的空閑圖形緩沖區(qū)在FramebufferNativeWindow類的成員變量buffers所描述的一個圖形緩沖區(qū)數(shù)組的位置。
?? ? ? ? 我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼用來創(chuàng)建FramebufferNativeWindow類的成員變量buffers所描述的一個圖形緩沖區(qū)數(shù)組。每一個圖形緩沖區(qū)都使用一個NativeBuffer對象來描述,并且這些圖形緩沖區(qū)都是通過調(diào)用HAL層中的Gralloc模塊的gralloc設(shè)備的成員函數(shù)alloc來分配的。注意,在分配圖形緩沖區(qū)時,指定的標志,即第5個參數(shù)的值為GRALLOC_USAGE_HW_FB。這意味著FramebufferNativeWindow類所管理的圖形緩沖區(qū)都是直接在硬件幀緩沖區(qū)上分配的,而不是在匿名共享內(nèi)存中分配的。
?
?? ? ? ?我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼主要就是用來設(shè)置FramebufferNativeWindow的父類ANativeWindow的成員變量flags、xdpi、ydpi、minSwapInternal和maxSwapInterval的值,以便OpenGL庫可以知道系統(tǒng)當前所使用的硬件幀緩沖區(qū)的一些屬性,例如,點密度、緩沖區(qū)數(shù)據(jù)交換時間間隔等信息。這些成員變量的具體含義可以參考前面Android應用程序請求SurfaceFlinger服務創(chuàng)建Surface的過程分析一文所提到的Surface類的初始化過程。
?
?? ? ? ?我們接著往下閱讀代碼:
?
[cpp]?view plaincopy?? ? ? ?這段代碼用來設(shè)置FramebufferNativeWindow的父類ANativeWindow的成員函數(shù)setSwapInterval、dequeueBuffer、lockBuffer、queueBuffer、query和perform,它們都是OpenGL的回調(diào)接口,分別指向FramebufferNativeWindow類的靜態(tài)成員函數(shù)setSwapInterval、dequeueBuffer、lockBuffer、queueBuffer、query和perform。與前面Android應用程序請求SurfaceFlinger服務創(chuàng)建Surface的過程分析一文所提到的Surface類一樣,我們只關(guān)注FramebufferNativeWindow類的靜態(tài)成員函數(shù)dequeueBuffer和queueBuffer的實現(xiàn),因為它們負責用來為OpenGL庫分配和渲染圖形緩沖區(qū)。
?
?? ? ? ?FramebufferNativeWindow類的成員函數(shù)dequeueBuffer的實現(xiàn)如下所示:
?
[cpp]?view plaincopy?? ? ? 這個函數(shù)定義在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中。
?
?? ? ? 參數(shù)window雖然是一個類型為ANativeWindow的指針,但是它指向的實際上是一個FramebufferNativeWindow對象,這個FramebufferNativeWindow對象是在DisplayHardware類的成員函數(shù)init中創(chuàng)建的,因此,函數(shù)在開始的地方就可以將它轉(zhuǎn)換一個FramebufferNativeWindow對象self。
?? ? ? 有了FramebufferNativeWindow對象self之后,我們就可以在它內(nèi)部的圖形緩沖區(qū)數(shù)組buffers中獲取下一個空閑圖形緩沖區(qū)。前面提到,下一個空閑圖形緩沖區(qū)的在數(shù)組buffer中的位置就保存在FramebufferNativeWindow對象self的成員變量mBufferHead中。因此,函數(shù)就可以將FramebufferNativeWindow對象self的成員變量mBufferHead的值取出來保存在變量index中,以便接下來可以從FramebufferNativeWindow對象self內(nèi)部的圖形緩沖區(qū)數(shù)組buffers中取出一個圖形緩沖區(qū)。此外,函數(shù)還需要將FramebufferNativeWindow對象self的成員變量mBufferHead增加1,以便它可以指向下一個空閑的圖形緩沖區(qū)。注意,FramebufferNativeWindow對象self內(nèi)部的圖形緩沖區(qū)數(shù)組buffers是循環(huán)使用的,因此,在將它的成員變量mBufferHead增加1之后,要判斷它的值是否已經(jīng)大于等于數(shù)組的大小,如果大于等于的話,就需要將它的值設(shè)置為0,即繞回到前面去。
?? ? ? 從前面的分析可以知道,FramebufferNativeWindow對象self內(nèi)部可用的空閑圖形緩沖區(qū)的個數(shù)保存在其成員變量mNumFreeBuffers中,因此,當這個成員變量的值等于0的時候,就表示FramebufferNativeWindow對象self沒有空閑的空閑圖形緩沖區(qū)可用,這時候當前線程就會通過FramebufferNativeWindow對象self的成員變量mCondition所描述的一個條件變量進入到睡眠等待狀態(tài),直到有可用的空閑圖形緩沖區(qū)為止。什么時候FramebufferNativeWindow對象self內(nèi)部才會有可用的空閑圖形緩沖區(qū)呢?當OpenGL庫請求FramebufferNativeWindow對象self將一個圖形緩沖區(qū)的內(nèi)容渲染到硬件幀緩沖區(qū)之后,FramebufferNativeWindow對象self就會獲得一個可用的空閑圖形緩沖區(qū)了,后面我們分析FramebufferNativeWindow類的成員函數(shù)queueBuffer的實現(xiàn)時就會看到這個邏輯。
?? ? ? 一旦FramebufferNativeWindow對象self內(nèi)部有可用的空閑圖形緩沖區(qū),那么函數(shù)就會將這個空閑圖形緩沖區(qū)就會返回給OpenGL庫,即保存在輸出參數(shù)buffer,并且將FramebufferNativeWindow對象self內(nèi)部可用的空閑圖形緩沖區(qū)的個數(shù)減1,以及將OpenGL庫當前正前正在使用的圖形緩沖區(qū)在數(shù)組buffers中的位置保存在FramebufferNativeWindow對象self的成員變量mCurrentBufferIndex中。
?? ? ? 至此,我們就分析完成FramebufferNativeWindow類的成員函數(shù)dequeueBuffer的實現(xiàn)了,接下來我們繼續(xù)分析FramebufferNativeWindow類的成員函數(shù)queueBuffer的實現(xiàn),如下所示:
?
[cpp]?view plaincopy?? ? ? ?這個函數(shù)定義在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中。
?
?? ? ? ?參數(shù)window指向的實際上也是一個FramebufferNativeWindow對象,這個FramebufferNativeWindow對象是在DisplayHardware類的成員函數(shù)init中創(chuàng)建的,因此,函數(shù)在開始的地方同樣是先將它轉(zhuǎn)換一個FramebufferNativeWindow對象self。
?? ? ? ?參數(shù)buffer指向的是一個實際類型為NativeBuffer的圖形緩沖區(qū),這個圖形緩沖區(qū)是在FramebufferNativeWindow類的成員函數(shù)dequeueBuffer中分配的,如前所述。
?? ? ? ?FramebufferNativeWindow類的成員函數(shù)queueBuffer目標就是要將參數(shù)buffer所描述的圖形緩沖區(qū)渲染到硬件幀緩沖區(qū)中去,因此,我們就需要獲得FramebufferNativeWindow對象self的成員變量fbDev所描述的一個fb設(shè)備。有了這個fb設(shè)備之后, 我們就可以調(diào)用它的成員函數(shù)post來將參數(shù)buffer所描述的圖形緩沖區(qū)渲染到硬件幀緩沖區(qū)中去,這個過程可以參考Android幀緩沖區(qū)(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現(xiàn)原理分析一文。
?? ? ? 參數(shù)buffer所描述的圖形緩沖區(qū)被渲染到硬件幀緩沖區(qū)中去之后,它就變成一個空閑的圖形緩沖區(qū)了,因此,我們就需要將它返回給FramebufferNativeWindow對象self內(nèi)部的圖形緩沖區(qū)數(shù)組buffers中去,并且將可用的空閑圖形緩沖區(qū)的個數(shù)增加1,最后通過FramebufferNativeWindow對象self的成員變量mCondition所描述的一個條件變量將前面正在等待從FramebufferNativeWindow對象self內(nèi)部分配空閑圖形緩的線程喚醒。
?? ? ? 至此,FramebufferNativeWindow類的成員函數(shù)queueBuffer的實現(xiàn)就分析完成了,FramebufferNativeWindow類的實現(xiàn)也分析完成了。通過GraphicPlane、DisplayHardware和FramebufferNativeWindow這三個類的實現(xiàn),我們就可以知道SurfaceFlinger服務是如何管理系統(tǒng)的顯示屏,即系統(tǒng)的硬件幀緩沖區(qū)的了。
?? ? ? 從SurfaceFlinger服務創(chuàng)建一個DisplayHardwareBase對象來管理系統(tǒng)的顯示屏的過程可以知道,這個DisplayHardwareBase對象會創(chuàng)建一個控制臺事件監(jiān)控線程來監(jiān)控硬件幀緩沖區(qū)的睡眠/喚醒狀態(tài)切換事件,而從前面Android系統(tǒng)Surface制的SurfaceFlinger服務的啟動過程分析一文又可以知道,System進程在啟動SurfaceFlinger服務過程中,又會創(chuàng)建一個Binder線程池,以及為SurfaceFlinger服務創(chuàng)建一個UI渲染線程,這樣在SurfaceFlinger服務中,就存在三種不同類型的線程,在接下來的一篇文章中,我們就將分析詳細SurfaceFlinger服務的線程模型,以便最后我們就可以更好地分析SurfaceFlinger服務的實現(xiàn),敬請關(guān)注!
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注!
轉(zhuǎn)載于:https://www.cnblogs.com/mfryf/archive/2013/05/22/3092063.html
總結(jié)
以上是生活随笔為你收集整理的Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server2008附加数据库之
- 下一篇: android sina oauth2.