概述
Android 中 lcd 是一个帧缓冲设备,驱动程序通过处理器的 lcd 控制器将物理内存的一段区域设置为显存,如果向这段内存区域写入数据就会马上在 lcd 上显示出来。Android 在 HAL 中提供了gralloc 模块,封装了用户层对帧缓冲设备的所有操作接口,并通过 SurfaceFlinger 服务向应用提供显示支持。在启动过程中系统会加载 gralloc 模块,然后打开帧缓冲设备,获取设备的各种参数并完成 gralloc 模块的初始化。当应用程序需要把内容显示到 lcd 上时,需要通过 gralloc 模块申请一块图形缓冲区,然后将这块图形缓冲区映射到自己的地址空间并写入内容即可。当应用程序不再需要这块图形缓冲区时需要通过 gralloc 模块释放掉,然后解除对缓冲区的映射。
1、基础数据结构
gralloc 模块通过 struct private_module_t 来描述,该结构定义如下:
- 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; /* pmem设备节点的描述符 */
- void* pmem_master_base; /* pmem的起始虚拟地址 */
- struct fb_var_screeninfo info; /* lcd的可变参数 */
- struct fb_fix_screeninfo finfo; /* lcd的固定参数 */
- float xdpi; /* x方向上每英寸的像素数量 */
- float ydpi; /* y方向上每英寸的像素数量 */
- float fps; /* lcd的刷新率 */
- int orientation; /* 显示方向 */
- enum {
- PRIV_USAGE_LOCKED_FOR_POST = 0x80000000 /* flag to indicate we'll post this buffer */
- };
- };
- #ifdef __cplusplus
- struct private_handle_t : public native_handle {
- #else
- struct private_handle_t {
- struct native_handle nativeHandle; /* 用来描述一个本地句柄值 */
- #endif
- enum {
- PRIV_FLAGS_FRAMEBUFFER = 0x00000001,
- PRIV_FLAGS_USES_PMEM = 0x00000002,
- PRIV_FLAGS_USES_MMEM = 0x00000004,
- PRIV_FLAGS_NEEDS_FLUSH = 0x00000008,
- };
- enum {
- LOCK_STATE_WRITE = 1<<31,
- LOCK_STATE_MAPPED = 1<<30,
- LOCK_STATE_READ_MASK = 0x3FFFFFFF
- };
- /* 指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存
- * 取决于private_handle_t描述的图形缓冲区是在帧缓冲区分配的,还是在内存中分配的 */
- int fd;
- /* 指向一个魔数,它的值由静态成员变量sMagic来指定,用来标识一个private_handle_t结构体 */
- int magic;
- /* 用来描述一个图形缓冲区的标志,它的值要么等于0,要么等于PRIV_FLAGS_FRAMEBUFFER
- * 当一个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER的时候,就表示它是在帧缓冲区中分配的 */
- int flags;
- int size; /* 描述一个图形缓冲区的大小 */
- int offset; /* 描述一个图形缓冲区的偏移地址 */
- int phys; /* 图形缓冲区或帧缓冲的起始物理地址 */
- int base; /* 图形缓冲区或帧缓冲的起始虚拟地址 */
- int lockState;
- int writeOwner;
- int pid; /* 描述一个图形缓冲区的创建者的PID */
- #ifdef __cplusplus
- static const int sNumInts = 9; /* 有9个整数变量 */
- static const int sNumFds = 1; /* 有1个文件描述符 */
- static const int sMagic = 0x3141592;
- private_handle_t(int fd, int size, int flags) :
- fd(fd), magic(sMagic), flags(flags), size(size), offset(0),
- phys(0), base(0), lockState(0), writeOwner(0), pid(getpid())
- {
- version = sizeof(native_handle);
- numInts = sNumInts;
- numFds = sNumFds;
- }
- ~private_handle_t() {
- magic = 0;
- }
- bool usesPhysicallyContiguousMemory() {
- return (flags & PRIV_FLAGS_USES_PMEM) != 0;
- }
- /* 用来验证一个native_handle_t指针是否指向了一个private_handle_t结构体 */
- static int validate(const native_handle* h) {
- const private_handle_t* hnd = (const private_handle_t*)h;
- if (!h || h->version != sizeof(native_handle) ||
- h->numInts != sNumInts || h->numFds != sNumFds ||
- hnd->magic != sMagic)
- {
- LOGE("invalid gralloc handle (at %p)", h);
- return -EINVAL;
- }
- return 0;
- }
- static private_handle_t* dynamicCast(const native_handle* in) {
- if (validate(in) == 0) {
- return (private_handle_t*) in;
- }
- return NULL;
- }
- #endif
- };
图形缓冲区的操作接口由结构 struct gralloc_module_t 定义:
- typedef struct gralloc_module_t {
- struct hw_module_t common;
- /* 注册一个图形缓冲区,这个指定的图形缓冲区使用一个buffer_handle_t句柄来描述 */
- int (*registerBuffer)(struct gralloc_module_t const* module,
- buffer_handle_t handle);
- /* 注销一个图形缓冲区 */
- int (*unregisterBuffer)(struct gralloc_module_t const* module,
- buffer_handle_t handle);
- /* 用来锁定一个图形缓冲区并将缓冲区映射到用户进程
- * 在锁定一块图形缓冲区的时候,可以指定要锁定的图形绘冲区的位置以及大小
- * 这是通过参数l、t、w和h来指定的,其中,参数l和t指定的是要访问的图形缓冲区的左上角位置
- * 而参数w和h指定的是要访问的图形缓冲区的宽度和长度
- * 锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数vaddr中
- * 另一方面,在访问完成一块图形缓冲区之后,需要解除这块图形缓冲区的锁定 */
- 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, ... );
- /* reserved for future use */
- void* reserved_proc[7];
- } gralloc_module_t;
gralloc 设备则用结构 struct alloc_device_t 来描述,其定义如下:
- 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);
- } alloc_device_t;
- typedef struct framebuffer_device_t {
- struct hw_device_t common;
- const uint32_t flags; /* 用来记录系统帧缓冲区的标志 */
- const uint32_t width; /* lcd显示区域的像素点数 */
- const uint32_t height;
- const int stride; /* 描述设备显示屏的一行有多少个像素点 */
- /* 描述系统帧缓冲区的像素格式,主要有HAL_PIXEL_FORMAT_RGBX_8888和HAL_PIXEL_FORMAT_RGB_565两种 */
- const int format;
- const float xdpi;
- const float ydpi;
- const float fps; /* lcd刷新率 */
- 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设备device,图形缓冲区的组合工作已经完成 */
- int (*compositionComplete)(struct framebuffer_device_t* dev);
- void* reserved_proc[8];
- } framebuffer_device_t;
2、gralloc 模块
HAL 中通过 hw_get_module 接口加载指定 id 的模块,并获得一个 hw_module_t 结构来打开设备,流程如下:
- #define HAL_LIBRARY_PATH1 "/system/lib/hw"
- #define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
- static const char *variant_keys[] = {
- "ro.hardware", /* This goes first so that it can pick up a different file on the emulator. */
- "ro.product.board",
- "ro.board.platform",
- "ro.arch"
- };
- static const int HAL_VARIANT_KEYS_COUNT =
- (sizeof(variant_keys)/sizeof(variant_keys[0]));
- int hw_get_module(const char *id, const struct hw_module_t **module)
- {
- int status;
- int i;
- const struct hw_module_t *hmi = NULL;
- char prop[PATH_MAX];
- char path[PATH_MAX];
- /*
- * Here we rely on the fact that calling dlopen multiple times on
- * the same .so will simply increment a refcount (and not load
- * a new copy of the library).
- * We also assume that dlopen() is thread-safe.
- */
- /* Loop through the configuration variants looking for a module */
- for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
- if (i < HAL_VARIANT_KEYS_COUNT) {
- if (property_get(variant_keys[i], prop, NULL) == 0) { /* 读取variant_keys数组指定的属性值 */
- continue;
- }
- snprintf(path, sizeof(path), "%s/%s.%s.so", /* 格式化模块名和路径,如:/system/lib/hw/gralloc.xxx.so */
- HAL_LIBRARY_PATH1, id, prop);
- if (access(path, R_OK) == 0) break;
- snprintf(path, sizeof(path), "%s/%s.%s.so",
- HAL_LIBRARY_PATH2, id, prop);
- if (access(path, R_OK) == 0) break;
- } else {
- snprintf(path, sizeof(path), "%s/%s.default.so",
- HAL_LIBRARY_PATH1, id);
- if (access(path, R_OK) == 0) break;
- }
- }
- status = -ENOENT;
- if (i < HAL_VARIANT_KEYS_COUNT+1) {
- /* load the module, if this fails, we're doomed, and we should not try to load a different variant. */
- status = load(id, path, module); /* 加载模块 */
- }
- return status;
- }
函数会在 /system/lib/hw 或者 /vendor/lib/hw 目录中去寻找gralloc.xxx.so 文件,如果找到了就调用load接口完成加载。最终会调用 gralloc_device_open完成 gralloc 设备成员的初始化:
- 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 {
- status = fb_device_open(module, name, device);
- }
- return status;
- }
- static int gralloc_alloc_framebuffer(alloc_device_t* dev,size_t size, int usage, buffer_handle_t* pHandle)
- {
- private_module_t* m = reinterpret_cast<private_module_t*>(
- dev->common.module);
- pthread_mutex_lock(&m->lock);
- int err = gralloc_alloc_framebuffer_locked(dev, size, usage, pHandle);
- pthread_mutex_unlock(&m->lock);
- return err;
- }
- static int gralloc_alloc_buffer(alloc_device_t* dev,size_t size, int usage, buffer_handle_t* pHandle)
- {
- int err = 0;
- int fd = -1;
- size = roundUpToPageSize(size);
- fd = ashmem_create_region("gralloc-buffer", size);
- if (fd < 0) {
- LOGE("couldn't create ashmem (%s)", strerror(-errno));
- err = -errno;
- }
- if (err == 0) {
- private_handle_t* hnd = new private_handle_t(fd, size, 0);
- gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(
- dev->common.module);
- err = mapBuffer(module, hnd);
- if (err == 0) {
- *pHandle = hnd;
- }
- }
- LOGE_IF(err, "gralloc failed err=%s", strerror(-err));
- return err;
- }
- /*****************************************************************************/
- static int gralloc_alloc(alloc_device_t* dev,int w, int h, int format, int usage,
- buffer_handle_t* pHandle, int* pStride)
- {
- if (!pHandle || !pStride)
- return -EINVAL;
- size_t size, stride;
- int align = 4;
- int bpp = 0;
- switch (format) { /* 一个像素点占用的字节数 */
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
- case HAL_PIXEL_FORMAT_BGRA_8888:
- bpp = 4;
- break;
- case HAL_PIXEL_FORMAT_RGB_888:
- bpp = 3;
- break;
- case HAL_PIXEL_FORMAT_RGB_565:
- case HAL_PIXEL_FORMAT_RGBA_5551:
- case HAL_PIXEL_FORMAT_RGBA_4444:
- bpp = 2;
- break;
- default:
- return -EINVAL;
- }
- size_t bpr = (w*bpp + (align-1)) & ~(align-1);
- size = bpr * h;
- stride = bpr / bpp;
- int err;
- if (usage & GRALLOC_USAGE_HW_FB) {
- err = gralloc_alloc_framebuffer(dev, size, usage, pHandle); /* 在系统帧缓冲中分配图形缓冲区 */
- } else {
- err = gralloc_alloc_buffer(dev, size, usage, pHandle); /* 在内存中分配图形缓冲区 */
- }
- if (err < 0) {
- return err;
- }
- *pStride = stride;
- return 0;
- }
在 gralloc_device_open 中会根据传递的参数分别初始化两个设备,定义如下:
- #define GRALLOC_HARDWARE_FB0 "fb0"
- #define GRALLOC_HARDWARE_GPU0 "gpu0"
- int mapFrameBufferLocked(struct private_module_t* module)
- {
- 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];
- 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;
- struct fb_fix_screeninfo finfo;
- if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) /* 获取帧缓冲的固定参数 */
- return -errno;
- struct fb_var_screeninfo info;
- if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) /* 获取帧缓冲的可变参数 */
- return -errno;
- info.reserved[0] = 0;
- info.reserved[1] = 0;
- info.reserved[2] = 0;
- info.xoffset = 0;
- info.yoffset = 0;
- info.activate = FB_ACTIVATE_NOW;
- info.bits_per_pixel = 32;
- info.red.offset = 16;
- info.red.length = 8;
- info.green.offset = 8;
- info.green.length = 8;
- info.blue.offset = 0;
- info.blue.length = 8;
- info.transp.offset = 24;
- info.transp.length = 8;
- /*
- * Request NUM_BUFFERS screens (at lest 2 for page flipping)
- */
- info.yres_virtual = info.yres * NUM_BUFFERS; /* 帧缓冲总长度 */
- uint32_t flags = PAGE_FLIP; /* 支持缓冲交换 */
- if (ioctl(fd, FBIOPAN_DISPLAY, &info) == -1) {
- info.yres_virtual = info.yres;
- flags &= ~PAGE_FLIP;
- LOGW("FBIOPAN_DISPLAY 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;
- LOGW("page flipping not supported (yres_virtual=%d, requested=%d)",
- info.yres_virtual, info.yres*2);
- }
- if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
- return -errno;
- int refreshRate = 1000000000000000LLU /
- (
- uint64_t( info.upper_margin + info.lower_margin + info.yres )
- * ( info.left_margin + info.right_margin + info.xres )
- * info.pixclock
- ); /* 计算lcd刷新率 */
- if (refreshRate == 0) {
- /* bleagh, bad info from the driver */
- refreshRate = 60*1000; // 60 Hz
- }
- if (int(info.width) <= 0 || int(info.height) <= 0) {
- /* the driver doesn't return that information, default to 160 dpi */
- 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;
- LOGI( "using (fd=%d)n"
- "id = %sn"
- "xres = %d pxn"
- "yres = %d pxn"
- "xres_virtual = %d pxn"
- "yres_virtual = %d pxn"
- "bpp = %dn"
- "r = %2u:%un"
- "g = %2u:%un"
- "b = %2u:%un",
- fd,
- finfo.id,
- info.xres,
- info.yres,
- info.xres_virtual,
- info.yres_virtual,
- info.bits_per_pixel,
- info.red.offset, info.red.length,
- info.green.offset, info.green.length,
- info.blue.offset, info.blue.length
- );
- LOGI( "width = %d mm (%f dpi)n"
- "height = %d mm (%f dpi)n"
- "refresh rate = %.2f Hzn",
- info.width, xdpi,
- info.height, ydpi,
- fps
- );
- if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
- return -errno;
- if (finfo.smem_len <= 0)
- return -errno;
- module->flags = flags;
- module->info = info;
- module->finfo = finfo;
- module->xdpi = xdpi;
- module->ydpi = ydpi;
- module->fps = fps;
- /*
- * map the framebuffer
- */
- int err;
- size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual); /* 帧缓冲大小 */
- module->framebuffer = new private_handle_t(dup(fd), fbSize,
- private_handle_t::PRIV_FLAGS_USES_PMEM);
- module->numBuffers = info.yres_virtual / info.yres; /* 计算系统帧缓冲的个数 */
- module->bufferMask = 0;
- void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); /* 将fb映射到用户空间 */
- if (vaddr == MAP_FAILED) {
- LOGE("Error mapping the framebuffer (%s)", strerror(errno));
- return -errno;
- }
- module->framebuffer->base = intptr_t(vaddr); /* 帧缓冲的起始虚拟地址 */
- memset(vaddr, 0, fbSize);
- return 0;
- }
fb 模块最重要的工作就是将应用程序指定的内容写入显存中,是通过函数 fb_post 完成的,流程如下:
- /* 将图形缓冲区buffer的内容渲染到帧缓冲区中去 */
- static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)
- {
- unsigned int phys;
- void* virt;
- int pitch;
- int format;
- /* 首先验证参数handle指向的一块图形缓冲区的确是由Gralloc模块分配的 */
- if (private_handle_t::validate(buffer) < 0)
- return -EINVAL;
- fb_context_t* ctx = (fb_context_t*)dev;
- private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer); /* 图形缓冲区 */
- private_module_t* m = reinterpret_cast<private_module_t*>(dev->common.module); /* 帧缓冲区 */
- if (m->currentBuffer) { /* 当前正在渲染的图形缓冲区 */
- m->base.unlock(&m->base, m->currentBuffer);
- m->currentBuffer = 0;
- }
- if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { /* 如果图形缓冲区是在系统帧缓冲中分配的 */
- m->base.lock(&m->base, buffer, /* 锁定图像缓冲区 */
- private_module_t::PRIV_USAGE_LOCKED_FOR_POST,
- 0, 0, m->info.xres, m->info.yres, NULL);
- const size_t offset = hnd->base - m->framebuffer->base; /* 计算图形缓冲区与帧缓冲的偏移 */
- /* 将作为参数的fb_var_screeninfo结构体的成员变量activate的值设置FB_ACTIVATE_VBL
- * 表示要等到下一个垂直同步事件出现时,再将当前要渲染的图形缓冲区的内容绘制出来
- * 这样做的目的是避免出现屏幕闪烁,即避免前后两个图形缓冲区的内容各有一部分同时出现屏幕中 */
- m->info.activate = FB_ACTIVATE_VBL;
- m->info.yoffset = offset / m->finfo.line_length; /* 得到偏移的起始行 */
- if (ioctl(m->framebuffer->fd, FBIOPAN_DISPLAY, &m->info) == -1) { /* 刷新显示内容 */
- LOGE("FBIOPAN_DISPLAY failed");
- m->base.unlock(&m->base, buffer);
- return -errno;
- }
- if (UNLIKELY(mDebugFps)) {
- debugShowFPS();
- }
- #ifdef SNAPSHOT
- dumpfile((void*)hnd->base, m->info.xres, m->info.yres); /* dump帧缓冲 */
- #endif
- m->currentBuffer = buffer; /* 设置当前图形缓冲区 */
- } else { /* 如果图形缓冲区是在内存中分配的 */
- /* If we can't do the page_flip, just copy the buffer to the front
- * FIXME: use copybit HAL instead of memcpy */
- LOGD("copy bit.n");
- void* fb_vaddr;
- void* buffer_vaddr;
- /* 将帧缓冲整个锁定 */
- m->base.lock(&m->base, m->framebuffer,
- GRALLOC_USAGE_SW_WRITE_RARELY,
- 0, 0, m->info.xres, m->info.yres,
- &fb_vaddr);
- /* 将图形缓冲整个锁定 */
- m->base.lock(&m->base, buffer,
- GRALLOC_USAGE_SW_READ_RARELY,
- 0, 0, m->info.xres, m->info.yres,
- &buffer_vaddr);
- /* 将图形缓冲的内容拷贝到帧缓冲中即显示到lcd */
- memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);
- m->base.unlock(&m->base, buffer); /* 解锁 */
- m->base.unlock(&m->base, m->framebuffer);
- }
- return 0;
- }
最后
以上就是追寻西牛为你收集整理的android的gralloc分析的全部内容,希望文章能够帮你解决android的gralloc分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复