原文转载自http://blog.csdn.net/g_salamander/article/details/8424334
增加了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 来描述,该结构定义如下:
图形缓冲区的操作接口由结构 struct gralloc_module_t 定义:
gralloc 设备则用结构 struct alloc_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;
}
可以看出,是使用id和系统平台的名字组合出so的文件名,去设定的目录动态加载该库文件然后解析特定符号,找到hw_module_t object。
函数会在 /system/lib/hw 或者 /vendor/lib/hw 目录中去寻找gralloc.xxx.so 文件,如果找到了就调用load接口完成加载。
最终会调用 gralloc_device_open完成 gralloc 设备成员的初始化:
在 gralloc_device_open 中会根据传递的参数分别初始化两个设备,定义如下:
#define GRALLOC_HARDWARE_FB0 "fb0"
#define GRALLOC_HARDWARE_GPU0 "gpu0"
如果参数不是 "gpu0" ,那么是"fb%u"的形式,则会调用fb_device_open 初始化 fb 设备,主要流程和打开 gralloc 基本一致,在函数中会通过调用 mapFrameBuffer->mapFrameBufferLocked 获取帧缓存设备的参数并将其设备节点映射到用户空间,流程如下(大致如此,msm8960平台代码有所变化,msm平台上fb设备文件名是/dev/graphics/fb%u):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;
}
关于fb设备的打开和HW_FB内存的分配,是在FrameBufferNativeWindow的构造代码中,可以看到打开fb0设备获取Framebuffer Info,然后使用gralloc为该FrameBufferNativeWindow分配两个HW_FB内存即Framebuffer,即每个Window,double buffer。代码如下:
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}
创建FrameBufferNativeWindow仅发生在DisplayHardware的构造后初始化中,代码片段如下:
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,当然这仅仅是针对一块屏的情况,当前大屏主流。相关代码片段如下:
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):
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的功能。