本文重点针对HDMI在android上的应用,而比较相关的就是overlay机制。overlay在这里只是简单的介绍,后续会有文章再专门详述。
我没记错的话,高通从7X30开始,平台就可以支持HDMI(1.3)输出了。只不过在7x30上通过RGB接口外接一颗HDMI的transmitter来实现;而到了8系列(8x60),高通把这颗IC也集成了,直接就提供HDMI的输出了。(这样下去,以后渐渐的把外围器件都集成了,做底层的估计要失业了,做硬件的似乎工作量也没多少了)。
先来看看HW的能力,下图是MDP4.0的结构图:
可以看到,MDP4内部有4个overlay pipe,2个是for UI(RGB)的,2个是VG for video和graphics的;另外有2个mixer,mixer1是for primary display的(可以是MDDI接口的,也可以是RGB接口的lcd、oled等);mixer2是for external display的,如通过RGB interface2外接HDMI transmitter到TV,也可以是NTSC/PAL等模拟电视信号。
Note:VG1和RGB1被mixer1混合到primary lcd,VG2和RGB2被mixer2混合到external LCD(如HDMI TV)
如果是MDP4.1的话,MDDI接口被移除了,另外RGB接口只有一个,另一个内部集成为HDMI接口了。
上面提到的是硬件平台相关的,就是说硬件有支持HDMI输出的能力,但是软件的状况呢?我们来看看Android和高通的状况。
关于HDMI本身,我就不介绍了,网上随便找找都可以看明白。
研究过Android的都知道,surfacefinger负责管理应用程序的显示窗口,每个窗口可以由多个surface组成,surfaceflinger通过OpenGL(可以通过HW,也可以是SW)把所有的surface合成后,通过调用gralloc模块或是overlay模块(MDP4.X才支持)把整屏数据送到显示设备上。可是Android(截止到2.2,3.0的状况还未知)上目前只支持一个显示设备,也就是说在surfaceflinger只能固定一个显示设备,那么HDMI这个应用在android手机上如何应用呢?
这里介绍2个做法,一个是高通给做好的,叫做UI mirroring和video mirroring;另一个就是我们自己添加接口,AP自己来实现想要的功能。
先来看高通在android中的做法,根据字面不难理解,UI mirroring和video mirroring其实就是把原来显示在primary LCD上的数据mirror到HDMI接口。下图为软件框架图:
先来看看HDMI的控制方面,上图的右侧,user空间中有一个HDMI service,包含一个listener(都是java的),当HDMI cable插入后,底层HDMI的驱动检测到(HPD)后,通过kobject_uevent传送给HDMI daemon,daemon再把event发送给HDMIservice,HDMI service除了判断这个event(cable状态),另外还要判断qualcomm setting中HDMI的on/off选项,然后把判断结果broadcast给各个AP,各个AP也就知道当前是否要开启HDMI输出了
接着先看UI mirroring(不含video的状况)的实现,它针对的是界面的操作,数据为RGB格式。我们知道在kernel中每个显示设备都对应一个fb,初始化时都会分配framebuffer,在这里,primary lcd对应fb0设备,HDMI对应fb1设备。正常情况下,surfaceflinger合成好一个main surface后,通过post buffer(gralloc模块)把数据放入fb0,然后,通过overlay(kernel下做的,上层看到的还是通过IOCRL-FBIOPUT_VSCREENINFO命令实现)输出到primary lcd;当平台支持HDMI并且UI mirroring开启时,gralloc中(framebuffer.cpp)初始化时会多创建一个task(hdmi_ui_loop),并新建一个overlay(主要是控制和数据,参考overlaylib.h),这个overlay对应的channel固定为fb1,src fd就是fb0,也就是说这个overlay的源数据就是fb0,也就是primary lcd上的数据,通过rotator进行旋转(电视是横屏),然后在overlay中再scale up后再通过HDMI送到TV。这样看来,送到HDMI上的数据其实就是把fb0中的数据copybit了一份并放大,多少会有些失真的,但对于UI界面来说是可以接受的。上述整个过程,surfaceflinger是不参与的。
再来看video mirroring是怎么做的?
先来看看什么是video mirroring,其实就是手机播放视频,同时通过HDMI输出到TV上,手机上的内容分为2个部分,一个是视频本身部分,另一个是UI,这已经占用2个overlay pipe了(一个VG pipe,一个RGB pipe),TV上视频部分肯定是需要一个VG pipe的,另外,由于视频大小问题,视频不可能正好为全屏模式,这样必须还需要一个RGB pipe来实现一个背景(全黑)。4个pipe都被占用了,没有多余的pipe来把UI部分传到TV上,所以再使用高通平台时候,进行video mirroring时,TV上只能播放视频画面,UI部分(如菜单)在TV上是无法显示的。
接着来看video部分是怎么处理的?首先手机端UI部分的处理模式不变,只不过上面提到的hdmi_ui_loop这个task会被停掉(UI不需要送到HDMI,原因上面已经解释过);video部分的frame通过opencore解码出来后,首先会通过surfaceflinger来创建overlay(参考layerbuffer.cpp),当系统支持HDMI时通过create overlay都会创建2个通道(这里是2个VG通道),其中包含2个control channel和2个data channel,它们的HAL层接口都再overlaylib.cpp中,channel0 for fb0;channel1 for fb1,如果需要旋转,则从系统pmem中再分配对应的内存。AP中overlay基本上的流程是这样的(可以参考overlays.cpp,里面不全,我补充了一些):
sp<SurfaceComposerClient> client = new SurfaceComposerClient();//新建surface客户端
// create pushbuffer surface
sp<Surface> surface = client->createSurface(getpid(), 0, 320, 240,
PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers);//创建一个surface
// get to the isurface
sp<ISurface> isurface = Test::getISurface(surface);//得到surface相关接口
printf("isurface = %p\n", isurface.get());
// now request an overlay
sp<OverlayRef> ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565);//创建overlay,并得到控制通道
sp<Overlay> overlay = new Overlay(ref);//初始化overlay并得到数据通道
overlay->setFd(mFd);//设置src data的fd
overlay->setCrop(x,y,w,h);//设置剪裁信息(根据需要)
overlay->queueBuffer(offset);//设置显示数据的偏移
这样video player没解码出一个frame,都会调用quene函数把数据送入2个数据通道,overlay engine会把数据送到2个显示设备。
关于那个背景,暂时在code中还没发现,也许是因为目前的版本不是最终版本,后续还会更新。
上面的做法是高通的;但它是有限制的,比如说无法在2个屏幕上显示不同的内容。如果我们要做,也是可以的,主要就是看AP怎么定义规则了。另外framework中需要添加接口,主要是提供一个针对fb1设备的控制接口,同样也是绕过surfaceflinger。比如说手机在播放一个影片,通过HDMI把影片传送到TV上,同时手机端可以去浏览网页。这个功能可以这样做,AP背景播放影片,得到的frame不送到primary display上,而是通过新加的接口输出到fb1设备上,而browser的UI正常显示即可。如果是在高通平台去实现的话,需要把qualcomm setting里面的HDMI选项关掉,否则高通的做法和你自己AP的做法就乱套了。不过目前看,高通提供的方式似乎也可以满足应用了,但应用是永无止境的,只要user有这样的需求,developer就要去做,呵呵!
OVERLAY
首次post后仍会更新,转载请注明出处http://blog.csdn.net/zirconsdu/article/details/8773263。
File Orgnization
目录/hardware/qcom/display/liboverlay/
Android.mk
mdpRotator.cpp Overlay Rotator Wrpper
mdpWrapper.h MDP Normal and Overlay FrameBuffer IOCTL Wrapper
mdssRotator.cpp Overlay MDSS Rotator Wrapper
overlay.cpp Overlay Top level implementation file
overlay.h Overlay Top level declaration file
overlayCtrl.cpp OverlayCtrl implementation file
overlayCtrlData.h OverlayCtrl and OverlayData declaration file including OverlayData implementation
overlayImpl.h Overlay implementation which operates overlay pipes pair(LayerMixer)
overlayMdp.cpp Overlay implementation on MDP, used by OverlayCtrlData
overlayMdp.h Overlay on MDP
overlayMem.h Overlay VG pipe input kernel memory file descriptor, maybe graphic buffer or rotator output buffer
overlayRotator.cpp Overlay Rotator top level implementation
overlayRotator.h Overlay Rotator top level declaration
overlayState.h Overlay state machine
overlayUtils.cpp Overlay Utils
overlayUtils.h Overlay Utils
pipes/ Overlay Pipes, that is Overlay channel. It is a VG and RGB pipe pair on MDP.
Platform architecture
MDP中每个VG和RGB pipe pair作为一个LayerMixer的输入,由LayerMixer完成Overlay功能,作为一个Overlay channel。
当使用Overlay功能时:
RGB pipe的输入是普通的Framebuffer,是Surfaceflinger的合成输出;
VG的输入是video或graphics或camera图像等,是内核空间内存buffer,其owner一般是Video、Graphics或V4L2等。当其前端是Rotator时,Rotator的输入是这些buffer,Rotator的输出Overlay rotator frame buffer作为VG的输入。
每个Overlay Channel结构如下图所示
关于Overlay Buffer(FrameBuffer RotatorBuffer OverlayBuffer)这些名称并不特别明确,只要明白Overlay Channel数据流路上的各输入输出Buffer的位置和作用即可。
下面以Layermixer1(对应/dev/graphics/fb0)为参考详述各buffer:
只UI显示时,
Framebuffer是fb0的framebuffer,是从启动时预留出的bootmem中的分配出来的。LayerMixer1处于BLT模式,Layermixer1和DMA_P(Primary display driver)分离,可以由软件完全控制。该Framebuffer做为DMA_P的输入,经MIPI_DSI输出到主屏上。
启用Overlay时,
上述Framebuffer做为RGB1 pipe的输入,而视频或图像的内核buffer做为VG pipe的输入,二者经Layermixer1合成;此时LayerMixer1工作在非BLT模式,LayerMixer1和DMA_P attach在一起,LayerMixer1输出控制参数直接提供给DMA_P使用。此时LayerMixer1仍有两种工作模式,FrameBuffer模式和DIRECT_OUT模式,前者时LayerMixer1和DMA_P之间使用一个overlaydouble buffer做缓冲,输出给DMA_P;DIRECT_OUT模式下不使用该ovl double buffer,LayerMixer1直接输出给DMA_P。
一般VG和RGB的输入都可以是double buffer,ping-pang;LayerMixer的输出也是double buffer。DMA_P/S/E做为display driver传输前端buffer作为后端接口控制器的输入。
下面两图是QC MDP UI mirror和Video mirror时的两结构图,并没有明确画出LayerMix1的Overlay流程路径,个别buffer的owner可能也有所差错,buffer也并不全,仅是大致描述Overlay及其部分buffer。
MDP和DSI和后端显示控制器和接口的连接结构如下图。
Layer architecture
Overlay -> OverlayImpl |
OverlayCtrlData |
OverlayMDPCtrlData |
MDPWrapper |
FrameBuffer |
KeyPoint
Ctrl用来设置overlay channel的参数,Data用来提交buffer到Overlay channel queue。其实使用overlay本质上就是设置好pin路由,设置好通道工作参数,然后不停的提交数据让Overlay Enginee工作。MDP的Overlay Channel并没有别的特殊的编程接口,都是使用控制、状态和数据寄存器来访问。其实MDP提供了远比Android Overlay实现强得多的Overlay功能。
Some flow
Ctrl::commit() -> MDPCtrl::set() -> mdp_wrapper::setOverlay() -> ioctl(fd, MSMFB_OVERLAY_SET, &ov)
-> msm_fb -> mdp4_overlay设置Overlay工作参数。
Data::queueBuffer -> MDPData::play -> mdp_wrapper::play() -> ioctl(fd, MSMFB_OVERLAY_PLAY, &od)
-> msm_fb -> mdp4_overlay进行Overlay合成。注意queueBuffer第一参数fd是memFd,是内核空间的buffer,并不在用户空间和内核空间拷贝buffer数据。作用与framebuffer类似的是提交内核空间的该buffer到Overlay Enginee Queue。
有了这些平台相关知识,msm_fb和mdp4_overlay驱动的功能也就容易理解了。
android gralloc流程分析for msm8960
原文转载自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 结构来打开设备,流程如下:
- <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 设备成员的初始化:
在 gralloc_device_open 中会根据传递的参数分别初始化两个设备,定义如下:
- <span style="font-size:12px;">#define GRALLOC_HARDWARE_FB0 "fb0"
- #define GRALLOC_HARDWARE_GPU0 "gpu0"</span>
- <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。代码如下:
- <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的构造后初始化中,代码片段如下:
- 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的功能。