转android gralloc流程分析for msm8960
2014-02-17 20:42
393 查看
原文转载自/article/1761467.html
增加了Gralloc模块的平台背景和功能概述部分。
对原文针对msm8960 android display做了修正。
增加了Surfaceflinger初始化FrameBufferNativeWindow的代码部分。
平台中内存有ashmen、PMEM等多种内存类型,为了Video、Graphics、GPU内存访问的需要,android引入Gralloc模块实现内存的管理。Gralloc把FrameBuffer的分配也纳入了其中,并且新引入ION做为Gralloc的非FrameBuffer内存的分配器。ION对于内核态内存在用户进程之间的访问和硬件平台模块之间数据流转提供了高效的解决方案。
Android 中 lcd 是一个帧缓冲设备,驱动程序通过处理器的 lcd 控制器将物理内存的一段区域设置为显存,如果向这段内存区域写入数据就会马上在 lcd 上显示出来。Android 在 HAL 中提供了gralloc 模块,封装了用户层对帧缓冲设备的所有操作接口,并通过 SurfaceFlinger 服务向应用提供显示支持。在启动过程中系统会加载 gralloc 模块,然后打开帧缓冲设备,获取设备的各种参数并完成 gralloc 模块的初始化。当应用程序需要把内容显示到
lcd 上时,需要通过 gralloc 模块申请一块图形缓冲区,然后将这块图形缓冲区映射到自己的地址空间并写入内容即可。当应用程序不再需要这块图形缓冲区时需要通过 gralloc 模块释放掉,然后解除对缓冲区的映射。
1、基础数据结构
gralloc 模块通过 struct private_module_t 来描述,该结构定义如下:
[cpp] view
plaincopy
<span style="font-size:12px;">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 */
};
};</span>
该结构的成员记录了 gralloc 模块的各种参数,主要为模块自己使用,应用程序操作的图形缓冲区的数据结构是struct private_handle_t,定义如下:
[cpp] view
plaincopy
<span style="font-size:12px;">#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
};</span>
图形缓冲区的操作接口由结构 struct gralloc_module_t 定义:
[cpp] view
plaincopy
<span style="font-size:12px;">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;</span>
gralloc 设备则用结构 struct alloc_device_t 来描述,其定义如下:
[cpp] view
plaincopy
<span style="font-size:12px;">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;</span>
帧缓冲设备则采用结构 struct framebuffer_device_t 描述:
[cpp] view
plaincopy
<span style="font-size:12px;">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;</span>
其中成员函数 post对应用程序来说是最重要的接口,它将完成数据写入显存的工作。
2、gralloc 模块
HAL 中通过 hw_get_module 接口加载指定 id 的模块,并获得一个 hw_module_t 结构来打开设备,流程如下:
[cpp] view
plaincopy
<span style="font-size:12px;">#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;
}</span>
可以看出,是使用id和系统平台的名字组合出so的文件名,去设定的目录动态加载该库文件然后解析特定符号,找到hw_module_t object。
函数会在 /system/lib/hw 或者 /vendor/lib/hw 目录中去寻找gralloc.xxx.so 文件,如果找到了就调用load接口完成加载。
最终会调用 gralloc_device_open完成 gralloc 设备成员的初始化:
[cpp] view
plaincopy
<span style="font-size:12px;">int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
98 int status = -EINVAL;
99 if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
100 const private_module_t* m = reinterpret_cast<const private_module_t*>(
101 module);
102 gpu_context_t *dev;
103 IAllocController* alloc_ctrl = IAllocController::getInstance();
104 dev = new gpu_context_t(m, alloc_ctrl);
105 *device = &dev->common;
106 status = 0;</span>
[cpp] view
plaincopy
<span style="font-size:12px;"> } else {
status = fb_device_open(module, name, device);
}
return status;
}</span>
可以认为Gralloc module中有两个设备gpu_alloc_device和fb_device,前者用于分配GPU0使用的内存和FB内存,GPU0内存管理使用ION allocator;后者用于获取分配Framebuffer Info并操作fb。
在 android 系统中,所有的图形缓冲区都是由 SurfaceFlinger服务分配的,在系统帧缓冲区中分配的图形缓冲区只在 SurfaceFlinger 服务中使用,而在内存中分配的图形缓冲区既可以在 SurfaceFlinger 服务中使用,也可以在其它的应用程序中使用,当应用程序请求 SurfaceFlinger 服务分配图形缓冲区时会发生两次映射:服务所在的进程首先会将申请到的缓冲区映射至服务的地址空间,然后应用程序使用这个图形缓冲时再将其映射至应用程序的地址空间。分配函数的实现如下:
[cpp] view
plaincopy
<span style="font-size:12px;">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)
{
127 int err = 0;
128 int flags = 0;
129 size = roundUpToPageSize(size);
130 alloc_data data;
131 data.offset = 0;
132 data.fd = -1;
133 data.base = 0;
134 data.size = size;
135 if(format == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED)
136 data.align = 8192;
137 else
138 data.align = getpagesize();
139 data.pHandle = (unsigned int) pHandle;
140 err = mAllocCtrl->allocate(data, usage);
141
142 if (!err) {
143 /* allocate memory for enhancement data */
144 alloc_data eData;
145 eData.fd = -1;
146 eData.base = 0;
147 eData.offset = 0;
148 eData.size = ROUND_UP_PAGESIZE(sizeof(MetaData_t));
149 eData.pHandle = data.pHandle;
150 eData.align = getpagesize();
151 int eDataUsage = GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP;
152 int eDataErr = mAllocCtrl->allocate(eData, eDataUsage);
153 ALOGE_IF(eDataErr, "gralloc failed for eData err=%s", strerror(-err));
154
155 if (usage & GRALLOC_USAGE_PRIVATE_UNSYNCHRONIZED) {
156 flags |= private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED;
157 }
158
159 if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_ONLY) {
160 flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_ONLY;
161 //The EXTERNAL_BLOCK flag is always an add-on
162 if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_BLOCK) {
163 flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_BLOCK;
164 }
165 if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_CC) {
166 flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_CC;
167 }
168 }
169
170 flags |= data.allocType;
171 int eBaseAddr = int(eData.base) + eData.offset;
172 private_handle_t *hnd = new private_handle_t(data.fd, size, flags,
173 bufferType, format, width, height, eData.fd, eData.offset,
174 eBaseAddr);
175
176 hnd->offset = data.offset;
177 hnd->base = int(data.base) + data.offset;
178 *pHandle = hnd;
179 }
180
181 ALOGE_IF(err, "gralloc failed err=%s", strerror(-err));
182
183 return err;
184}
/*****************************************************************************/
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;
}</span>
3、gpu_alloc 模块
gpu0内存即非HW_FB内存使用ION分配器进行分配,此文不做详述。
4、fb 模块
在 gralloc_device_open 中会根据传递的参数分别初始化两个设备,定义如下:
[cpp] view
plaincopy
<span style="font-size:12px;">#define GRALLOC_HARDWARE_FB0 "fb0"
#define GRALLOC_HARDWARE_GPU0 "gpu0"</span>
如果参数不是 "gpu0" ,那么是"fb%u"的形式,则会调用fb_device_open 初始化 fb 设备,主要流程和打开 gralloc 基本一致,在函数中会通过调用 mapFrameBuffer->mapFrameBufferLocked 获取帧缓存设备的参数并将其设备节点映射到用户空间,流程如下(大致如此,msm8960平台代码有所变化,msm平台上fb设备文件名是/dev/graphics/fb%u):
[cpp] view
plaincopy
<span style="font-size:12px;">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 = %s\n"
"xres = %d px\n"
"yres = %d px\n"
"xres_virtual = %d px\n"
"yres_virtual = %d px\n"
"bpp = %d\n"
"r = %2u:%u\n"
"g = %2u:%u\n"
"b = %2u:%u\n",
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 Hz\n",
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;
}</span>
关于fb设备的打开和HW_FB内存的分配,是在FrameBufferNativeWindow的构造代码中,可以看到打开fb0设备获取Framebuffer Info,然后使用gralloc为该FrameBufferNativeWindow分配两个HW_FB内存即Framebuffer,即每个Window,double buffer。代码如下:
[cpp] view
plaincopy
<span style="font-size:12px;">62/*
63 * This implements the (main) framebuffer management. This class is used
64 * mostly by SurfaceFlinger, but also by command line GL application.
65 *
66 * In fact this is an implementation of ANativeWindow on top of
67 * the framebuffer.
68 *
69 * Currently it is pretty simple, it manages only two buffers (the front and
70 * back buffer).
71 *
72 */
73
74FramebufferNativeWindow::FramebufferNativeWindow()
75 : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
76{
77 hw_module_t const* module;
78 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
79 int stride;
80 int err;
81 int i;
82 err = framebuffer_open(module, &fbDev);
83 ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
84
85 err = gralloc_open(module, &grDev);
86 ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));
87
88 // bail out if we can't initialize the modules
89 if (!fbDev || !grDev)
90 return;
91
92 mUpdateOnDemand = (fbDev->setUpdateRect != 0);
93
94 // initialize the buffer FIFO
95 if(fbDev->numFramebuffers >= MIN_NUM_FRAME_BUFFERS &&
96 fbDev->numFramebuffers <= MAX_NUM_FRAME_BUFFERS){
97 mNumBuffers = fbDev->numFramebuffers;
98 } else {
99 mNumBuffers = MIN_NUM_FRAME_BUFFERS;
100 }
101 mNumFreeBuffers = mNumBuffers;
102 mBufferHead = mNumBuffers-1;
103
104 /*
105 * This does not actually change the framebuffer format. It merely
106 * fakes this format to surfaceflinger so that when it creates
107 * framebuffer surfaces it will use this format. It's really a giant
108 * HACK to allow interworking with buggy gralloc+GPU driver
109 * implementations. You should *NEVER* need to set this for shipping
110 * devices.
111 */
112#ifdef FRAMEBUFFER_FORCE_FORMAT
113 *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT;
114#endif
115
116 for (i = 0; i < mNumBuffers; i++)
117 {
118 buffers[i] = new NativeBuffer(
119 fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
120 }
121
122 for (i = 0; i < mNumBuffers; i++)
123 {
124 err = grDev->alloc(grDev,
125 fbDev->width, fbDev->height, fbDev->format,
126 GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);
127
128 ALOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s",
129 i, fbDev->width, fbDev->height, strerror(-err));
130
131 if (err)
132 {
133 mNumBuffers = i;
134 mNumFreeBuffers = i;
135 mBufferHead = mNumBuffers-1;
136 break;
137 }
138 }
139
140 const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags;
141 const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
142 const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
143 const_cast<int&>(ANativeWindow::minSwapInterval) =
144 fbDev->minSwapInterval;
145 const_cast<int&>(ANativeWindow::maxSwapInterval) =
146 fbDev->maxSwapInterval;
147 } else {
148 ALOGE("Couldn't get gralloc module");
149 }
150
151 ANativeWindow::setSwapInterval = setSwapInterval;
152 ANativeWindow::dequeueBuffer = dequeueBuffer;
153 ANativeWindow::lockBuffer = lockBuffer;
154 ANativeWindow::queueBuffer = queueBuffer;
155 ANativeWindow::query = query;
156 ANativeWindow::perform = perform;
157 ANativeWindow::cancelBuffer = NULL;
158}</span>
创建FrameBufferNativeWindow仅发生在DisplayHardware的构造后初始化中,代码片段如下:
[cpp] view
plaincopy
150void DisplayHardware::init(uint32_t dpy)
151{
152 mNativeWindow = new FramebufferNativeWindow(); //******
153 framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
154 if (!fbDev) {
155 ALOGE("Display subsystem failed to initialize. check logs. exiting...");
156 exit(0);
157 }
158
159 int format;
160 ANativeWindow const * const window = mNativeWindow.get();
161 window->query(window, NATIVE_WINDOW_FORMAT, &format);
162 mDpiX = mNativeWindow->xdpi;
163 mDpiY = mNativeWindow->ydpi;
164 mRefreshRate = fbDev->fps;
....
}
而DisplayHardware的真正构造仅在Surfacelinger启动后readyToRun中,其余都是使用拷贝构造默认的Bitwise Copy,当然这仅仅是针对一块屏的情况,当前大屏主流。相关代码片段如下:
[cpp] view
plaincopy
217status_t SurfaceFlinger::readyToRun()
218{
219 ALOGI( "SurfaceFlinger's main thread ready to run. "
220 "Initializing graphics H/W...");
221
222 // we only support one display currently
223 int dpy = 0;
224
225 {
226 // initialize the main display
227 GraphicPlane& plane(graphicPlane(dpy));
228 DisplayHardware* const hw = new DisplayHardware(this, dpy); //*******
229 plane.setDisplayHardware(hw);
230 }
231
232 // create the shared control-block
233 mServerHeap = new MemoryHeapBase(4096,
234 MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap");
235 ALOGE_IF(mServerHeap==0, "can't create shared memory dealer");
236
237 mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase());
238 ALOGE_IF(mServerCblk==0, "can't get to shared control block's address");
239
240 new(mServerCblk) surface_flinger_cblk_t;
241
242 // initialize primary screen
243 // (other display should be initialized in the same manner, but
244 // asynchronously, as they could come and go. None of this is supported
245 // yet).
246 const GraphicPlane& plane(graphicPlane(dpy));
247 const DisplayHardware& hw = plane.displayHardware();
248 const uint32_t w = hw.getWidth();
249 const uint32_t h = hw.getHeight();
250 const uint32_t f = hw.getFormat();
251 hw.makeCurrent();
.....
}
fb 模块最重要的工作就是将应用程序指定的内容写入显存中,是通过函数 fb_post完成的,流程如下(msm8960代码大致如此,不过使用的是FBIOPUT_VSCREENINFO IOCTL_CODE):
[cpp] view
plaincopy
<span style="font-size:12px;">/* 将图形缓冲区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();
}
m->currentBuffer = buffer; /* 设置当前图形缓冲区 */
return 0;
}</span>
5、Gralloc map/unmap、register/unregister
当GPU内存file descriptor从一个进程A传递到另一个进程B后,进程B做gralloc_register_buffer就是使用allocator此时是ION将该buffer在本进程映射一下,用于访问。这些功能做为gralloc的mapper功能。
关于内存file descriptor、binder传递该内存fd的具体机制参见ION的功能。
Overlay & HWC on MDP -- MIMO Display软硬整合
概述
Android显示系统SurfaceFlinger使用Overlay和HWC(Hardware composer)完成Surface
Layer的硬件合成。Overlay和HWC表现为两个HAL,为芯片方案制造商留了实现余地。
因为Overlay也时常被称为hardware composition,为了避免混淆,本文中Overlay专指Android
Display Overlay HAL,对应liboverlay.so;HWC专指SurfaceFlinger使用的硬件合成器HAL,对应hwcomposer.msm8xxxx.so。
Qualcomm MSM8k系列平台的MDP4.x(Mobile Display Platform)提供了硬件Overlay的功能,Android
Overlay HAL与这个Overlay硬件相对应;当然,为了与上段照应,需要说明的是,MDP并不存在一个实现HWC的专门硬件,HWC也是在Overlay上实现的;体现在Android软件中,HWC
HAL是建立在Overlay HAL基础上的,是使用Overlay HAL来实现的。
因此,再次,准确地说,提供硬件合成功能的是模块是Overlay,是MDP Overlay硬件的体现;而HWC则是SurfaceFlinger使用Overlay
HAL的一个桥梁。
Overlay并非仅仅由SurfaceFlinger得到Surface后就不再经过SurfaceFlinger而使用硬件直接输出;从函数调用上看似乎是这样的,但是实际情形却非如此。硬件合成仅是合成的一种手段,合成的结果输出destination还是必须受控,因而也必须纳入SurfaceFlinger的输出管理范畴。
Overlay硬件平台
MSM8k上目前有MDP4.0, MDP4.1, MDP4.2三个硬件版本,主要是合成能力和管道数目的区别。通常,一个RGB pipe和一个VG
pipe组成一个pipe pair,前者是对应UI RGB图像数据,后者对应Camera或Video的RGB或YUV数据,两个pipe输入到一个LayerMixer0用于合成;额外的,LayerMixer可能还有BF(Border
Fill) pipe,用于视频按比例显示时屏幕多余边框填充,这个多用于Ext TV或HDMI输出的时候。MDP4有7个pipe,3个LayerMixer,其中LayerMix0可以配置有多达两个RGB
pipe,两个VG pipe,一个BF pipe输入,完成5 Layer合成。
上述pipe是LayerMixer的输入元素,LayerMixer的输出对应到LCD,TV,HDMI等,当然不是直接对应,而是由DMA
channel和上述模块的控制器相连。三个LayerMixer对应的三个输出使用一般是约定的,当然,软件层面上MDP driver中对每个管道的目标LayerMixer也做了固定的配置。三个输出一般标为Primary,
Seconday, Extra,对应的DMA通道为DMA_P, DMA_S, DMA_E。
更直观地,参见Android Overlay on QC MDP4平台要点简记
由于LayerMixer0提供了多达5个Layer的合成能力,所以当没有Camera或Video优先使用它的时候,它被充分利用来做Layer的Hardware
composition。提前且概要地,这儿必须清楚说明的是,LayerMixer0使用的两种情景:
当有Camera或Video的时候,SurfaceView对应的Layer为HWC_OVERLAY,这时该Layer对应一个VG
pipe,而其余HWC_FRAMEBUFFER Layer经3D GPU render到Framebuffer后,该Framebuffer输入一个pipe(RGB1--base
layer?),和VG pipe经LayerMixer0合成输出。
当没有Camera或Video的时候,如果UI Layer即HWC_FRAMEBUFFER Layer小于等于3个且都满足图像格式条件,那么这些Layer的CompositionType属性会被修改为HWC_OVERLAY,为每个Layer分配pipe,经LayerMixer0合成经DMA_P输出,这就是HWC。由于BF
pipe能力条件的限制,不使用其做HWC,而RGB1做预留的base layer Framebuffer使用,所以标榜的4-layer mdp composition support实际只能接受SurfceFlinger的3个Layer做合成,也就是SurfaceFlinger的属性debug.mdpcomp.maxlayer=3。当超过3个Layer的时候,由于管道数量的限制,不能够再使用LayerMixer0,就使用GPU
render,也即每个Layer->draw。当然GPU render也算是一个Hardware Composition,CompositionType方式的其中之一就是GPU。
Overlay HAL结构
MIMO输入输出代码结构设计,简单地讲,分为流路控制和数据流动两部分;Overlay及构建在其上的hwcomposer都是采用这种流路控制和数据流动的结构。
Overlay有多种应用场景,具体设计上描述为Overlay State。就是说如果允许管道任意灵活组合使用的话,可以有很多种花样的应用,但是这儿预先设计好这么些应用场景,应用场景一定,需要的管道类型和数目随之确定,管道连接的LayerMixer及其输出Video驱动控制器也确定。
Overlay State具体实现使用模板类OverlayImpl<P0, P1, P2>,P0, P1, P2是3个pipe,最多可以使用3个pipe。每个pipe对应的类是模板类GenericPipe<int
PANEL>,PANEL指定该pipe合成后图像输出目的,从而也决定其使用的LayerMixer。另外地,每个OverlayImpl<P0, P1, P2>模板类的3个管道可能需要图像旋转,所以有可能使用3个Rotator。此处的P0,
P1, P2只是逻辑索引,并不具体是MDP的管道索引,根据使用规格需求,从MDP管道中为之动态分配。
hwcomposer也设计了几个封装类与Overlay应用场景对应,屏蔽了场景功能的具体实现。hwcomposer中定义的应用场景比Overlay提供的场景还要少,仅是有限几个。大致结构图如下:
由于overlay场景太多,因此只示意了四个,OV_BYPASS_x_LAYER可以是1,2,3个,分别对应1个,2个或3个HWC_FRAMEBUFFER
Layer和base layer Framebuffer合成的情形。
hwc_prepare的设计逻辑过于固化和简单,成了一种基于if-else判断的优先配置方式,宽阔的城堡顶部建了个鸟笼似的小阁楼;应该设计成Policy或RouteState的形式更具备可扩展性。
Overlay进行流路控制的接口是commit,然后调用其具体实现类和MDP封装类的相关接口,一个调用栈(适当向内核驱动做了扩展)如下:
mdp4_overlay_set()
msmfb_overlay_set()
ioctl(fb, MSMFB_OVERLAY_SET)
overlay::mdp_wrapper::setOverlay()
MdpCtrl::set()
Ctrl::commit()
pipe::commit() -> GernericPipe<utils::PRIMARY>::commit()
OverlayImplBase::commit() -> OverlayImpl<>::commit()
Overlay::commit(overlay::utils::eDest)
Overlay提交数据的接口是queueBuffer,然后同样调用其具体实现类和MDP封装类的相关接口,一个调用栈(适当向内核驱动做了扩展)如下:
mdp4_overlay_play()
msmfb_overlay_play()
ioctl(fd, MSMFB_OVERLAY_PLAY, &od) msmfb_overlay_data.id is mdp_overlay.id is the kernel pipe index in kernel db returned by MdpCtrl.commit()
overlay::mdp_wrapper::play()
MdpData::play(int fd, uint32_t offset)
Data::play()
GenericPipe<PANEL>::queueBuffer(int fd, uint32_t offset)
OverlayImplBase::queueBuffer() -> OverlayImpl<>::(int fd, uint32_t offset, utils::eDest dest)
Overlay::queueBuffer(int fd, uint32_t offset, utils::eDest dest)
Camera或Video Overlay
Camera Overlay一般有两种实现模式,一种模式是V4L2不提供Overlay接口,在用户态把V4L2 buffer handle传递给Framebuffer,由Framebuffer
Overlay Implementation完成合成;另一种模式是V4L2提供Overlay接口,可以把Video buffer在V4L2内核驱动中提交给Framebuffer
Overlay内核驱动完成合成。QC MSM8k Overlay是采用的第一种模式实现,但是V4L2和Framebuffer驱动也提供了第二种模式的实现;第二种模式可参见Freescale处理器上Android
Overlay的实现。
Camera的取景器PreviewDisplay Window是个SurfaceView,所在的Layer(HWC_OVERLAY)在UI
Layer底下,但是UI layer会开个全透的区域露出这个HWC_OVERLAY Layer。
下面描述其合成过程。
3D GPU 绘制:所有的HWC_FRAMEBUFFER layers会首先经OpenGL 3D GPU绘制到back Framebuffer上;
OV_PIPE0配置:然后在setupHardwareComposer=> HWComposer::prepare=>hwc_prepare=>VideoOverlay::prepare中使用HWC_OVERLAY Layer参数设置OV_PIPE0分配的类型为Video/Graphic的MDP
pipe;
OV_PIPE0提交:DisplayHardware::flip=>HWComposer::commit=>hwc_set=>VideoOverlay::draw提交HWC_OVERLAY Layer的图像数据到OV_PIPE0的内核VG
pipe的工作队列中(单容量队列即可);
OV_PIPE3配置:DisplayHardware::flip => HWComposer::commit=> hwc_set=>eglSwapBuffers=> queueBuffer=> fb_post=>update_framebuffer中使用base
layer back framebuffer参数配置OV_PIPE3即RGB1 pipe(注意base layer的特殊地位);
OV_PIPE3提交:当update_framebuffer真正提交图像数据到RGB1 pipe时,对应stage 0 for base layer(内核驱动)会启动MDP
LayerMixer0进行Layers合成;
DMA传输:然后fb_post通过FBIOPUT_VSCREENINFO或PAN_DISPLAY启动DMA_P送合成好的图像经MIPI-DSI输出显示。
BufferFlip:queueBuffer后,该framebuffer即为front framebuffer,然后eglSwapBuffers会dequeueBuffer下一个Framebuffer做为back
framebuffer。
此时Hardware Composer使用的是VideoOverlay@hwcomposer.msm8960.so对应的逻辑。VG pipe设置和提交的栈示例如下:
/system/lib/liboverlay.so (overlay::Overlay::commit(overlay::utils::eDest)+71)
/system/lib/liboverlay.so configPrimVid(hwc_context_t *ctx, hwc_layer_t *layer)
/system/lib/liboverlay.so VideoOverlay::configure(hwc_context_t *ctx, hwc_layer_t *, hwc_layer_t *)
/system/lib/hw/hwcomposer.msm8960.so (qhwc::VideoOverlay::prepare(hwc_context_t *, hwc_layer_list_t *)+xxx)
/system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::setup(hwc_context_t*, hwc_layer_list*)+163) for each pipe.
/system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::configure(hwc_composer_device*, hwc_layer_list*)+59)
/system/lib/hw/hwcomposer.msm8960.so hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list)
/system/lib/libsurfaceflinger.so (android::HWComposer::prepare() const+9)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::setupHardwareComposer()+127)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::handleRepaint()+147)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+91)
/system/lib/liboverlay.so Overlay::queueBuffer(int fd, uint32_t offset, utils::eDest dest)
/system/lib/hw/hwcomposer.msm8960.so (qhwc::VideoOverlay::draw(hwc_context_t*, hwc_layer_list*)+xx) for each pipe.
/system/lib/hw/hwcomposer.msm8960.so static int hwc_set(hwc_composer_device_t *dev,hwc_display_t dpy,hwc_surface_t sur, hwc_layer_list_t* list)
/system/lib/libsurfaceflinger.so (android::HWComposer::commit() const+11)
/system/lib/libsurfaceflinger.so (android::DisplayHardware::flip(android::Region const&) const+59)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::postFramebuffer()+61)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+103)
Base layer对应的RGB pipe设置和提交的栈示例从略。Base layer提交到RGB1时真正启动LayerMixer0的4-stage
layer composition。
FB Layer Hardware Composition
当开启硬件(debug.sf.hw=1)合成且合成方式选择MDP时,属性debug.mdpcomp.maxlayer决定了可以使用的通道的个数,当前设置为3,因为RGB1
pipe预留为Camera或Video时的base layer使用,在全Framebuffer
layer MDP合成情景下,RGB1对应的base layer是一个处于最顶端的全透明层。相关系统设置和属性如下:
在系统根目录文件 /systembuild.prop中,
debug.sf.hw=1
debug.egl.hw=1
debug.composition.type=mdp
debug.enable.wl_log=1
debug.mdpcomp.maxlayer=3
debug.mdpcomp.logs=0
其中CompositionType有 4种,debug.composition.type=dyn/c2d/mdp/cpu/gpu,dyn是根据c2d和mdp的硬件存在选择使用二者之一。
gpu/c2d/mdp可以释放cpu/gpu,减轻cpu/gpu的负担,但并不一定能使显示效果流畅。
SurfaceFlinger启动时会取debug.mdpcomp.maxlayer属性值记录在全局变量sMaxLayers中。相关代码如下:
782 sMaxLayers = 0;
783 if(property_get("debug.mdpcomp.maxlayer", property, NULL) > 0) {
784 if(atoi(property) != 0)
785 sMaxLayers = atoi(property);
786 }
当没有Camera/Video/HDMI/WFD等Overlay应用情景时,所有Layer都是HWC_FRAMEBUFFER的,MDP的LayerMixer0是闲置的,这时可以优化利用其来做Framebuffer
Layer合成。由于管道数目限制的原因,只能合成小于等于sMaxLayers个Layers。多于3个的时候是否可以MDP合成其中的3个?可能需要考虑Layer
buffer维度、格式、缩放、Z序等因素。当多于3个的时候,是遍历layer::draw使用GPU来绘制纹理到back
framebuffer上的。
下面着重看少于等于3个Layer,MDP合成的情况。
首先,所有的Layer的compositiontype都是HWC_FRAMEBUFFER的;
然后SurfaceFlinger在setHardwareComposer时发现hwc_prepare没有别的优先的Overlay情景,最后的一个if分支就是使用MDP来做Layer合成;SurfaceFlinger会检查Layer的属性看是否满足使用MDP的条件,然后设置满足条件的Layer的属性[compositionType,
flags] = (HWC_OVERLAY, MDPCOMP);这样SurfaceFlinger::composeSurfaces时,就不再通过layer::draw使用GPU来绘制;
PIPE配置:SurfaceFlinger为HWC创建工作集,为每个Layer分配并使用MDPComp::prepare配置每个pipe,如果有缩放需求,则会分配VG
pipe,因为RGB pipe不支持缩放;有一个Layer则Overlay状态为BYPASS_1_LAYER,表示有1个Layer
Bypass,不需要OpenGL绘制,同理2个Layer为BYPASS_2_LAYER,3个为BYPASS_3_LAYER;
PIPE提交:在DisplayHardware::flip中提交每个Layer的数据到管道,即MDPComp::draw() => Overlay::queueBuffer()。注意Layer图像数据是在PMEM/ASHMEM内存中而不是Framebuffer内存中,但仍物理连续或IOMMU映射连续,LayerMixerdraw可访问。MDPComp::draw完即提交数据到对应管道的单容量队列后,清layer->flags
&= ~HWC_MDPCOMP标志;
在eglSwapBuffers时,真正做back & front Buffer的切换(准确地说双缓冲是切换,三缓冲是轮用)
OV_PIPE3配置:DisplayHardware::flip => HWComposer::commit=> hwc_set=>eglSwapBuffers=> queueBuffer=> fb_post=>update_framebuffer中使用base
layer back framebuffer参数配置OV_PIPE3即RGB1 pipe;
OV_PIPE3提交:当update_framebuffer真正提交图像数据到RGB1 pipe时,对应stage 0 for base layer(内核驱动)会启动MDP
LayerMixer0进行Layers合成;此时base layer是个最顶层的全透明Layer,不妨碍底层Layer的显示;
DMA传输:然后fb_post通过FBIOPUT_VSCREENINFO或PAN_DISPLAY启动DMA_P送合成好的图像经MIPI-DSI输出显示。
BufferFlip:queueBuffer后,该framebuffer即为front framebuffer,然后eglSwapBuffers会dequeueBuffer下一个Framebuffer做为back
framebuffer。
HWC功能时,是MDPComp@hwcomposer.msm8960.so对应的逻辑,一个Layer对应的 pipe设置和提交的栈示例如下:
#00 pc 0000b0d0 /system/lib/liboverlay.so (overlay::Overlay::commit(overlay::utils::eDest)+71)
#01 pc 00005d16 /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::prepare(hwc_context_t*, hwc_layer*, qhwc::MDPComp::mdp_pipe_info&)+577)
#02 pc 00006190 /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::setup(hwc_context_t*, hwc_layer_list*)+163) for each pipe.
#03 pc 0000652c /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::configure(hwc_composer_device*, hwc_layer_list*)+59)
#04 pc 000037ea /system/lib/hw/hwcomposer.msm8960.so hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list)
#05 pc 0001ebba /system/lib/libsurfaceflinger.so (android::HWComposer::prepare() const+9)
#06 pc 00020df4 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::setupHardwareComposer()+127)
#07 pc 000212f4 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::handleRepaint()+147)
#08 pc 000222e8 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+91)
/system/lib/liboverlay.so Overlay::queueBuffer(int fd, uint32_t offset, utils::eDest dest)
#00 pc 00006592 /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::draw(hwc_context_t*, hwc_layer_list*)+85) for each pipe.
#01 pc 00003ae4 /system/lib/hw/hwcomposer.msm8960.so static int hwc_set(hwc_composer_device_t *dev,hwc_display_t dpy,hwc_surface_t sur, hwc_layer_list_t* list)
#02 pc 0001ed56 /system/lib/libsurfaceflinger.so (android::HWComposer::commit() const+11)
#03 pc 0001e57c /system/lib/libsurfaceflinger.so (android::DisplayHardware::flip(android::Region const&) const+59)
#04 pc 000209c6 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::postFramebuffer()+61)
#05 pc 00022474 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+103)
Base layer对应的RGB pipe设置和提交的栈示例从略。重复地,Base layer提交到RGB1时真正启动LayerMixer0的4-stage
layer composition。
HDMI / WFD
有机会待续。
LayerMixer0合成后图像经DMA_P输出时,在BLT模式和DIRECTOUT模式下LayerMixer0与DMA之间没有buffer;即使FB模式,也是一个Overlay
buffer而未必是做为Framebuffer内存,当然这个Overlay buffer做为DMA_P输入传输给MIPI-DSI/LCDC。
增加了Gralloc模块的平台背景和功能概述部分。
对原文针对msm8960 android display做了修正。
增加了Surfaceflinger初始化FrameBufferNativeWindow的代码部分。
平台中内存有ashmen、PMEM等多种内存类型,为了Video、Graphics、GPU内存访问的需要,android引入Gralloc模块实现内存的管理。Gralloc把FrameBuffer的分配也纳入了其中,并且新引入ION做为Gralloc的非FrameBuffer内存的分配器。ION对于内核态内存在用户进程之间的访问和硬件平台模块之间数据流转提供了高效的解决方案。
Android 中 lcd 是一个帧缓冲设备,驱动程序通过处理器的 lcd 控制器将物理内存的一段区域设置为显存,如果向这段内存区域写入数据就会马上在 lcd 上显示出来。Android 在 HAL 中提供了gralloc 模块,封装了用户层对帧缓冲设备的所有操作接口,并通过 SurfaceFlinger 服务向应用提供显示支持。在启动过程中系统会加载 gralloc 模块,然后打开帧缓冲设备,获取设备的各种参数并完成 gralloc 模块的初始化。当应用程序需要把内容显示到
lcd 上时,需要通过 gralloc 模块申请一块图形缓冲区,然后将这块图形缓冲区映射到自己的地址空间并写入内容即可。当应用程序不再需要这块图形缓冲区时需要通过 gralloc 模块释放掉,然后解除对缓冲区的映射。
1、基础数据结构
gralloc 模块通过 struct private_module_t 来描述,该结构定义如下:
[cpp] view
plaincopy
<span style="font-size:12px;">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 */
};
};</span>
该结构的成员记录了 gralloc 模块的各种参数,主要为模块自己使用,应用程序操作的图形缓冲区的数据结构是struct private_handle_t,定义如下:
[cpp] view
plaincopy
<span style="font-size:12px;">#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
};</span>
图形缓冲区的操作接口由结构 struct gralloc_module_t 定义:
[cpp] view
plaincopy
<span style="font-size:12px;">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;</span>
gralloc 设备则用结构 struct alloc_device_t 来描述,其定义如下:
[cpp] view
plaincopy
<span style="font-size:12px;">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;</span>
帧缓冲设备则采用结构 struct framebuffer_device_t 描述:
[cpp] view
plaincopy
<span style="font-size:12px;">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;</span>
其中成员函数 post对应用程序来说是最重要的接口,它将完成数据写入显存的工作。
2、gralloc 模块
HAL 中通过 hw_get_module 接口加载指定 id 的模块,并获得一个 hw_module_t 结构来打开设备,流程如下:
[cpp] view
plaincopy
<span style="font-size:12px;">#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;
}</span>
可以看出,是使用id和系统平台的名字组合出so的文件名,去设定的目录动态加载该库文件然后解析特定符号,找到hw_module_t object。
函数会在 /system/lib/hw 或者 /vendor/lib/hw 目录中去寻找gralloc.xxx.so 文件,如果找到了就调用load接口完成加载。
最终会调用 gralloc_device_open完成 gralloc 设备成员的初始化:
[cpp] view
plaincopy
<span style="font-size:12px;">int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
98 int status = -EINVAL;
99 if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
100 const private_module_t* m = reinterpret_cast<const private_module_t*>(
101 module);
102 gpu_context_t *dev;
103 IAllocController* alloc_ctrl = IAllocController::getInstance();
104 dev = new gpu_context_t(m, alloc_ctrl);
105 *device = &dev->common;
106 status = 0;</span>
[cpp] view
plaincopy
<span style="font-size:12px;"> } else {
status = fb_device_open(module, name, device);
}
return status;
}</span>
可以认为Gralloc module中有两个设备gpu_alloc_device和fb_device,前者用于分配GPU0使用的内存和FB内存,GPU0内存管理使用ION allocator;后者用于获取分配Framebuffer Info并操作fb。
在 android 系统中,所有的图形缓冲区都是由 SurfaceFlinger服务分配的,在系统帧缓冲区中分配的图形缓冲区只在 SurfaceFlinger 服务中使用,而在内存中分配的图形缓冲区既可以在 SurfaceFlinger 服务中使用,也可以在其它的应用程序中使用,当应用程序请求 SurfaceFlinger 服务分配图形缓冲区时会发生两次映射:服务所在的进程首先会将申请到的缓冲区映射至服务的地址空间,然后应用程序使用这个图形缓冲时再将其映射至应用程序的地址空间。分配函数的实现如下:
[cpp] view
plaincopy
<span style="font-size:12px;">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)
{
127 int err = 0;
128 int flags = 0;
129 size = roundUpToPageSize(size);
130 alloc_data data;
131 data.offset = 0;
132 data.fd = -1;
133 data.base = 0;
134 data.size = size;
135 if(format == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED)
136 data.align = 8192;
137 else
138 data.align = getpagesize();
139 data.pHandle = (unsigned int) pHandle;
140 err = mAllocCtrl->allocate(data, usage);
141
142 if (!err) {
143 /* allocate memory for enhancement data */
144 alloc_data eData;
145 eData.fd = -1;
146 eData.base = 0;
147 eData.offset = 0;
148 eData.size = ROUND_UP_PAGESIZE(sizeof(MetaData_t));
149 eData.pHandle = data.pHandle;
150 eData.align = getpagesize();
151 int eDataUsage = GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP;
152 int eDataErr = mAllocCtrl->allocate(eData, eDataUsage);
153 ALOGE_IF(eDataErr, "gralloc failed for eData err=%s", strerror(-err));
154
155 if (usage & GRALLOC_USAGE_PRIVATE_UNSYNCHRONIZED) {
156 flags |= private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED;
157 }
158
159 if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_ONLY) {
160 flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_ONLY;
161 //The EXTERNAL_BLOCK flag is always an add-on
162 if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_BLOCK) {
163 flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_BLOCK;
164 }
165 if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_CC) {
166 flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_CC;
167 }
168 }
169
170 flags |= data.allocType;
171 int eBaseAddr = int(eData.base) + eData.offset;
172 private_handle_t *hnd = new private_handle_t(data.fd, size, flags,
173 bufferType, format, width, height, eData.fd, eData.offset,
174 eBaseAddr);
175
176 hnd->offset = data.offset;
177 hnd->base = int(data.base) + data.offset;
178 *pHandle = hnd;
179 }
180
181 ALOGE_IF(err, "gralloc failed err=%s", strerror(-err));
182
183 return err;
184}
/*****************************************************************************/
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;
}</span>
3、gpu_alloc 模块
gpu0内存即非HW_FB内存使用ION分配器进行分配,此文不做详述。
4、fb 模块
在 gralloc_device_open 中会根据传递的参数分别初始化两个设备,定义如下:
[cpp] view
plaincopy
<span style="font-size:12px;">#define GRALLOC_HARDWARE_FB0 "fb0"
#define GRALLOC_HARDWARE_GPU0 "gpu0"</span>
如果参数不是 "gpu0" ,那么是"fb%u"的形式,则会调用fb_device_open 初始化 fb 设备,主要流程和打开 gralloc 基本一致,在函数中会通过调用 mapFrameBuffer->mapFrameBufferLocked 获取帧缓存设备的参数并将其设备节点映射到用户空间,流程如下(大致如此,msm8960平台代码有所变化,msm平台上fb设备文件名是/dev/graphics/fb%u):
[cpp] view
plaincopy
<span style="font-size:12px;">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 = %s\n"
"xres = %d px\n"
"yres = %d px\n"
"xres_virtual = %d px\n"
"yres_virtual = %d px\n"
"bpp = %d\n"
"r = %2u:%u\n"
"g = %2u:%u\n"
"b = %2u:%u\n",
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 Hz\n",
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;
}</span>
关于fb设备的打开和HW_FB内存的分配,是在FrameBufferNativeWindow的构造代码中,可以看到打开fb0设备获取Framebuffer Info,然后使用gralloc为该FrameBufferNativeWindow分配两个HW_FB内存即Framebuffer,即每个Window,double buffer。代码如下:
[cpp] view
plaincopy
<span style="font-size:12px;">62/*
63 * This implements the (main) framebuffer management. This class is used
64 * mostly by SurfaceFlinger, but also by command line GL application.
65 *
66 * In fact this is an implementation of ANativeWindow on top of
67 * the framebuffer.
68 *
69 * Currently it is pretty simple, it manages only two buffers (the front and
70 * back buffer).
71 *
72 */
73
74FramebufferNativeWindow::FramebufferNativeWindow()
75 : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
76{
77 hw_module_t const* module;
78 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
79 int stride;
80 int err;
81 int i;
82 err = framebuffer_open(module, &fbDev);
83 ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
84
85 err = gralloc_open(module, &grDev);
86 ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));
87
88 // bail out if we can't initialize the modules
89 if (!fbDev || !grDev)
90 return;
91
92 mUpdateOnDemand = (fbDev->setUpdateRect != 0);
93
94 // initialize the buffer FIFO
95 if(fbDev->numFramebuffers >= MIN_NUM_FRAME_BUFFERS &&
96 fbDev->numFramebuffers <= MAX_NUM_FRAME_BUFFERS){
97 mNumBuffers = fbDev->numFramebuffers;
98 } else {
99 mNumBuffers = MIN_NUM_FRAME_BUFFERS;
100 }
101 mNumFreeBuffers = mNumBuffers;
102 mBufferHead = mNumBuffers-1;
103
104 /*
105 * This does not actually change the framebuffer format. It merely
106 * fakes this format to surfaceflinger so that when it creates
107 * framebuffer surfaces it will use this format. It's really a giant
108 * HACK to allow interworking with buggy gralloc+GPU driver
109 * implementations. You should *NEVER* need to set this for shipping
110 * devices.
111 */
112#ifdef FRAMEBUFFER_FORCE_FORMAT
113 *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT;
114#endif
115
116 for (i = 0; i < mNumBuffers; i++)
117 {
118 buffers[i] = new NativeBuffer(
119 fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
120 }
121
122 for (i = 0; i < mNumBuffers; i++)
123 {
124 err = grDev->alloc(grDev,
125 fbDev->width, fbDev->height, fbDev->format,
126 GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);
127
128 ALOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s",
129 i, fbDev->width, fbDev->height, strerror(-err));
130
131 if (err)
132 {
133 mNumBuffers = i;
134 mNumFreeBuffers = i;
135 mBufferHead = mNumBuffers-1;
136 break;
137 }
138 }
139
140 const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags;
141 const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
142 const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
143 const_cast<int&>(ANativeWindow::minSwapInterval) =
144 fbDev->minSwapInterval;
145 const_cast<int&>(ANativeWindow::maxSwapInterval) =
146 fbDev->maxSwapInterval;
147 } else {
148 ALOGE("Couldn't get gralloc module");
149 }
150
151 ANativeWindow::setSwapInterval = setSwapInterval;
152 ANativeWindow::dequeueBuffer = dequeueBuffer;
153 ANativeWindow::lockBuffer = lockBuffer;
154 ANativeWindow::queueBuffer = queueBuffer;
155 ANativeWindow::query = query;
156 ANativeWindow::perform = perform;
157 ANativeWindow::cancelBuffer = NULL;
158}</span>
创建FrameBufferNativeWindow仅发生在DisplayHardware的构造后初始化中,代码片段如下:
[cpp] view
plaincopy
150void DisplayHardware::init(uint32_t dpy)
151{
152 mNativeWindow = new FramebufferNativeWindow(); //******
153 framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
154 if (!fbDev) {
155 ALOGE("Display subsystem failed to initialize. check logs. exiting...");
156 exit(0);
157 }
158
159 int format;
160 ANativeWindow const * const window = mNativeWindow.get();
161 window->query(window, NATIVE_WINDOW_FORMAT, &format);
162 mDpiX = mNativeWindow->xdpi;
163 mDpiY = mNativeWindow->ydpi;
164 mRefreshRate = fbDev->fps;
....
}
而DisplayHardware的真正构造仅在Surfacelinger启动后readyToRun中,其余都是使用拷贝构造默认的Bitwise Copy,当然这仅仅是针对一块屏的情况,当前大屏主流。相关代码片段如下:
[cpp] view
plaincopy
217status_t SurfaceFlinger::readyToRun()
218{
219 ALOGI( "SurfaceFlinger's main thread ready to run. "
220 "Initializing graphics H/W...");
221
222 // we only support one display currently
223 int dpy = 0;
224
225 {
226 // initialize the main display
227 GraphicPlane& plane(graphicPlane(dpy));
228 DisplayHardware* const hw = new DisplayHardware(this, dpy); //*******
229 plane.setDisplayHardware(hw);
230 }
231
232 // create the shared control-block
233 mServerHeap = new MemoryHeapBase(4096,
234 MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap");
235 ALOGE_IF(mServerHeap==0, "can't create shared memory dealer");
236
237 mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase());
238 ALOGE_IF(mServerCblk==0, "can't get to shared control block's address");
239
240 new(mServerCblk) surface_flinger_cblk_t;
241
242 // initialize primary screen
243 // (other display should be initialized in the same manner, but
244 // asynchronously, as they could come and go. None of this is supported
245 // yet).
246 const GraphicPlane& plane(graphicPlane(dpy));
247 const DisplayHardware& hw = plane.displayHardware();
248 const uint32_t w = hw.getWidth();
249 const uint32_t h = hw.getHeight();
250 const uint32_t f = hw.getFormat();
251 hw.makeCurrent();
.....
}
fb 模块最重要的工作就是将应用程序指定的内容写入显存中,是通过函数 fb_post完成的,流程如下(msm8960代码大致如此,不过使用的是FBIOPUT_VSCREENINFO IOCTL_CODE):
[cpp] view
plaincopy
<span style="font-size:12px;">/* 将图形缓冲区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();
}
m->currentBuffer = buffer; /* 设置当前图形缓冲区 */
return 0;
}</span>
5、Gralloc map/unmap、register/unregister
当GPU内存file descriptor从一个进程A传递到另一个进程B后,进程B做gralloc_register_buffer就是使用allocator此时是ION将该buffer在本进程映射一下,用于访问。这些功能做为gralloc的mapper功能。
关于内存file descriptor、binder传递该内存fd的具体机制参见ION的功能。
Overlay & HWC on MDP -- MIMO Display软硬整合
概述
Android显示系统SurfaceFlinger使用Overlay和HWC(Hardware composer)完成Surface
Layer的硬件合成。Overlay和HWC表现为两个HAL,为芯片方案制造商留了实现余地。
因为Overlay也时常被称为hardware composition,为了避免混淆,本文中Overlay专指Android
Display Overlay HAL,对应liboverlay.so;HWC专指SurfaceFlinger使用的硬件合成器HAL,对应hwcomposer.msm8xxxx.so。
Qualcomm MSM8k系列平台的MDP4.x(Mobile Display Platform)提供了硬件Overlay的功能,Android
Overlay HAL与这个Overlay硬件相对应;当然,为了与上段照应,需要说明的是,MDP并不存在一个实现HWC的专门硬件,HWC也是在Overlay上实现的;体现在Android软件中,HWC
HAL是建立在Overlay HAL基础上的,是使用Overlay HAL来实现的。
因此,再次,准确地说,提供硬件合成功能的是模块是Overlay,是MDP Overlay硬件的体现;而HWC则是SurfaceFlinger使用Overlay
HAL的一个桥梁。
Overlay并非仅仅由SurfaceFlinger得到Surface后就不再经过SurfaceFlinger而使用硬件直接输出;从函数调用上看似乎是这样的,但是实际情形却非如此。硬件合成仅是合成的一种手段,合成的结果输出destination还是必须受控,因而也必须纳入SurfaceFlinger的输出管理范畴。
Overlay硬件平台
MSM8k上目前有MDP4.0, MDP4.1, MDP4.2三个硬件版本,主要是合成能力和管道数目的区别。通常,一个RGB pipe和一个VG
pipe组成一个pipe pair,前者是对应UI RGB图像数据,后者对应Camera或Video的RGB或YUV数据,两个pipe输入到一个LayerMixer0用于合成;额外的,LayerMixer可能还有BF(Border
Fill) pipe,用于视频按比例显示时屏幕多余边框填充,这个多用于Ext TV或HDMI输出的时候。MDP4有7个pipe,3个LayerMixer,其中LayerMix0可以配置有多达两个RGB
pipe,两个VG pipe,一个BF pipe输入,完成5 Layer合成。
上述pipe是LayerMixer的输入元素,LayerMixer的输出对应到LCD,TV,HDMI等,当然不是直接对应,而是由DMA
channel和上述模块的控制器相连。三个LayerMixer对应的三个输出使用一般是约定的,当然,软件层面上MDP driver中对每个管道的目标LayerMixer也做了固定的配置。三个输出一般标为Primary,
Seconday, Extra,对应的DMA通道为DMA_P, DMA_S, DMA_E。
更直观地,参见Android Overlay on QC MDP4平台要点简记
由于LayerMixer0提供了多达5个Layer的合成能力,所以当没有Camera或Video优先使用它的时候,它被充分利用来做Layer的Hardware
composition。提前且概要地,这儿必须清楚说明的是,LayerMixer0使用的两种情景:
当有Camera或Video的时候,SurfaceView对应的Layer为HWC_OVERLAY,这时该Layer对应一个VG
pipe,而其余HWC_FRAMEBUFFER Layer经3D GPU render到Framebuffer后,该Framebuffer输入一个pipe(RGB1--base
layer?),和VG pipe经LayerMixer0合成输出。
当没有Camera或Video的时候,如果UI Layer即HWC_FRAMEBUFFER Layer小于等于3个且都满足图像格式条件,那么这些Layer的CompositionType属性会被修改为HWC_OVERLAY,为每个Layer分配pipe,经LayerMixer0合成经DMA_P输出,这就是HWC。由于BF
pipe能力条件的限制,不使用其做HWC,而RGB1做预留的base layer Framebuffer使用,所以标榜的4-layer mdp composition support实际只能接受SurfceFlinger的3个Layer做合成,也就是SurfaceFlinger的属性debug.mdpcomp.maxlayer=3。当超过3个Layer的时候,由于管道数量的限制,不能够再使用LayerMixer0,就使用GPU
render,也即每个Layer->draw。当然GPU render也算是一个Hardware Composition,CompositionType方式的其中之一就是GPU。
Overlay HAL结构
MIMO输入输出代码结构设计,简单地讲,分为流路控制和数据流动两部分;Overlay及构建在其上的hwcomposer都是采用这种流路控制和数据流动的结构。
Overlay有多种应用场景,具体设计上描述为Overlay State。就是说如果允许管道任意灵活组合使用的话,可以有很多种花样的应用,但是这儿预先设计好这么些应用场景,应用场景一定,需要的管道类型和数目随之确定,管道连接的LayerMixer及其输出Video驱动控制器也确定。
Overlay State具体实现使用模板类OverlayImpl<P0, P1, P2>,P0, P1, P2是3个pipe,最多可以使用3个pipe。每个pipe对应的类是模板类GenericPipe<int
PANEL>,PANEL指定该pipe合成后图像输出目的,从而也决定其使用的LayerMixer。另外地,每个OverlayImpl<P0, P1, P2>模板类的3个管道可能需要图像旋转,所以有可能使用3个Rotator。此处的P0,
P1, P2只是逻辑索引,并不具体是MDP的管道索引,根据使用规格需求,从MDP管道中为之动态分配。
hwcomposer也设计了几个封装类与Overlay应用场景对应,屏蔽了场景功能的具体实现。hwcomposer中定义的应用场景比Overlay提供的场景还要少,仅是有限几个。大致结构图如下:
由于overlay场景太多,因此只示意了四个,OV_BYPASS_x_LAYER可以是1,2,3个,分别对应1个,2个或3个HWC_FRAMEBUFFER
Layer和base layer Framebuffer合成的情形。
hwc_prepare的设计逻辑过于固化和简单,成了一种基于if-else判断的优先配置方式,宽阔的城堡顶部建了个鸟笼似的小阁楼;应该设计成Policy或RouteState的形式更具备可扩展性。
Overlay进行流路控制的接口是commit,然后调用其具体实现类和MDP封装类的相关接口,一个调用栈(适当向内核驱动做了扩展)如下:
mdp4_overlay_set()
msmfb_overlay_set()
ioctl(fb, MSMFB_OVERLAY_SET)
overlay::mdp_wrapper::setOverlay()
MdpCtrl::set()
Ctrl::commit()
pipe::commit() -> GernericPipe<utils::PRIMARY>::commit()
OverlayImplBase::commit() -> OverlayImpl<>::commit()
Overlay::commit(overlay::utils::eDest)
Overlay提交数据的接口是queueBuffer,然后同样调用其具体实现类和MDP封装类的相关接口,一个调用栈(适当向内核驱动做了扩展)如下:
mdp4_overlay_play()
msmfb_overlay_play()
ioctl(fd, MSMFB_OVERLAY_PLAY, &od) msmfb_overlay_data.id is mdp_overlay.id is the kernel pipe index in kernel db returned by MdpCtrl.commit()
overlay::mdp_wrapper::play()
MdpData::play(int fd, uint32_t offset)
Data::play()
GenericPipe<PANEL>::queueBuffer(int fd, uint32_t offset)
OverlayImplBase::queueBuffer() -> OverlayImpl<>::(int fd, uint32_t offset, utils::eDest dest)
Overlay::queueBuffer(int fd, uint32_t offset, utils::eDest dest)
Camera或Video Overlay
Camera Overlay一般有两种实现模式,一种模式是V4L2不提供Overlay接口,在用户态把V4L2 buffer handle传递给Framebuffer,由Framebuffer
Overlay Implementation完成合成;另一种模式是V4L2提供Overlay接口,可以把Video buffer在V4L2内核驱动中提交给Framebuffer
Overlay内核驱动完成合成。QC MSM8k Overlay是采用的第一种模式实现,但是V4L2和Framebuffer驱动也提供了第二种模式的实现;第二种模式可参见Freescale处理器上Android
Overlay的实现。
Camera的取景器PreviewDisplay Window是个SurfaceView,所在的Layer(HWC_OVERLAY)在UI
Layer底下,但是UI layer会开个全透的区域露出这个HWC_OVERLAY Layer。
下面描述其合成过程。
3D GPU 绘制:所有的HWC_FRAMEBUFFER layers会首先经OpenGL 3D GPU绘制到back Framebuffer上;
OV_PIPE0配置:然后在setupHardwareComposer=> HWComposer::prepare=>hwc_prepare=>VideoOverlay::prepare中使用HWC_OVERLAY Layer参数设置OV_PIPE0分配的类型为Video/Graphic的MDP
pipe;
OV_PIPE0提交:DisplayHardware::flip=>HWComposer::commit=>hwc_set=>VideoOverlay::draw提交HWC_OVERLAY Layer的图像数据到OV_PIPE0的内核VG
pipe的工作队列中(单容量队列即可);
OV_PIPE3配置:DisplayHardware::flip => HWComposer::commit=> hwc_set=>eglSwapBuffers=> queueBuffer=> fb_post=>update_framebuffer中使用base
layer back framebuffer参数配置OV_PIPE3即RGB1 pipe(注意base layer的特殊地位);
OV_PIPE3提交:当update_framebuffer真正提交图像数据到RGB1 pipe时,对应stage 0 for base layer(内核驱动)会启动MDP
LayerMixer0进行Layers合成;
DMA传输:然后fb_post通过FBIOPUT_VSCREENINFO或PAN_DISPLAY启动DMA_P送合成好的图像经MIPI-DSI输出显示。
BufferFlip:queueBuffer后,该framebuffer即为front framebuffer,然后eglSwapBuffers会dequeueBuffer下一个Framebuffer做为back
framebuffer。
此时Hardware Composer使用的是VideoOverlay@hwcomposer.msm8960.so对应的逻辑。VG pipe设置和提交的栈示例如下:
/system/lib/liboverlay.so (overlay::Overlay::commit(overlay::utils::eDest)+71)
/system/lib/liboverlay.so configPrimVid(hwc_context_t *ctx, hwc_layer_t *layer)
/system/lib/liboverlay.so VideoOverlay::configure(hwc_context_t *ctx, hwc_layer_t *, hwc_layer_t *)
/system/lib/hw/hwcomposer.msm8960.so (qhwc::VideoOverlay::prepare(hwc_context_t *, hwc_layer_list_t *)+xxx)
/system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::setup(hwc_context_t*, hwc_layer_list*)+163) for each pipe.
/system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::configure(hwc_composer_device*, hwc_layer_list*)+59)
/system/lib/hw/hwcomposer.msm8960.so hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list)
/system/lib/libsurfaceflinger.so (android::HWComposer::prepare() const+9)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::setupHardwareComposer()+127)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::handleRepaint()+147)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+91)
/system/lib/liboverlay.so Overlay::queueBuffer(int fd, uint32_t offset, utils::eDest dest)
/system/lib/hw/hwcomposer.msm8960.so (qhwc::VideoOverlay::draw(hwc_context_t*, hwc_layer_list*)+xx) for each pipe.
/system/lib/hw/hwcomposer.msm8960.so static int hwc_set(hwc_composer_device_t *dev,hwc_display_t dpy,hwc_surface_t sur, hwc_layer_list_t* list)
/system/lib/libsurfaceflinger.so (android::HWComposer::commit() const+11)
/system/lib/libsurfaceflinger.so (android::DisplayHardware::flip(android::Region const&) const+59)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::postFramebuffer()+61)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+103)
Base layer对应的RGB pipe设置和提交的栈示例从略。Base layer提交到RGB1时真正启动LayerMixer0的4-stage
layer composition。
FB Layer Hardware Composition
当开启硬件(debug.sf.hw=1)合成且合成方式选择MDP时,属性debug.mdpcomp.maxlayer决定了可以使用的通道的个数,当前设置为3,因为RGB1
pipe预留为Camera或Video时的base layer使用,在全Framebuffer
layer MDP合成情景下,RGB1对应的base layer是一个处于最顶端的全透明层。相关系统设置和属性如下:
在系统根目录文件 /systembuild.prop中,
debug.sf.hw=1
debug.egl.hw=1
debug.composition.type=mdp
debug.enable.wl_log=1
debug.mdpcomp.maxlayer=3
debug.mdpcomp.logs=0
其中CompositionType有 4种,debug.composition.type=dyn/c2d/mdp/cpu/gpu,dyn是根据c2d和mdp的硬件存在选择使用二者之一。
gpu/c2d/mdp可以释放cpu/gpu,减轻cpu/gpu的负担,但并不一定能使显示效果流畅。
SurfaceFlinger启动时会取debug.mdpcomp.maxlayer属性值记录在全局变量sMaxLayers中。相关代码如下:
782 sMaxLayers = 0;
783 if(property_get("debug.mdpcomp.maxlayer", property, NULL) > 0) {
784 if(atoi(property) != 0)
785 sMaxLayers = atoi(property);
786 }
当没有Camera/Video/HDMI/WFD等Overlay应用情景时,所有Layer都是HWC_FRAMEBUFFER的,MDP的LayerMixer0是闲置的,这时可以优化利用其来做Framebuffer
Layer合成。由于管道数目限制的原因,只能合成小于等于sMaxLayers个Layers。多于3个的时候是否可以MDP合成其中的3个?可能需要考虑Layer
buffer维度、格式、缩放、Z序等因素。当多于3个的时候,是遍历layer::draw使用GPU来绘制纹理到back
framebuffer上的。
下面着重看少于等于3个Layer,MDP合成的情况。
首先,所有的Layer的compositiontype都是HWC_FRAMEBUFFER的;
然后SurfaceFlinger在setHardwareComposer时发现hwc_prepare没有别的优先的Overlay情景,最后的一个if分支就是使用MDP来做Layer合成;SurfaceFlinger会检查Layer的属性看是否满足使用MDP的条件,然后设置满足条件的Layer的属性[compositionType,
flags] = (HWC_OVERLAY, MDPCOMP);这样SurfaceFlinger::composeSurfaces时,就不再通过layer::draw使用GPU来绘制;
PIPE配置:SurfaceFlinger为HWC创建工作集,为每个Layer分配并使用MDPComp::prepare配置每个pipe,如果有缩放需求,则会分配VG
pipe,因为RGB pipe不支持缩放;有一个Layer则Overlay状态为BYPASS_1_LAYER,表示有1个Layer
Bypass,不需要OpenGL绘制,同理2个Layer为BYPASS_2_LAYER,3个为BYPASS_3_LAYER;
PIPE提交:在DisplayHardware::flip中提交每个Layer的数据到管道,即MDPComp::draw() => Overlay::queueBuffer()。注意Layer图像数据是在PMEM/ASHMEM内存中而不是Framebuffer内存中,但仍物理连续或IOMMU映射连续,LayerMixerdraw可访问。MDPComp::draw完即提交数据到对应管道的单容量队列后,清layer->flags
&= ~HWC_MDPCOMP标志;
在eglSwapBuffers时,真正做back & front Buffer的切换(准确地说双缓冲是切换,三缓冲是轮用)
OV_PIPE3配置:DisplayHardware::flip => HWComposer::commit=> hwc_set=>eglSwapBuffers=> queueBuffer=> fb_post=>update_framebuffer中使用base
layer back framebuffer参数配置OV_PIPE3即RGB1 pipe;
OV_PIPE3提交:当update_framebuffer真正提交图像数据到RGB1 pipe时,对应stage 0 for base layer(内核驱动)会启动MDP
LayerMixer0进行Layers合成;此时base layer是个最顶层的全透明Layer,不妨碍底层Layer的显示;
DMA传输:然后fb_post通过FBIOPUT_VSCREENINFO或PAN_DISPLAY启动DMA_P送合成好的图像经MIPI-DSI输出显示。
BufferFlip:queueBuffer后,该framebuffer即为front framebuffer,然后eglSwapBuffers会dequeueBuffer下一个Framebuffer做为back
framebuffer。
HWC功能时,是MDPComp@hwcomposer.msm8960.so对应的逻辑,一个Layer对应的 pipe设置和提交的栈示例如下:
#00 pc 0000b0d0 /system/lib/liboverlay.so (overlay::Overlay::commit(overlay::utils::eDest)+71)
#01 pc 00005d16 /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::prepare(hwc_context_t*, hwc_layer*, qhwc::MDPComp::mdp_pipe_info&)+577)
#02 pc 00006190 /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::setup(hwc_context_t*, hwc_layer_list*)+163) for each pipe.
#03 pc 0000652c /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::configure(hwc_composer_device*, hwc_layer_list*)+59)
#04 pc 000037ea /system/lib/hw/hwcomposer.msm8960.so hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list)
#05 pc 0001ebba /system/lib/libsurfaceflinger.so (android::HWComposer::prepare() const+9)
#06 pc 00020df4 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::setupHardwareComposer()+127)
#07 pc 000212f4 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::handleRepaint()+147)
#08 pc 000222e8 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+91)
/system/lib/liboverlay.so Overlay::queueBuffer(int fd, uint32_t offset, utils::eDest dest)
#00 pc 00006592 /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::draw(hwc_context_t*, hwc_layer_list*)+85) for each pipe.
#01 pc 00003ae4 /system/lib/hw/hwcomposer.msm8960.so static int hwc_set(hwc_composer_device_t *dev,hwc_display_t dpy,hwc_surface_t sur, hwc_layer_list_t* list)
#02 pc 0001ed56 /system/lib/libsurfaceflinger.so (android::HWComposer::commit() const+11)
#03 pc 0001e57c /system/lib/libsurfaceflinger.so (android::DisplayHardware::flip(android::Region const&) const+59)
#04 pc 000209c6 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::postFramebuffer()+61)
#05 pc 00022474 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+103)
Base layer对应的RGB pipe设置和提交的栈示例从略。重复地,Base layer提交到RGB1时真正启动LayerMixer0的4-stage
layer composition。
HDMI / WFD
有机会待续。
LayerMixer0合成后图像经DMA_P输出时,在BLT模式和DIRECTOUT模式下LayerMixer0与DMA之间没有buffer;即使FB模式,也是一个Overlay
buffer而未必是做为Framebuffer内存,当然这个Overlay buffer做为DMA_P输入传输给MIPI-DSI/LCDC。
相关文章推荐
- 转android gralloc流程分析for msm8960
- android gralloc流程分析for msm8960
- 转android gralloc流程分析for msm8960
- Qt for Android 部署流程分析
- android gralloc 流程分析
- 基于androidO的gralloc 流程分析
- Qt for Android 部署流程分析
- Android Gralloc 流程分析
- android gralloc 流程分析
- android gralloc 流程分析
- Qt for Android 部署流程分析
- android gralloc 流程分析
- android gralloc 流程分析
- Android中View绘制流程以及invalidate()等相关方法分析
- Android系统应用---SystemUI之一:SystemUI概述和创建启动流程分析
- Android 4.4 Kitkat Phone工作流程浅析(九)__状态通知流程分析
- Android之 MTP框架和流程分析 (1)
- Android OTA升级原理和流程分析(七)---Recovery服务的核心install_package函数
- Android 7.0 虚拟按键(NavigationBar)源码分析 之 View的创建流程
- Android待机流程分析