日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析

發布時間:2025/5/22 Android 78 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? ?在前文中,我們分析了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來實現的,如下所示:

    ?

    [cpp]?view plaincopy
  • 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]?view plaincopy
  • 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對象,如下所示:

    ?

    [cpp]?view plaincopy
  • const?DisplayHardware&?GraphicPlane::displayHardware()?const?{??
  • ????return?*mHw;??
  • }??
  • ?? ? ?這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。

    ?

    ?? ? ?接下來,回到前面SurfaceFlinger類的成員函數readyToRun中,我們通過DisplayHardware對象的創建過程來分析DisplayHardware類的實現。

    ?? ? ?在創建DisplayHardware對象的過程中,會調用DisplayHardware類的構造函數,如下所示:

    ?

    [cpp]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • //?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]?view plaincopy
  • 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]?view plaincopy
  • /*?
  • ?*?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時,都要將后端圖形緩沖區的內容拷貝回那些不在那些需要更新的區域中去,這會導致性能低下。因此,系統一般都不支持不規則區域部分更新功能。

    ?? ? ? ?我們接著往下閱讀代碼:

    ?

    [cpp]?view plaincopy
  • /*?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]?view plaincopy
  • ????/*?
  • ?????*?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]?view plaincopy
  • ????/*?
  • ?????*?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擴展屬性以及部分更新屬性,那么將會優先使用部分更新屬性,因為后者是直接在硬件上支持部分更新,因而性能會更好。

    ?

    ?? ? ? ?我們接著往下閱讀最后一段代碼:

    ?

    [cpp]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • 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類的構造函數的實現如下所示:

    ?

    [cpp]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • 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]?view plaincopy
  • ????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]?view plaincopy
  • 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]?view plaincopy
  • 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,歡迎關注!

    轉載于:https://www.cnblogs.com/mfryf/archive/2013/05/22/3092063.html

    總結

    以上是生活随笔為你收集整理的Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    成人免费观看视频网站 | 亚洲欧美在线综合 | 二区三区中文字幕 | 一级淫片a | 亚洲a色 | 国产精品成人自产拍在线观看 | 99国产精品视频免费观看一公开 | 毛片一区二区 | 九九一级片 | 国产精品久久久久av | 国产一级在线视频 | 中文字幕在线色 | 深爱激情开心 | 丁香六月久久综合狠狠色 | 三级黄色网络 | 日韩免费不卡视频 | 日韩三级.com | 伊人永久 | 免费看特级毛片 | 久久精品一区二区 | 亚洲国产精品影院 | 好看的国产精品视频 | 最近中文字幕在线播放 | 99精品福利 | 91精品国产麻豆 | 亚洲 欧洲av | 五月天久久 | 久久爽久久爽久久av东京爽 | 欧美成年黄网站色视频 | 久久精品第一页 | 夜夜爽88888免费视频4848 | 天天干天天操天天干 | 欧美一级免费黄色片 | 久久精品韩国 | 综合网久久 | 成人在线视频网 | 91在线www | 亚洲婷婷在线 | 99热.com| 日批视频在线观看免费 | 少妇搡bbbb搡bbb搡忠贞 | 精品在线观看视频 | 色多多在线观看 | 日韩大片在线看 | 国产精品一区在线播放 | 亚洲美女精品视频 | 五月丁色 | 午夜精品久久久久久久99热影院 | 亚洲无人区小视频 | 成人国产一区二区 | 人人爽人人爱 | 亚洲精品视频在线免费播放 | 久久精品美女 | 免费看片色| 日产av在线播放 | 精品一区二区精品 | 香蕉久久久久 | 久久久久国产a免费观看rela | 国产精品免费在线观看视频 | 午夜手机电影 | 国产日韩三级 | 国产韩国日本高清视频 | 国产日韩精品在线观看 | 久久尤物电影视频在线观看 | 一区二区三区免费播放 | 超碰最新网址 | 中文字幕在线资源 | 国产人成免费视频 | 天天操天天操天天操 | 国产免费观看高清完整版 | 在线观看av中文字幕 | 97在线视频免费看 | 九九热免费在线观看 | 九九久久久久久久久激情 | www,黄视频 | 日本精品久久久一区二区三区 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 黄色片网站av | 欧美在线1区 | 欧美日韩不卡在线观看 | 久久精品99久久 | 欧美另类69 | 2024av在线播放| 九九热精品视频在线播放 | 九九精品在线观看 | 亚洲狠狠丁香婷婷综合久久久 | 国产精品av免费观看 | 国产三级午夜理伦三级 | 日韩免费观看高清 | 99久久精品免费一区 | 2019国产精品| 天天爱综合 | 久久精品欧美一区 | 蜜臀av性久久久久蜜臀av | 欧美激情精品久久久久久 | 久久躁日日躁aaaaxxxx | 91.精品高清在线观看 | 一区三区视频 | 亚洲在线视频播放 | 久久国产麻豆 | 亚洲精品国产精品久久99热 | 亚州国产精品 | 欧美国产日韩激情 | av在线网站大全 | 国产精品小视频网站 | www.国产视频 | 麻豆视频在线免费看 | 91福利视频免费 | 天天色成人网 | 五月婷婷伊人网 | 91精品伦理 | 成人18视频| 国产精品白丝jk白祙 | 久久久久高清毛片一级 | 久久字幕网 | 国产超碰在线 | 91精品久久久久久综合五月天 | 国产精品一区二区麻豆 | 91麻豆精品国产91久久久无需广告 | 久草在线中文视频 | 亚洲精品ww | 欧美一级免费在线 | 成人av网址大全 | 日韩精品视频免费 | 人人爱爱 | 日本最新一区二区三区 | 成人在线观看免费视频 | 成人一区二区在线观看 | 99热九九这里只有精品10 | 久久婷婷色综合 | 成人高清在线观看 | 国产永久免费高清在线观看视频 | 国产手机视频精品 | 日韩在线观看中文 | 精品美女在线观看 | 很黄很污的视频网站 | 中文字幕精品三区 | 亚洲女同videos | 美女黄频在线观看 | 成人性生交视频 | 日韩av电影中文字幕在线观看 | 在线观看视频国产一区 | 国产精品一区二区三区观看 | www免费看片com | 丁香六月在线观看 | 国产色影院 | 在线观看成人 | 最新不卡av | a天堂中文在线 | 午夜精品一区二区三区在线 | 波多野结衣小视频 | 丁香影院在线 | 久久精品系列 | av免费在线网站 | 欧美日韩亚洲国产一区 | 欧美日韩在线免费视频 | 天天综合色天天综合 | 国产群p | 国产视频2 | 日韩伦理片一区二区三区 | 中国美女一级看片 | 久草资源免费 | 丁香五婷| 玖玖视频 | 亚洲影视九九影院在线观看 | 国产97碰免费视频 | 黄色福利网 | 在线视频手机国产 | 国产婷婷一区二区 | 亚洲午夜精品一区二区三区电影院 | 成人小视频免费在线观看 | 欧美日韩中文视频 | 九色视频自拍 | 色综合久久五月天 | 国产精品久久二区 | 最近中文字幕免费观看 | 日韩精品欧美视频 | 99视频偷窥在线精品国自产拍 | 欧美大片在线观看一区 | 国产一级在线免费观看 | 天天操网址 | 97国产小视频 | 91手机视频在线 | 久久精品亚洲一区二区三区观看模式 | 在线中文字幕一区二区 | 成人网在线免费视频 | 亚洲精选视频免费看 | 九九九免费视频 | 国产高清一区二区 | 亚洲区精品 | 欧美午夜理伦三级在线观看 | 国产视频1 | 国产精品久久久久久久久搜平片 | 中文资源在线观看 | 九九久久免费 | 国产视频精品视频 | 久久亚洲欧美日韩精品专区 | www黄com| 91成人天堂久久成人 | 国产麻豆剧传媒免费观看 | 欧美日韩一区二区三区在线观看视频 | 国产一区网址 | 丁香久久综合 | 成人免费视频播放 | 天天爱天天操 | 日韩精品视频在线观看网址 | 99爱视频 | 国产女教师精品久久av | 精品久久一区二区三区 | www国产一区 | 亚洲一级黄色大片 | 一级成人网 | 国产成人三级 | 久久久精品国产免费观看一区二区 | 国产在线更新 | 中文字幕丝袜一区二区 | 91在线免费观看国产 | 国产免费一区二区三区网站免费 | 人人舔人人| 人人爽人人av | 毛片99| 久久成人黄色 | 蜜臀av性久久久久蜜臀aⅴ四虎 | 午夜视频一区二区三区 | 欧美一二三专区 | 特级黄色电影 | 精品在线视频播放 | 免费视频国产 | 色婷婷激婷婷情综天天 | 999男人的天堂 | 久草在线资源免费 | 91桃色视频 | 中文字幕在线免费看线人 | 六月天综合网 | 日韩在线第一 | 伊人五月天综合 | 国产97在线观看 | 久久热首页 | 天堂av网址| 在线 日韩 av | 欧美精品免费在线 | 日韩小视频网站 | 国产精品一区二区在线免费观看 | 欧美久久成人 | 最近日本韩国中文字幕 | av大全在线播放 | 视频国产精品 | 国产精品系列在线播放 | 国产高清在线免费观看 | 国产手机av | 九九综合久久 | www.操.com| 四虎影视久久久 | 黄色h在线观看 | 免费麻豆| 日韩一二三在线 | 天天干天天操天天爱 | 国产精品热视频 | 精品国产一区二区三区在线 | 成人在线视频免费 | 人人爱人人做人人爽 | 91av在线播放视频 | 久久视频网 | 欧美久久久久久久久久久久久 | 色欧美成人精品a∨在线观看 | 操操综合| 国产午夜在线 | 在线国产日本 | 国产亚洲精品久久久久久久久久 | 五月香婷 | 国产99久久久国产精品免费二区 | 亚洲综合在线观看视频 | 波多野结衣精品在线 | av一二三区 | 九九热精品在线 | 久久欧美视频 | 免费视频你懂得 | 午夜a区| 免费a一级 | 久久久久久久久久影视 | 午夜av一区二区三区 | 五月婷婷欧美 | 午夜精品久久久久久久久久久 | 四月婷婷在线观看 | www.888.av| 天天爽天天爽夜夜爽 | 国产精品18久久久久久久久 | 国内视频在线 | 免费在线观看一区 | 成人免费影院 | 亚洲一区日韩精品 | 美女国内精品自产拍在线播放 | 97av视频在线 | 久久久久成人精品亚洲国产 | 麻豆手机在线 | 国产精品久久在线 | 天天操天天干天天爱 | 一区二区三区免费网站 | 麻豆91在线观看 | 亚洲国产精品女人久久久 | 天天插伊人 | 婷婷久久综合九色综合 | 天天摸天天干天天操天天射 | 亚洲精品国产麻豆 | 狠狠操狠狠操 | 久久综合电影 | 日本黄色一级电影 | 午夜精品视频一区二区三区在线看 | 日韩精品久久久免费观看夜色 | 久久久人人爽 | 91看成人 | 丁香六月欧美 | 日韩av不卡播放 | 久久久蜜桃一区二区 | 国产麻豆果冻传媒在线观看 | 亚洲精品乱码白浆高清久久久久久 | 欧美日韩中文字幕在线视频 | 91av视频在线免费观看 | 国产一区二区视频在线播放 | 涩涩网站在线 | 国产一区二区三精品久久久无广告 | 在线免费国产 | 91九色porny蝌蚪视频 | 精品国产一区二区在线 | 国产xxxx性hd极品 | 最近免费中文字幕 | 波多野结衣视频一区二区三区 | 视频一区二区在线 | 91探花国产综合在线精品 | 亚洲欧洲中文日韩久久av乱码 | 欧美黑人性爽 | 天天av综合网 | 97视频在线观看视频免费视频 | 国产黄色在线网站 | 日韩免费三级 | 91手机视频在线 | 亚洲国产中文字幕在线视频综合 | 欧美另类交人妖 | 深夜福利视频一区二区 | 黄色一区二区在线观看 | 久久9999久久 | 免费黄色在线网址 | 天天射天天干 | 丰满少妇麻豆av | 99视频 | 欧美国产一区在线 | 97在线视频免费 | 毛片永久免费 | 婷婷丁香av | 91 在线视频 | 亚洲国产精品一区二区久久hs | 欧美一级久久 | 免费成人黄色片 | av网站播放 | 色婷婷综合久久久久中文字幕1 | av青草| 成年人精品 | 亚洲精品电影在线 | 天天天综合 | 中文字幕精品久久 | 天天综合天天做天天综合 | av看片在线 | 色综合婷婷 | 中文字幕色在线 | 91精品办公室少妇高潮对白 | 久久人人爽人人爽人人 | 亚洲精品一区二区三区四区高清 | 日韩激情网 | a极黄色片| 男女视频久久久 | 毛片.com | 精品国产不卡 | 中文字幕在线精品 | 久久99精品国产一区二区三区 | 99久久综合精品五月天 | www.天天干.com | 中文字幕乱偷在线 | 黄网站色欧美视频 | 99久久影院 | 久草免费看 | 超碰97在线资源站 | 精品在线视频观看 | 久久精品国产精品亚洲 | 在线免费观看视频一区 | 99在线热播精品免费99热 | av资源免费看 | 久久伊人精品天天 | 自拍超碰在线 | 国产视频在线观看一区 | 国产精华国产精品 | 亚洲精品乱码久久久久久蜜桃动漫 | japanesefreesex中国少妇 | 911亚洲精品第一 | 日韩电影久久 | 国产第一二区 | 日韩av在线免费看 | 黄色av电影 | zzijzzij亚洲日本少妇熟睡 | 亚洲永久在线 | 国产这里只有精品 | 欧美久久久久久久久 | 一区二区精品国产 | 色播五月激情五月 | 91热这里只有精品 | 综合精品久久 | 色偷偷中文字幕 | 久草久草视频 | 91亚洲国产 | 天天色天天骑天天射 | 六月丁香色婷婷 | 中文字幕色在线 | 亚洲精品国产自产拍在线观看 | 国产日本在线观看 | 色婷婷狠 | 99免费在线视频观看 | 日韩欧美第二页 | 成人国产精品电影 | 成人免费在线观看av | 天天天色综合 | 亚洲成人一二三 | 免费在线观看国产黄 | 免费看av在线 | 韩国一区二区三区视频 | 在线免费av播放 | 精品自拍av | 99久久精品无码一区二区毛片 | 中文字幕一区二区三区四区 | 亚洲午夜精品一区 | 日韩欧美一区二区三区免费观看 | 四虎国产精品成人免费4hu | 91色网址 | 成人午夜网址 | 在线视频你懂得 | 天天天操操操 | 国产在线色站 | 日韩精品免费在线观看 | 免费观看性生活大片3 | 免费h精品视频在线播放 | www黄在线| 超碰97在线资源站 | 久久久久高清毛片一级 | 9在线观看免费高清完整 | 国产精品久久99精品毛片三a | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 久久久夜色 | 国产二区电影 | 在线观看中文字幕网站 | www操操| 国产麻豆电影在线观看 | 成年人国产在线观看 | 丁香婷婷色综合亚洲电影 | av视屏在线播放 | 久久伊人免费视频 | 91人人在线| 伊人视频 | 奇人奇案qvod| 成人网页在线免费观看 | 欧美亚洲一区二区在线 | 丁香婷婷久久 | 国产精品女人网站 | 91视频在线网址 | 久久久影片 | 久久久久成人免费 | 91干干干 | 91精品国产麻豆国产自产影视 | 91av视频在线观看 | 中午字幕在线观看 | 婷婷综合久久 | 国产99久久精品一区二区永久免费 | 成人a在线 | 久久精品三| 久久久精品网站 | 草久久影院| 亚洲天堂视频在线 | 精品一二三四视频 | 亚洲精品国产精品国自产观看浪潮 | 色婷婷国产精品一区在线观看 | 国产一区av在线 | 国产午夜精品理论片在线 | 国产福利不卡视频 | www.香蕉视频| 7777精品伊人久久久大香线蕉 | 免费在线观看av | 久久好看免费视频 | 啪啪小视频网站 | 久一在线| 成人四虎影院 | 日本中文字幕在线看 | 久久激五月天综合精品 | 亚洲 中文 在线 精品 | 中文字幕网址 | 狠狠地日 | 国产精品美女999 | 欧美成人精品在线 | 一级片色播影院 | 国产成人精品福利 | 毛片99| 国产爽视频 | 日韩18p| 成年人免费观看在线视频 | 天堂va在线观看 | 2024国产精品视频 | 国产精品乱码久久久 | 在线观看福利网站 | 麻豆视传媒官网免费观看 | 色噜噜狠狠狠狠色综合久不 | 69av视频在线| 免费视频 三区 | 久久精品视频在线 | 97视频免费在线观看 | 天天射射天天 | 久久久精品二区 | 国产99久久九九精品 | 色av男人的天堂免费在线 | 欧美成年网站 | 久久久久久黄 | 国产精品一区二区av影院萌芽 | 国产高清在线免费观看 | 国产午夜精品一区二区三区嫩草 | 四虎永久网站 | 国产精品1区2区 | 99久久国产免费免费 | 超碰97免费 | 久久久久久久久久久黄色 | 中文字幕 影院 | av三级av | 中文字幕中文字幕在线一区 | 人人澡人人爱 | 99久久这里有精品 | 91丨九色丨高潮丰满 | 最近2019好看的中文字幕免费 | 成人黄色片在线播放 | 97成人资源| v片在线看 | 天天射天天色天天干 | 国产一区二区三区视频在线 | 色综合久久88色综合天天 | 黄色在线免费观看网址 | 97电影院在线观看 | 日韩特黄av| 黄色不卡av| www.狠狠 | adn—256中文在线观看 | 国精产品满18岁在线 | 在线观看a视频 | 黄色免费电影网站 | 亚洲视频播放 | 四虎成人在线 | 69国产盗摄一区二区三区五区 | 国产精品青草综合久久久久99 | 国产欧美久久久精品影院 | 99视频精品免费观看, | 日韩一二区在线观看 | 91麻豆看国产在线紧急地址 | 久久国语露脸国产精品电影 | 成年人黄色av| 久久av一区二区三区亚洲 | 日韩精品一区二区不卡 | 成人三级av| 日日爽| 天天色天天综合 | 免费视频成人 | 亚洲综合小说 | 婷婷亚洲综合五月天小说 | 欧洲一区二区在线观看 | 成人国产精品一区二区 | av青草| 在线看国产一区 | 久草视频在线资源站 | 又黄又刺激又爽的视频 | 国产日韩在线播放 | 免费色黄 | 色婷婷丁香 | av在线收看| 成人av在线电影 | 美女黄色网在线播放 | 操一草 | 在线免费观看黄 | 精品久久久久久综合 | 中文在线字幕免费观 | 中文国产字幕在线观看 | 国产亚洲成av片在线观看 | 99热这里只有精品在线观看 | 婷婷午夜天 | 亚洲一区欧美激情 | 久久精品www人人爽人人 | 日韩在线免费观看视频 | 欧美美女一级片 | 亚洲va欧美| 亚洲成人在线免费 | 99热国产在线中文 | 国产精品国产三级国产 | 在线成人欧美 | 在线91网 | 免费av福利| 亚洲天堂精品视频 | 99热国产在线观看 | 精品国产a | 久久www免费视频 | 久久久久免费网站 | 丁香九月激情 | 手机av在线网站 | 99r在线观看| 天天色综合三 | 在线亚洲天堂网 | 亚洲美女在线一区 | 亚洲精品视频在线观看免费视频 | 亚洲激情视频 | 91视频传媒| 国产精品入口传媒 | 天天摸日日摸人人看 | 亚洲国产精品日韩 | 天堂av色婷婷一区二区三区 | 国产美女精彩久久 | 玖玖玖国产精品 | 日本中文一区二区 | 亚洲人av免费网站 | av在线播放观看 | wwxxxx日本| 久久网站av| 99热国产精品 | 亚洲精品美女在线观看播放 | 五月婷综合网 | 天堂av官网 | 夜夜夜夜爽 | 色婷婷免费 | 国产精品对白一区二区三区 | 永久av免费在线观看 | 亚洲无吗视频在线 | 中文字幕色站 | 国内精品久久久久久久久久 | 亚洲狠狠丁香婷婷综合久久久 | 99 色| 亚洲另类交 | 免费福利在线播放 | 视频在线观看国产 | 黄色精品一区 | 最新国产在线观看 | av成人黄色 | 亚洲二级片 | 涩涩资源网 | 久久99九九99精品 | 在线观看免费av片 | 成人在线观看影院 | 欧美性大战 | 国产成人一区二区啪在线观看 | 在线观看视频精品 | 日本视频久久久 | av一区在线 | 在线观看免费黄色 | 亚洲精品av在线 | 黄色三级在线 | 91av福利视频 | 亚在线播放中文视频 | 日av免费| a级国产乱理伦片在线观看 亚洲3级 | 日韩极品在线 | 在线a视频| 91在线网址 | 国产99久久久精品 | 精品久久久久久综合 | 欧美另类tv | 精品麻豆入口免费 | 午夜精品久久久久久久99 | 国产精品一区二区你懂的 | 久久婷婷一区二区三区 | www.久草.com| 在线天堂中文在线资源网 | 黄色av成人在线 | 99看视频在线观看 | 一区二区三区在线观看中文字幕 | 国产成人精品av | 日本午夜在线亚洲.国产 | 久久精品欧美一 | 中文字幕精品一区久久久久 | 国产自产高清不卡 | 国产成人精品一区二区三区福利 | 97精品国产97久久久久久 | 亚洲精品在线观看不卡 | 国产精品久久久久久久久大全 | 成人av免费看 | 久久精品日本啪啪涩涩 | 免费av网址在线观看 | 日韩国产高清在线 | 日韩一区二区三区高清免费看看 | 久草免费在线视频观看 | 中文字幕中文字幕在线中文字幕三区 | 97热在线观看| 99免费在线播放99久久免费 | 中文字幕在线观看第一区 | 欧美坐爱视频 | av成人在线播放 | 国产精品高潮呻吟久久av无 | 九九综合九九综合 | 亚洲精品永久免费视频 | 久久成人毛片 | 日韩.com | 婷婷丁香激情五月 | 香蕉视频91 | av成人免费网站 | 久久久综合 | 免费亚洲黄色 | 国产午夜三级一二三区 | 成人污视频在线观看 | 激情综合国产 | 手机av在线免费观看 | 婷婷综合久久 | 日日草天天干 | 国产精品乱码久久久久 | 在线免费观看黄色小说 | 91av亚洲| 中文字幕 在线 一 二 | 久久视频网址 | 手机av电影在线 | 91成人蝌蚪 | 三级大片网站 | 四虎永久免费在线观看 | 女人18片毛片90分钟 | 黄色亚洲免费 | 日韩在线视频观看 | 久久久久成人免费 | 欧美精品久久99 | 午夜丰满寂寞少妇精品 | 九九九在线观看视频 | 免费三级骚 | 国产精品一区二区三区在线播放 | 婷婷国产在线 | 亚洲乱码国产乱码精品天美传媒 | 在线小视频你懂得 | 免费h在线观看 | 99久久精品免费 | 亚洲综合视频网 | 精品亚洲男同gayvideo网站 | 国产精品久久久久久久免费观看 | 久久伊人热| 欧美一级性 | 丁香婷婷综合网 | 日韩免费 | 精品久久久久久久久亚洲 | 99性视频 | 亚洲专区欧美专区 | 日韩久久久久久 | 欧美另类重口 | 在线观看av国产 | 国产成人精品不卡 | 99精品国产亚洲 | 成人黄色av免费在线观看 | 午夜视频99 | 9在线观看免费 | 少妇18xxxx性xxxx片 | 久久国语 | 制服丝袜欧美 | 亚洲综合欧美日韩狠狠色 | 久久精品精品 | 日韩欧美精品一区二区三区经典 | 在线观看的黄色 | 久久久国产精品网站 | 在线观看视频免费播放 | 超薄丝袜一二三区 | 91激情视频在线播放 | 日韩精品视频免费看 | 欧美精品做受xxx性少妇 | 国产精品嫩草影视久久久 | 欧美一区二区三区免费观看 | 免费观看国产精品 | 免费看黄色大全 | 狠狠色综合网站久久久久久久 | 黄色av免费| 激情影音 | 91精品在线免费观看 | 久草在线资源观看 | av片在线观看 | av黄色免费看 | 在线观看国产 | 在线看片视频 | 一区二区三区四区在线 | 欧美午夜久久 | 综合久久一本 | 日韩精品视频在线观看免费 | 深夜免费小视频 | 亚洲 成人 欧美 | 91麻豆精品国产91久久久无限制版 | 在线亚州| 欧美精品久久久久久 | 色妞久久福利网 | 国产美女网站在线观看 | 久二影院 | 中文字幕欧美日韩va免费视频 | 欧美va天堂va视频va在线 | 一级黄毛片 | 在线亚洲小视频 | 国产精品 中文字幕 亚洲 欧美 | 日韩免费看视频 | 69久久夜色精品国产69 | 国产精品女人久久久 | 九九精品久久 | 国产免费黄色 | 99自拍视频在线观看 | 日日夜夜精品视频 | 夜夜操夜夜干 | 国产免费观看久久 | 国内精品久久久久影院一蜜桃 | 日韩av成人在线观看 | 日韩二区三区在线观看 | 日日天天干 | 亚洲成年人在线播放 | 色婷婷www | 中文字幕超清在线免费 | 麻豆影视在线免费观看 | 亚洲午夜久久久久久久久电影网 | 99国产精品久久久久久久久久 | 超级碰碰碰免费视频 | 欧美日韩亚洲在线观看 | 成人资源在线 | 天天操狠狠操网站 | 97超碰人人模人人人爽人人爱 | 成人 国产 在线 | 国产精品一区二区无线 | 精品在线一区二区 | 96国产精品 | 精品五月天 | 狠狠操导航 | 国产婷婷一区二区 | 久久激情视频免费观看 | 美女网站在线免费观看 | 亚洲人片在线观看 | 日本精品一区二区 | 91伊人久久大香线蕉蜜芽人口 | 国产一级免费在线观看 | 亚洲粉嫩av | 干亚洲少妇| 免费av小说 | 日韩成人在线一区二区 | 国产视频一区精品 | 青草视频在线 | 成片免费观看视频大全 | 99视频在线观看免费 | 国产精品涩涩屋www在线观看 | 色噜噜在线观看视频 | 精品亚洲免费视频 | 久香蕉| 热久久最新地址 | 国产婷婷久久 | 日韩三级免费观看 | 91精品毛片 | 日韩成人免费在线 | 亚洲日本国产精品 | 日日夜夜免费精品 | 亚洲欧美色婷婷 | 99久久精品日本一区二区免费 | 亚洲精品久久久蜜桃直播 | 国产一级二级在线观看 | 天天天干夜夜夜操 | 精品视频123区在线观看 | 国内精品99 | 日韩国产精品久久 | 亚洲成人精品久久久 | 国产xvideos免费视频播放 | 国产精品中文字幕在线 | 天天干天天干天天干 | 欧美伦理一区二区 | 91精品国产高清 | 中文字幕影片免费在线观看 | 日本久久久久久久久久 | 九九热国产 | 人人舔人人干 | 成人app在线播放 | 91少妇精拍在线播放 | 国产91九色蝌蚪 | 一区在线免费观看 | 精品一区免费 | 亚洲天堂网视频在线观看 | 亚州av成人 | 久久人人爽人人爽 | 国产中文字幕大全 | 精品三级av | 99精品久久99久久久久 | 久久久久久久久久久福利 | 亚洲精品动漫成人3d无尽在线 | www成人av | 91九色视频在线 | 成人网色 | 91成人免费看片 | 成年人视频免费在线播放 | 99中文视频在线 | 日韩av成人在线 | 九九激情视频 | 久久久综合精品 | 亚洲最新合集 | 最近能播放的中文字幕 | 久久久久欠精品国产毛片国产毛生 | 狠狠狠狠狠狠狠狠 | 精品女同一区二区三区在线观看 | 一区二区三区四区五区在线 | 亚洲天天看 | 国产视频二 | 国产97在线视频 | 五月婷婷综合激情网 | 国产伦精品一区二区三区免费 | 欧美一级性生活 | 麻豆91精品91久久久 | 波多野结衣视频一区二区三区 | 狠狠色噜噜狠狠 | 国内一级片在线观看 | 国产精品福利午夜在线观看 | 日日精品| 成年人在线免费看 | 国产精品 视频 | 97成人在线观看 | 色婷婷视频| 久久久久亚洲精品成人网小说 | 91精品久久久久久久久 | 国产xxxxx在线观看 | 韩日视频在线 | 国产中的精品av小宝探花 | 亚洲一区欧美激情 | 91豆花在线观看 | 国产在线精品播放 | 国产中文字幕一区二区三区 | 黄色三级免费观看 | 成人久久亚洲 | 国产在线精品一区二区三区 | 久久国产精品久久精品国产演员表 | 在线亚洲高清视频 | 国产一二区免费视频 | 久久精品国产v日韩v亚洲 | 国产特级毛片aaaaaa高清 | 日日添夜夜添 | 青青久视频 | 99精品热视频 | 天天摸日日摸人人看 | 偷拍福利视频一区二区三区 | 国产一区二区久久久久 | 亚洲 欧美 91| 成人片在线播放 | 欧美精品国产综合久久 | 精品久久久久免费极品大片 | 99re久久资源最新地址 | 1024手机在线看 | 精品视频久久 | 欧美成人在线免费 | 在线中文视频 | 久青草影院 | av色一区 | 久草在线免费电影 | www.久久免费| 毛片的网址 | 亚洲欧美日韩精品久久久 | 亚洲一区av | 天天鲁天天干天天射 | 天天操天天干天天操天天干 | 精品视频不卡 | 中文字幕在线播放第一页 | 免费欧美精品 | 国产免费黄视频在线观看 | 亚洲午夜激情网 | 韩日av一区二区 | 99精品视频在线观看播放 | 亚洲高清av | 在线视频 精品 | 91试看 | 久精品视频在线观看 | 久久久精品国产一区二区 | 久久久精品二区 | 四虎免费在线观看 | 亚洲欧美日韩中文在线 | 91精品国产入口 | 不卡视频一区二区三区 | 日韩在线电影一区二区 | 九九视频精品免费 | 激情五月在线视频 | 久久精品三 | 99欧美视频| 九草视频在线观看 | 亚洲永久免费av | 日韩免费久久 | 亚洲精品播放 | 尤物一区二区三区 | 天天弄天天操 | 欧美精品中文字幕亚洲专区 | 亚洲精品视频在 | 午夜性色 | 日韩精品中文字幕在线不卡尤物 | 波多野结衣资源 | 精品欧美小视频在线观看 | 婷婷六月天在线 | 色综合久久66 | 成人小电影在线看 | 欧美一区二区日韩一区二区 | 欧美午夜寂寞影院 | 亚洲精品99久久久久久 | 色综合天天综合网国产成人网 | 国产精品破处视频 | 国内精品久久天天躁人人爽 | 久久99婷婷| 男女精品久久 | 99久久久久国产精品免费 | 国产精品毛片久久久 |