Android圖形顯示之硬體抽象層Gralloc

來源:互聯網
上載者:User

FrameBuffer驅動程式分析文中介紹了Linux系統下的顯示驅動架構,每個顯示屏被抽象為一個框架緩衝區,註冊到FrameBuffer模組中,並在/dev/graphics目錄下建立對應的fbX裝置。Android系統在硬體抽象層中提供了一個Gralloc模組,封裝了對框架緩衝區的所有訪問操作。使用者空間的應用程式在使用框架緩衝區之間,首先要載入Gralloc模組,並且獲得一個gralloc裝置和一個fb裝置。有了gralloc裝置之後,使用者空間中的應用程式就可以申請分配一塊圖形緩衝區,並且將這塊圖形緩衝區映射到應用程式的地址空間來,以便可以向裡面寫入要繪製的畫面的內容。最後,使用者空間中的應用程式就通過fb裝置來將已經準備好了的圖形緩衝區渲染到框架緩衝區中去,即將圖形緩衝區的內容繪製到顯示屏中去。相應地,當使用者空間中的應用程式不再需要使用一塊圖形緩衝區的時候,就可以通過gralloc裝置來釋放它,並且將它從地址空間中解除映射。

Gralloc模組實現源碼位於:hardware/libhardware/modules/gralloc

.
├── Android.mk
├── framebuffer.cpp
├── gralloc.cpp
├── gralloc_priv.h
├── gr.h
└── mapper.cpp

Android硬體抽象Hardware庫載入過程源碼分析介紹了Android系統中的硬體抽象層模組的載入過程,並指出每個硬體抽象層模組都必須定義HAL_MODULE_INFO_SYM符號,並且有自己唯一的ID,Gralloc也不例外,Gralloc模組ID定義為:

#define GRALLOC_HARDWARE_MODULE_ID "gralloc"
同時定義了以HAL_MODULE_INFO_SYM為符號的類型為private_module_t的結構體:

hardware\libhardware\modules\gralloc\gralloc.cpp

static struct hw_module_methods_t gralloc_module_methods = {        open: gralloc_device_open};struct private_module_t HAL_MODULE_INFO_SYM = {    base: {        common: {            tag: HARDWARE_MODULE_TAG,            version_major: 1,            version_minor: 0,            id: GRALLOC_HARDWARE_MODULE_ID,            name: "Graphics Memory Allocator Module",            author: "The Android Open Source Project",            methods: &gralloc_module_methods        },        registerBuffer: gralloc_register_buffer,        unregisterBuffer: gralloc_unregister_buffer,        lock: gralloc_lock,        unlock: gralloc_unlock,    },    framebuffer: 0,    flags: 0,    numBuffers: 0,    bufferMask: 0,    lock: PTHREAD_MUTEX_INITIALIZER,    currentBuffer: 0,};
通過Android硬體抽象Hardware庫載入過程源碼分析的方法將Gralloc模組載入到記憶體中來之後,就可以調用函數dlsym來獲得它所匯出的符號HMI,得到private_module_t的首地址後,由於private_module_t的第一個成員變數的類型為gralloc_module_t,因此也是gralloc_module_t的首地址,由於gralloc_module_t的第一個成員變數類型為hw_module_t,因此也是hw_module_t的首地址,因此只要得到這三種類型中其中一種類型變數的地址,就可以相互轉換為其他兩種類型的指標。

資料結構定義

在分析Gralloc模組之前,首先介紹Gralloc模組定義的一些資料結構。private_module_t用於描述Gralloc模組下的系統框架緩衝區資訊

struct private_module_t {    gralloc_module_t base;    private_handle_t* framebuffer; //指向系統框架緩衝區的控制代碼    uint32_t flags; //用來標誌系統框架緩衝區是否支援雙緩衝    uint32_t numBuffers;//表示系統框架緩衝區包含有多少個圖形緩衝區    uint32_t bufferMask; //記錄系統框架緩衝區中的圖形緩衝區的使用方式    pthread_mutex_t lock; //一個互斥鎖,用來保護結構體private_module_t的並行訪問    buffer_handle_t currentBuffer; //用來描述當前正在被渲染的圖形緩衝區    int pmem_master;    void* pmem_master_base;    struct fb_var_screeninfo info; //儲存裝置顯示屏的動態屬性資訊    struct fb_fix_screeninfo finfo; ////儲存裝置顯示屏的固定屬性資訊    float xdpi; //描述裝置顯示屏在寬度    float ydpi; //描述裝置顯示屏在高度    float fps; //用來描述顯示屏的重新整理頻率};

framebuffer_device_t用來描述系統框架緩衝區裝置的資訊

typedef struct framebuffer_device_t {    struct hw_device_t common;    const uint32_t  flags;//用來記錄系統框架緩衝區的標誌    const uint32_t  width;//用來描述裝置顯示屏的寬度    const uint32_t  height;//用來描述裝置顯示屏的高度    const int       stride;//用來描述裝置顯示屏的一行有多少個像素點    const int       format;//用來描述系統框架緩衝區的像素格式    const float     xdpi;//用來描述裝置顯示屏在寬度上的密度    const float     ydpi;//用來描述裝置顯示屏在高度上的密度    const float     fps;//用來描述裝置顯示屏的重新整理頻率    const int       minSwapInterval;//用來描述框架緩衝區交換前後兩個圖形緩衝區的最小時間間隔    const int       maxSwapInterval;//用來描述框架緩衝區交換前後兩個圖形緩衝區的最大時間間隔    int reserved[8];//保留//用來設定框架緩衝區交換前後兩個圖形緩衝區的最小和最大時間間隔    int (*setSwapInterval)(struct framebuffer_device_t* window,int interval);//用來設定框架緩衝區的更新地區    int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);//用來將圖形緩衝區buffer的內容渲染到框架緩衝區中去    int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);//用來通知fb裝置,圖形緩衝區的組合工作已經完成    int (*compositionComplete)(struct framebuffer_device_t* dev);    void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len);    int (*enableScreen)(struct framebuffer_device_t* dev, int enable);//保留    void* reserved_proc[6];} framebuffer_device_t;

gralloc_module_t用於描述gralloc模組資訊

typedef struct gralloc_module_t {  struct hw_module_t common;  //映射一塊圖形緩衝區到一個進程的地址空間去  int (*registerBuffer)(struct gralloc_module_t const* module,buffer_handle_t handle);  //取消映射一塊圖形緩衝區到一個進程的地址空間去  int (*unregisterBuffer)(struct gralloc_module_t const* module,buffer_handle_t handle);  //鎖定一個指定的圖形緩衝區    int (*lock)(struct gralloc_module_t const* module,buffer_handle_t handle, int usage,            int l, int t, int w, int h,void** vaddr);    //解鎖一個指定的圖形緩衝區  int (*unlock)(struct gralloc_module_t const* module,buffer_handle_t handle);    int (*perform)(struct gralloc_module_t const* module,int operation, ... );    void* reserved_proc[7];} gralloc_module_t;

alloc_device_t用於描述gralloc裝置的資訊

typedef struct alloc_device_t {    struct hw_device_t common;//用於分配一塊圖形緩衝區    int (*alloc)(struct alloc_device_t* dev,int w, int h, int format, int usage,buffer_handle_t* handle, int* stride);//用於釋放指定的圖形緩衝區    int (*free)(struct alloc_device_t* dev,buffer_handle_t handle);    void (*dump)(struct alloc_device_t *dev, char *buff, int buff_len);    void* reserved_proc[7];} alloc_device_t;

typedef struct hw_module_t {    uint32_t tag;//標籤  uint16_t version_major;//模組主裝置號  uint16_t version_minor;//模組次裝置號    const char *id;//模組ID    const char *name;//模組名稱    const char *author;//模組作者    struct hw_module_methods_t* methods;//模組操作方法    void* dso;//儲存模組首地址    uint32_t reserved[32-7];//保留位} hw_module_t;

硬體抽象層Gralloc模組定義了裝置fb和裝置gpu:

#define GRALLOC_HARDWARE_FB0 "fb0"#define GRALLOC_HARDWARE_GPU0 "gpu0"


裝置gpu用於分配圖形緩衝區,而裝置fb用於渲染圖形緩衝區;hw_module_t用於描述硬體抽象層Gralloc模組,而hw_device_t則用於描述硬體抽象層Gralloc裝置,通過硬體抽象層裝置可以找到對應的硬體抽象層模組。在Gralloc模組中,無論是定義fb裝置還是gpu裝置,都是用來處理圖形緩衝區,以下是關於緩衝區的資料結構 定義:

private_handle_t用來描述一塊緩衝區,Android對緩衝區的定義提供了C和C++兩種方式,C語言編譯器下的定義:

struct private_handle_t {    struct native_handle nativeHandle;    enum {        PRIV_FLAGS_FRAMEBUFFER = 0x00000001    };    int     fd; //指向一個檔案描述符,這個檔案描述符要麼指向框架緩衝區裝置,要麼指向一塊匿名共用記憶體    int     magic;    int     flags;//用來描述一個緩衝區的標誌,當一個緩衝區的標誌值等於PRIV_FLAGS_FRAMEBUFFER的時候,就表示它是在框架緩衝區中分配的。    int     size;//用來描述一個緩衝區的大小    int     offset;//用來描述一個緩衝區的位移地址    int     base;//用來描述一個緩衝區的實際地址    int     pid;//用來描述一個緩衝區的建立者的PID};

C++編譯器下的定義:

struct private_handle_t : public native_handle {    enum {        PRIV_FLAGS_FRAMEBUFFER = 0x00000001    };    int     fd; //指向一個檔案描述符,這個檔案描述符要麼指向框架緩衝區裝置,要麼指向一塊匿名共用記憶體    int     magic;//指向一個魔數,它的值由靜態成員變數sMagic來指定,用來標識一個private_handle_t結構體。    int     flags;//用來描述一個緩衝區的標誌,它的值要麼等於0,要麼等於PRIV_FLAGS_FRAMEBUFFER    int     size;//用來描述一個緩衝區的大小。    int     offset;//用來描述一個緩衝區的位移地址。    int     base;//用來描述一個緩衝區的實際地址,它是通過成員變數offset來計算得到的。    int     pid;//用來描述一個緩衝區的建立者的PID。    static const int sNumInts = 6; //包含有6個整數    static const int sNumFds = 1; //包含有1個檔案描述符    static const int sMagic = 0x3141592;};

兩種編譯器下的private_handle_t定義都繼承於native_handle,native_handle的定義如下:

typedef struct native_handle{    int version; //設定為結構體native_handle_t的大小,用來標識結構體native_handle_t的版本    int numFds;  //表示結構體native_handle_t所包含的檔案描述符的個數,這些檔案描述符儲存在成員變數data所指向的一塊緩衝區中。    int numInts; //表示結構體native_handle_t所包含的整數值的個數,這些整數儲存在成員變數data所指向的一塊緩衝區中。    int data[0]; //指向的一塊緩衝區中} native_handle_t;typedef const native_handle_t* buffer_handle_t;

Gralloc模組的開啟過程在Android硬體抽象Hardware庫載入過程源碼分析中詳細分析過了,下面就分析Gralloc模組中定義了兩種裝置的開啟過程。


Fb裝置開啟過程

fb裝置的ID值定義為#defineGRALLOC_HARDWARE_FB0"fb0",fb裝置使用結構體framebuffer_device_t來描述。結構體framebuffer_device_t是用來描述系統框架緩衝區的資訊

hardware\libhardware\include\hardware\fb.h

static inline int framebuffer_open(const struct hw_module_t* module,        struct framebuffer_device_t** device) {    return module->methods->open(module,GRALLOC_HARDWARE_FB0, (struct hw_device_t**)device);}
module指向的是一個用來描述Gralloc模組的hw_module_t結構體,前面提到,它的成員變數methods所指向的一個hw_module_methods_t結構體的成員函數open指向了Gralloc模組中的函數gralloc_device_open

hardware\libhardware\modules\gralloc\gralloc.cpp

int gralloc_device_open(const hw_module_t* module, const char* name,        hw_device_t** device){    int status = -EINVAL;    if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {       ...    } else {        status = fb_device_open(module, name, device);    }    return status;}
gralloc_device_open函數即可以開啟fb裝置,也可以用於開啟gpu裝置,這雷根據裝置名稱來區分開啟的裝置,對應fb裝置,則調用fb_device_open函數來完成裝置開啟操作。

hardware\libhardware\modules\gralloc\framebuffer.cpp

int fb_device_open(hw_module_t const* module, const char* name,        hw_device_t** device){    int status = -EINVAL;//判斷開啟的是fb裝置    if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {        alloc_device_t* gralloc_device;//開啟gpu裝置        status = gralloc_open(module, &gralloc_device);        if (status < 0)            return status;        //建立一個fb_context_t對象,用來描述fb裝置上下文        fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));        memset(dev, 0, sizeof(*dev));        //初始化fb_context_t對象        dev->device.common.tag = HARDWARE_DEVICE_TAG;        dev->device.common.version = 0;        dev->device.common.module = const_cast<hw_module_t*>(module);//註冊fb裝置的操作函數        dev->device.common.close = fb_close;         dev->device.setSwapInterval = fb_setSwapInterval;        dev->device.post            = fb_post;        dev->device.setUpdateRect = 0;                private_module_t* m = (private_module_t*)module;//將fb映射到當前進程地址空間        status = mapFrameBuffer(m);        if (status >= 0) {            int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);            int format = (m->info.bits_per_pixel == 32)                         ? HAL_PIXEL_FORMAT_RGBX_8888                         : HAL_PIXEL_FORMAT_RGB_565;            const_cast<uint32_t&>(dev->device.flags) = 0;            const_cast<uint32_t&>(dev->device.width) = m->info.xres;            const_cast<uint32_t&>(dev->device.height) = m->info.yres;            const_cast<int&>(dev->device.stride) = stride;            const_cast<int&>(dev->device.format) = format;            const_cast<float&>(dev->device.xdpi) = m->xdpi;            const_cast<float&>(dev->device.ydpi) = m->ydpi;            const_cast<float&>(dev->device.fps) = m->fps;            const_cast<int&>(dev->device.minSwapInterval) = 1;            const_cast<int&>(dev->device.maxSwapInterval) = 1;            *device = &dev->device.common;        }    }    return status;}

這個函數主要是用來建立一個fb_context_t結構體,並且對它的成員變數device進行初始化。結構體fb_context_t的成員變數device的類型為framebuffer_device_t,它是用來描述fb裝置的。fb裝置主要是用來渲染圖形緩衝區的,這是通過調用它的成員函數post來實現的。函數fb_device_open所開啟的fb裝置的成員函數post被設定為Gralloc模組中的函數fb_post。函數mapFrameBuffer除了用來獲得系統框架緩衝區的資訊之外,還會將系統框架緩衝區映射到當前進程的地址空間來。line_length用來描述顯示屏一行像素總共所佔用的位元組數,bits_per_pixel用來描述顯示屏每一個像素所佔用的位元,bits_per_pixel的值向右移3位,就可以得到顯示屏每一個像素所佔用的位元組數。用顯示屏像素總共所佔用的位元組數line_length除以每一個像素所佔用的位元組數就可以得到顯示屏一行有多少個像素點,並儲存在stride中。

stride = line_length / ( bits_per_pixel >> 3)

static int mapFrameBuffer(struct private_module_t* module){    pthread_mutex_lock(&module->lock);    int err = mapFrameBufferLocked(module);    pthread_mutex_unlock(&module->lock);    return err;}
調用mapFrameBufferLocked函數執行映射過程,該函數線上程保護下完成。
int mapFrameBufferLocked(struct private_module_t* module){    // already initialized...    if (module->framebuffer) {        return 0;    }        char const * const device_template[] = {            "/dev/graphics/fb%u",            "/dev/fb%u",            0 };    int fd = -1;    int i=0;    char name[64];//檢查是否存在裝置檔案/dev/graphics/fb0或者/dev/fb0。如果存在的話,那麼就調用函數open來開啟它,並且將得到的檔案描述符儲存在變數fd中    while ((fd==-1) && device_template[i]) {        snprintf(name, 64, device_template[i], 0);        fd = open(name, O_RDWR, 0);        i++;    }    if (fd < 0)        return -errno;//通過IO控制命令FBIOGET_FSCREENINFO來獲得系統框架緩衝區的固定資訊,儲存在fb_fix_screeninfo結構體finfo中    struct fb_fix_screeninfo finfo;    if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)        return -errno;//通過IO控制命令FBIOGET_VSCREENINFO來獲得系統框架緩衝區的可變資訊,儲存在fb_var_screeninfo結構體info中    struct fb_var_screeninfo info;    if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)        return -errno;//初始化info    info.reserved[0] = 0;    info.reserved[1] = 0;    info.reserved[2] = 0;    info.xoffset = 0;    info.yoffset = 0;    info.activate = FB_ACTIVATE_NOW;//fb_var_screeninfo的成員變數xres和yres用來描述顯示屏的可視解析度,而成員變數xres_virtual和yres_virtual用來描述顯示屏的虛擬解析度。//將虛擬解析度的高度值設定為可視解析度的高度值的NUM_BUFFERS倍。    info.yres_virtual = info.yres * NUM_BUFFERS; //2    uint32_t flags = PAGE_FLIP;//設定裝置顯示屏的虛擬解析度    if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {//設定失敗,重新設定顯示屏的虛擬解析度        info.yres_virtual = info.yres;        flags &= ~PAGE_FLIP;        ALOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");    }    if (info.yres_virtual < info.yres * 2) {        // we need at least 2 for page-flipping        info.yres_virtual = info.yres;        flags &= ~PAGE_FLIP;        ALOGW("page flipping not supported (yres_virtual=%d, requested=%d)",info.yres_virtual, info.yres*2);    }//通過IO控制命令FBIOGET_VSCREENINFO來重新獲得系統框架緩衝區的可變資訊    if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)        return -errno;//計算裝置顯示屏的重新整理頻率    uint64_t  refreshQuotient =    (            uint64_t( info.upper_margin + info.lower_margin + info.yres )* ( info.left_margin  + info.right_margin + info.xres )* info.pixclock    );//模擬器的info.pixclock=0,因此計算得到的refreshQuotient=0    int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0;//如果是模擬器,設定重新整理頻率為60 Hz    if (refreshRate == 0) {        refreshRate = 60*1000;  // 60 Hz    }    if (int(info.width) <= 0 || int(info.height) <= 0) {        info.width  = ((info.xres * 25.4f)/160.0f + 0.5f);        info.height = ((info.yres * 25.4f)/160.0f + 0.5f);    }//計算顯示屏的密度    float xdpi = (info.xres * 25.4f) / info.width;    float ydpi = (info.yres * 25.4f) / info.height;    float fps  = refreshRate / 1000.0f;//再次通過IO控制命令FBIOGET_FSCREENINFO來獲得系統框架緩衝區的固定資訊    if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)        return -errno;    if (finfo.smem_len <= 0)        return -errno;//得到的系統框架緩衝區的其它資訊來初始化參數module所描述的一個private_module_t結構體。    module->flags = flags;    module->info = info;    module->finfo = finfo;    module->xdpi = xdpi;    module->ydpi = ydpi;    module->fps = fps;    int err;//整個系統框架緩衝區的大小=虛擬解析度的高度值info.yres_virtual * 每一行所佔用的位元組數finfo.line_length,並將整個系統框架緩衝區的大小對齊到頁面邊界    size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);//建立一個private_handle_t,用來描述整個系統框架緩衝區的資訊,    module->framebuffer = new private_handle_t(dup(fd), fbSize, 0);    //計算整個系統框架緩衝區可以劃分為多少個圖形緩衝區來使用    module->numBuffers = info.yres_virtual / info.yres;//表示系統框架緩衝區中的所有圖形緩衝區都是處於空閑狀態    module->bufferMask = 0;    //以讀寫共用方式將框架緩衝區映射到當前進程地址空間中    void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);    if (vaddr == MAP_FAILED) {        ALOGE("Error mapping the framebuffer (%s)", strerror(errno));        return -errno;    }//系統框架緩衝區在當前進程的地址空間中的起始地址儲存到private_handle_t的域base中    module->framebuffer->base = intptr_t(vaddr);//清空大小為fbSize的框架緩衝區    memset(vaddr, 0, fbSize);    return 0;}


在瞭解本節內容之前首先需要瞭解Linux的FrameBuffer驅動,請查看FrameBuffer驅動程式分析.

Gpu裝置開啟過程gralloc裝置使用結構體alloc_device_t來描述。結構體alloc_device_t有兩個成員函數alloc和free,分別用來分配和釋放圖形緩衝區,gralloc裝置的ID值定義為:

#defineGRALLOC_HARDWARE_GPU0"gpu0"
hardware\libhardware\include\hardware\gralloc.h

static inline int gralloc_open(const struct hw_module_t* module,         struct alloc_device_t** device) {    return module->methods->open(module,             GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device);}
module指向的是一個用來描述Gralloc模組的hw_module_t結構體,它的成員變數methods所指向的一個hw_module_methods_t結構體的成員函數open指向了Gralloc模組中的函數gralloc_device_open。前面介紹了函數gralloc_device_open即可以開啟fb裝置也可用來開啟gpu裝置,這裡傳入的裝置名稱為GRALLOC_HARDWARE_GPU0,表示當前開啟的是gpu裝置。

hardware\libhardware\modules\gralloc\gralloc.cpp

int gralloc_device_open(const hw_module_t* module, const char* name,        hw_device_t** device){    int status = -EINVAL;    if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {        gralloc_context_t *dev;        dev = (gralloc_context_t*)malloc(sizeof(*dev));        /* initialize our state here */        memset(dev, 0, sizeof(*dev));        /* initialize the procs */        dev->device.common.tag = HARDWARE_DEVICE_TAG;        dev->device.common.version = 0;        dev->device.common.module = const_cast<hw_module_t*>(module);        dev->device.common.close = gralloc_close;        dev->device.alloc   = gralloc_alloc;        dev->device.free    = gralloc_free;        *device = &dev->device.common;        status = 0;    } else {        ...    }    return status;}
這個函數主要是用來建立一個gralloc_context_t結構體,並且對它的成員變數device進行初始化。結構體gralloc_context_t的成員變數device的類型為gralloc_device_t,它用來描述一個gralloc裝置。前面提到,gralloc裝置是用來分配和釋放圖形緩衝區的,這是通過調用它的成員函數alloc和free來實現的。從這裡可以看出,函數gralloc_device_open所開啟的gralloc裝置的成員函數alloc和free分別被設定為Gralloc模組中的函數gralloc_alloc和gralloc_free。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.