本文主要是介绍Android6.0 显示系统(四) 图像显示相关,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Linux通常使用Framebuffer来用作显示输出,Framebuffer就是一块内存区域,它通常是显示驱动的内部缓冲区在内存中的映射。一旦用户进程把图像数据复制到Framebuffer中,显示驱动会一个像素一个像素地扫描整个Framebuffer,并根据其中的值更新屏幕上像素点的颜色。驱动中这种更新屏幕的动作是固定的,它的周期就是我们常说的刷新率。
但是在屏幕更新一半时,用户进程更新了Framebuffer中的数据,将导致屏幕上画面的上半部分是前一帧的画面,下半部分变成了新的画面。当然错误会在下次刷新时纠正过来,但是这样也会有闪烁的感觉。这个可以使用双缓冲机制,双缓冲就是提供两块Framebuffer,一块用于显示,一块用于数据更新。数据准备好后,通过ioctl操作告诉显示设备切换用于显示的FrameBuffer,这样图像就能快速的显示出来。
但是双缓冲并没有完全解决问题,虽然双缓冲切换的速度很快,但是如果切换的时间点不对,在画面更新一半的时候切换,还是会出现闪烁的问题。当然,我们可以在底层进行控制,收到切换请求的时候,内部并不马上执行,等到刷新完成后再切换,这样完全避免了画面重叠问题。但是这样也有问题,如果用ioctl操作告诉底层可以进行切换了,但是缓冲区没有切换,这样应用层就不能确定何时可以再使用缓冲区,因此只能不断的通过ioctl来查询缓冲区的状态,一直到切换完成了。这种方式效率太低,拖慢了整个系统。解决这个问题就是底层固定发送信号给用户进程,通知进程切换的时机。这个信号就是VSync信号。
VSync信号是一个硬件信号,一般是显示设备刷新的周期到了会发送。
一、VSync信号的产生
Android通过VSync机制来提高显示效果,那么VSync是如何产生的?通常这个信号是由显示驱动产生,这样才能达到最佳效果。但是Android为了能运行在不支持VSync机制的设备上,也提供了软件模拟产生VSync信号的手段。
SurfaceFlinger中用HWComposer类来表示硬件显示设备,
- HWComposer::HWComposer(
- const sp<SurfaceFlinger>& flinger,
- EventHandler& handler)
- : mFlinger(flinger),
- mFbDev(0), mHwc(0), mNumDisplays(1),
- mCBContext(new cb_context),
- mEventHandler(handler),
- mDebugForceFakeVSync(false)
- {
- ......
- bool needVSyncThread = true;
- // Note: some devices may insist that the FB HAL be opened before HWC.
- int fberr = loadFbHalModule();//装载FrameBuffer的硬件模块
- loadHwcModule();//装载HWComposer的硬件模块,这个函数中会将mHwc置为true
- ......
- if (mHwc) {//这个为true代表硬件设备打开了
- ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
- (hwcApiVersion(mHwc) >> 24) & 0xff,
- (hwcApiVersion(mHwc) >> 16) & 0xff);
- if (mHwc->registerProcs) {
- mCBContext->hwc = this;
- mCBContext->procs.invalidate = &hook_invalidate;
- mCBContext->procs.vsync = &hook_vsync;//vsync回调函数
- if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
- mCBContext->procs.hotplug = &hook_hotplug;
- else
- mCBContext->procs.hotplug = NULL;
- memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
- mHwc->registerProcs(mHwc, &mCBContext->procs);
- }
- // don't need a vsync thread if we have a hardware composer
- needVSyncThread = false;//打开硬件设备成功了,将needVSncThread为false
- ......
- }
- ......
- if (needVSyncThread) {
- // we don't have VSYNC support, we need to fake it
- mVSyncThread = new VSyncThread(*this);
- }
- }
通过loadHwcModule来装载硬件模块,如果成功,mHwc为true,needVSyncThread为false。如果不成功,needVsyncThread为true,然后就要创建VSyncThread对象了,它就是产生VSync信号的软件手段了。
VSyncThread是一个thread,在onFirstRef中会调用run函数,就是执行threadLoop,这个函数只要返回true就会一直执行。
- bool HWComposer::VSyncThread::threadLoop() {
- { // scope for lock
- Mutex::Autolock _l(mLock);
- while (!mEnabled) {
- mCondition.wait(mLock);
- }
- }
- const nsecs_t period = mRefreshPeriod;
- const nsecs_t now = systemTime(CLOCK_MONOTONIC);
- nsecs_t next_vsync = mNextFakeVSync;
- nsecs_t sleep = next_vsync - now;
- if (sleep < 0) {
- // we missed, find where the next vsync should be
- sleep = (period - ((now - next_vsync) % period));
- next_vsync = now + sleep;
- }
- mNextFakeVSync = next_vsync + period;
- struct timespec spec;
- spec.tv_sec = next_vsync / 1000000000;
- spec.tv_nsec = next_vsync % 1000000000;
- int err;
- do {
- err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
- } while (err<0 && errno == EINTR);
- if (err == 0) {
- mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
- }
- return true;
- }
这个函数会间隔模拟产生VSync的信号的原理是在固定时间发送消息给HWCompoer的消息对象mEventHandler,这个其实就到SurfaceFlinger的onVSyncReceived函数了。用软件模拟VSync信号在系统比较忙的时候可能会丢失一些信号。
Android源码再hardware/lib/libhardware/modules下有一个hwcomposer目录,里面是一个Android提供的缺省的硬件HWComposer模块的例子,这个例子只实现了一个open接口,并不能真正工作。在前面HWComposer的构造函数中,有如下代码
- mCBContext->procs.vsync = &hook_vsync;
这里指定了vsync的回调函数是hook_vsync,如果硬件中产生了VSync信号,将通过这个函数来通知上层,看看它的代码:
- void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp,
- int64_t timestamp) {
- cb_context* ctx = reinterpret_cast<cb_context*>(
- const_cast<hwc_procs_t*>(procs));
- ctx->hwc->vsync(disp, timestamp);
- }
然后又调用了vsync函数,这个函数最后也是调用了mEventHandler.onVSyncReceived函数,这个函数最后回到SurfaceFlinger中的onVsyncReceived函数中。
- void HWComposer::vsync(int disp, int64_t timestamp) {
- if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
- {
- Mutex::Autolock _l(mLock);
- // There have been reports of HWCs that signal several vsync events
- // with the same timestamp when turning the display off and on. This
- // is a bug in the HWC implementation, but filter the extra events
- // out here so they don't cause havoc downstream.
- if (timestamp == mLastHwVSync[disp]) {
- ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")",
- timestamp);
- return;
- }
- mLastHwVSync[disp] = timestamp;
- }
- char tag[16];
- snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
- ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
- mEventHandler.onVSyncReceived(disp, timestamp);
- }
- }
二、FrameBuffer工作原理
我们先来看下loadFbHalModule函数,hw_get_module是HAl框架中装载HAL模块的函数
- int HWComposer::loadFbHalModule()
- {
- hw_module_t const* module;
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (err != 0) {
- ALOGE("%s module not found", GRALLOC_HARDWARE_MODULE_ID);
- return err;
- }
- return framebuffer_open(module, &mFbDev);
- }
我们再来看看framebuffer_open函数,
- static inline int framebuffer_open(const struct hw_module_t* module,
- struct framebuffer_device_t** device) {
- return module->methods->open(module,
- GRALLOC_HARDWARE_FB0, (struct hw_device_t**)device);
- }
GRALLOC_HARDWARE_FB0 就是fb0
- #define GRALLOC_HARDWARE_FB0 "fb0"
Gralloc模块在实际设备中有硬件厂商提供。我们来看下这个open函数
- static int gralloc_device_open(const hw_module_t* module, const char* name, hw_device_t** device)
- {
- int status = -EINVAL;
- if (!strncmp(name, GRALLOC_HARDWARE_GPU0, MALI_GRALLOC_HARDWARE_MAX_STR_LEN))
- {
- status = alloc_device_open(module, name, device);//处理gpu的
- }
- else if (!strncmp(name, GRALLOC_HARDWARE_FB0, MALI_GRALLOC_HARDWARE_MAX_STR_LEN))
- {
- status = framebuffer_device_open(module, name, device);
- }
- return status;
- }
我们来看framebuffer_device_open函数,如果不支持framebuffer直接退出了(现在很多设备都开始不支持了)。如果支持framebuffer的话先是调用了init_frame_buffer函数来获取设备信息,通过mmap分配一块共享内存,然后设置FrameBuffer的操作函数等。
- int framebuffer_device_open(hw_module_t const* module, const char* name, hw_device_t** device)
- {
- int status = -EINVAL;
- log_fbpost = false;
- char property[PROPERTY_VALUE_MAX];
- if(property_get("debug.gralloc.fbpost", property, "0") > 0) {
- if(atoi(property) == 1) {
- log_fbpost = true;
- ALOGI("enable fbpost log!");
- }
- }
- alloc_device_t* gralloc_device;
- #if DISABLE_FRAMEBUFFER_HAL == 1 //不支持FrameBuffer
- AERR("Framebuffer HAL not support/disabled %s",
- #ifdef MALI_DISPLAY_VERSION
- "with MALI display enable");
- #else
- "");
- #endif
- return -ENODEV;
- #endif
- status = gralloc_open(module, &gralloc_device);
- if (status < 0)
- {
- return status;
- }
- private_module_t* m = (private_module_t*)module;
- status = init_frame_buffer(m);
- framebuffer_device_t *dev = reinterpret_cast<framebuffer_device_t*> (malloc(sizeof(framebuffer_device_t)));
- /* if either or both of init_frame_buffer() and malloc failed */
- if ((status < 0) || (!dev))
- {
- gralloc_close(gralloc_device);
- (!dev) ? (void)(status = -ENOMEM) : free(dev);
- return status;
- }
- memset(dev, 0, sizeof(*dev));
- //设置framebuffer的操作函数
- dev->common.tag = HARDWARE_DEVICE_TAG;
- dev->common.version = 0;
- dev->common.module = const_cast<hw_module_t*>(module);
- dev->common.close = fb_close;
- dev->setSwapInterval = fb_set_swap_interval;
- dev->post = fb_post;
- dev->enableScreen = fb_enable_screen;
- dev->setUpdateRect = 0;
- dev->compositionComplete = &compositionComplete;
- int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
- const_cast<uint32_t&>(dev->flags) = 0;
- const_cast<uint32_t&>(dev->width) = m->info.xres;
- const_cast<uint32_t&>(dev->height) = m->info.yres;
- const_cast<int&>(dev->stride) = stride;
- const_cast<int&>(dev->format) = m->fbFormat;
- const_cast<float&>(dev->xdpi) = m->xdpi;
- const_cast<float&>(dev->ydpi) = m->ydpi;
- const_cast<float&>(dev->fps) = m->fps;
- const_cast<int&>(dev->minSwapInterval) = 0;
- const_cast<int&>(dev->maxSwapInterval) = 1;
- const_cast<int&>(dev->numFramebuffers) = m->numBuffers;
- *device = &dev->common;
- AINF("%s line %d format %d numBuffers %d",__FUNCTION__,__LINE__, dev->format, m->numBuffers);
- //init dynamic lcd fps adjustment
- dyn_fps_init(m);
- #if GRALLOC_VSYNC_NEEDED == 1
- gralloc_vsync_enable(dev);//支持vsync
- #endif
- gralloc_close(gralloc_device);
- return status;
- }
init_frame_buffer函数主要调用了init_frame_buffer_locked函数
- static int init_frame_buffer(struct private_module_t* module)
- {
- pthread_mutex_lock(&module->lock);
- int err = init_frame_buffer_locked(module);
- pthread_mutex_unlock(&module->lock);
- return err;
- }
我们来看看init_frame_buffer_locked函数,先打开设备列表中的一个设备即可,然后通过ioctl获取设备信息,把设备信息放到module中,后面通过mmap分配一块共享内存。
- int init_frame_buffer_locked(struct private_module_t* module)
- {
- if (module->framebuffer)
- {
- return 0; // Nothing to do, already initialized
- }
- char const * const device_template[] =//设备列表
- {
- "/dev/graphics/fb%u",
- "/dev/fb%u",
- NULL
- };
- 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;
- if(info.bits_per_pixel == 32)
- {
- /*
- * Explicitly request 8/8/8
- */
- 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;
- }
- else
- {
- /*
- * Explicitly request 5/6/5
- */
- info.bits_per_pixel = 16;
- info.red.offset = 11;
- info.red.length = 5;
- info.green.offset = 5;
- info.green.length = 6;
- info.blue.offset = 0;
- info.blue.length = 5;
- info.transp.offset = 0;
- info.transp.length = 0;
- }
- /*
- * 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, FBIOPUT_VSCREENINFO, &info) == -1)
- {
- info.yres_virtual = info.yres;
- flags &= ~PAGE_FLIP;
- AWAR( "FBIOPUT_VSCREENINFO failed, page flipping not supported fd: %d", fd );
- }
- if (info.yres_virtual < info.yres * 2)
- {
- // we need at least 2 for page-flipping
- info.yres_virtual = info.yres;
- flags &= ~PAGE_FLIP;
- AWAR( "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 = 0;
- if ( info.pixclock > 0 )
- {
- refreshRate = 1000000000000000LLU /
- (
- uint64_t( info.upper_margin + info.lower_margin + info.yres + info.hsync_len )
- * ( info.left_margin + info.right_margin + info.xres + info.vsync_len )
- * info.pixclock
- );
- }
- else
- {
- AWAR( "fbdev pixclock is zero for fd: %d", fd );
- }
- if (refreshRate == 0)
- {
- refreshRate = 60*1000; // 60 Hz
- }
- if (int(info.width) <= 0 || int(info.height) <= 0)
- {
- // the driver doesn't return that information
- // default to 320 dpi
- // debugging stuff...
- char value[PROPERTY_VALUE_MAX];
- int lcd_density;
- property_get("ro.sf.lcd_density", value, "320");
- lcd_density = atoi(value);
- info.width = ((info.xres * 25.4f) / (float)lcd_density + 0.5f);
- info.height = ((info.yres * 25.4f) / (float)lcd_density + 0.5f);
- }
- float xdpi = (info.xres * 25.4f) / info.width;
- float ydpi = (info.yres * 25.4f) / info.height;
- float fps = refreshRate / 1000.0f;
- AINF("leadcore fb 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);
- AINF("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 (0 == strncmp(finfo.id, "CLCD FB", 7))
- {
- module->dpy_type = MALI_DPY_TYPE_CLCD;
- }
- else if (0 == strncmp(finfo.id, "ARM Mali HDLCD", 14))
- {
- module->dpy_type = MALI_DPY_TYPE_HDLCD;
- }
- else
- {
- module->dpy_type = MALI_DPY_TYPE_UNKNOWN;
- }
- if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
- {
- return -errno;
- }
- if (finfo.smem_len <= 0)
- {
- return -errno;
- }
- if( 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)
- {
- module->fbFormat = HAL_PIXEL_FORMAT_BGRA_8888;
- }
- if( info.bits_per_pixel == 32 &&
- info.red.offset == 0 &&
- info.red.length == 8 &&
- info.green.offset == 8 &&
- info.green.length == 8 &&
- info.blue.offset == 16 &&
- info.blue.length == 8)
- {
- module->fbFormat = HAL_PIXEL_FORMAT_RGBA_8888;
- }
- if( info.bits_per_pixel == 16 &&
- info.red.offset == 0 &&
- info.red.length == 5 &&
- info.green.offset == 5 &&
- info.green.length == 6 &&
- info.blue.offset == 11 &&
- info.blue.length == 5)
- {
- module->fbFormat = HAL_PIXEL_FORMAT_RGB_565;
- }
- module->flags = flags;//设置信息
- module->info = info;
- module->finfo = finfo;
- module->xdpi = xdpi;
- module->ydpi = ydpi;
- module->fps = fps;
- module->swapInterval = 1;
- /*
- * map the framebuffer
- */
- size_t fbSize = round_up_to_page_size(finfo.line_length * info.yres_virtual);
- void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);//mmap分配一块共享内存
- if (vaddr == MAP_FAILED)
- {
- AERR( "Error mapping the framebuffer (%s)", strerror(errno) );
- return -errno;
- }
- //fix black screen between uboot logo and bootanimation
- //memset(vaddr, 0, fbSize);
- // Create a "fake" buffer object for the entire frame buffer memory, and store it in the module
- module->framebuffer = new private_handle_t(private_handle_t::PRIV_FLAGS_FRAMEBUFFER, GRALLOC_USAGE_HW_FB, fbSize, vaddr,
- 0, dup(fd), 0, 0);
- module->numBuffers = info.yres_virtual / info.yres;
- module->bufferMask = 0;
- return 0;
- }
最后我们再来看看framebuffer的操作函数fb_post,这个函数根据PRIV_FLAGS_FRAMEBUFFER来判断Framebuffer是否支持多缓冲,如果不支持方法很简单,直接把buffer中的数据复制到Framebuffer中就可以了。
Filp是指使用ioctl的FBIOPUT_VSCREENINFO参数设置当前显示的buffer。通过将显示区域指向Framebuffer中的新的数据帧,能非常迅速地完成buffer的切换。单缓冲模式下数据复制到缓冲区还需要一定时间,会加重闪烁感,通过Filp的方式切换缓冲区就不存在这个问题了。
- static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)
- {
- if (private_handle_t::validate(buffer) < 0)
- {
- return -EINVAL;
- }
- 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;
- }
- struct timeval tv1, tv2;
- if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) //framebuffer是否支持多缓冲区(flip)
- {
- 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 = (uintptr_t)hnd->base - (uintptr_t)m->framebuffer->base;
- int interrupt;
- m->info.activate = FB_ACTIVATE_VBL;
- m->info.yoffset = offset / m->finfo.line_length;
- up_fps(m);
- gettimeofday(&tv1, NULL);
- if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1)
- {
- AERR( "FBIOPUT_VSCREENINFO failed for fd: %d", m->framebuffer->fd );
- m->base.unlock(&m->base, buffer);
- return -errno;
- }
- #if GRALLOC_VSYNC_NEEDED
- if ( 0 != gralloc_wait_for_vsync(dev) )
- {
- AERR( "Gralloc wait for vsync failed for fd: %d", m->framebuffer->fd );
- m->base.unlock(&m->base, buffer);
- return -errno;
- }
- #endif
- gettimeofday(&tv2, NULL);
- if((int64_t)tv2.tv_sec * 1000 + tv2.tv_usec/1000 - (int64_t)tv1.tv_sec * 1000 - tv1.tv_usec/1000 > 50)
- {
- ALOGI("%s line %d FBIOPUT_VSCREENINFO buffer %p blocktime=%lldms now=%lldms lasttime=%lld tid=%d",__FUNCTION__,__LINE__,
- buffer,
- (int64_t)tv2.tv_sec * 1000 + tv2.tv_usec/1000 - (int64_t)tv1.tv_sec * 1000 - tv1.tv_usec/1000,
- systemTime(CLOCK_MONOTONIC)/1000000,
- (int64_t)tv2.tv_sec * 1000 + tv2.tv_usec/1000 - lasttime,
- gettid());
- } else {
- ALOGD_IF(log_fbpost, "%s line %d FBIOPUT_VSCREENINFO buffer %p blocktime=%lldms now=%lldms lasttime=%lld tid=%d",__FUNCTION__,__LINE__,
- buffer,
- (int64_t)tv2.tv_sec * 1000 + tv2.tv_usec/1000 - (int64_t)tv1.tv_sec * 1000 - tv1.tv_usec/1000,
- systemTime(CLOCK_MONOTONIC)/1000000,
- (int64_t)tv2.tv_sec * 1000 + tv2.tv_usec/1000 - lasttime,
- gettid());
- }
- lasttime = (int64_t)tv2.tv_sec * 1000 + tv2.tv_usec/1000;
- postcount++;
- if(postcount == 1000)
- {
- ALOGI("%s line %d avgframerate = %2f",__FUNCTION__,__LINE__,
- (float)postcount*1000.0/(lasttime - timecount));
- postcount = 0;
- timecount = lasttime;
- }
- m->currentBuffer = buffer;
- } else {//不支持多缓冲(flip)
- void* fb_vaddr;
- void* buffer_vaddr;
- m->base.lock(&m->base, m->framebuffer, GRALLOC_USAGE_SW_WRITE_RARELY,
- 0, 0, m->info.xres, m->info.yres, &fb_vaddr);
- m->base.lock(&m->base, buffer, GRALLOC_USAGE_SW_READ_RARELY,
- 0, 0, m->info.xres, m->info.yres, &buffer_vaddr);
- // If buffer's alignment match framebuffer alignment we can do a direct copy.
- // If not we must fallback to do an aligned copy of each line.
- if ( hnd->byte_stride == (int)m->finfo.line_length )
- {
- memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);
- }
- else
- {
- uintptr_t fb_offset = 0;
- uintptr_t buffer_offset = 0;
- unsigned int i;
- for (i = 0; i < m->info.yres; i++)
- {
- memcpy((void *)((uintptr_t)fb_vaddr + fb_offset),
- (void *)((uintptr_t)buffer_vaddr + buffer_offset),
- m->finfo.line_length);
- fb_offset += m->finfo.line_length;
- buffer_offset += hnd->byte_stride;
- }
- }
- m->base.unlock(&m->base, buffer);
- m->base.unlock(&m->base, m->framebuffer);
- }
- return 0;
- }
三、分配图像缓冲区的内存
之前的博客在GraphicBufferAllocator的alloc方法是调用了mAllocDev的alloc,而这个mAllocDev也是Gralloc模块,最后会调用如下方法。
- int gralloc_alloc(struct alloc_device_t* dev,
- int w, int h, int format, int usage,
- buffer_handle_t* pHandle, int* pStride)
- {
- int rel;
- /* TODO: Redirect to specific allocator according to usage. */
- if (usage & GRALLOC_USAGE_HW_FB) {
- /* Dispatch to framebuffer allocator. */
- rel = gralloc_alloc_framebuffer(dev,
- w, h, format, usage, pHandle, pStride);
- } else {
- rel = gc_gralloc_alloc(dev, w, h, format, usage, pHandle, pStride);
- }
- return rel;
- }
gralloc_alloc函数会根据usage中的标志是否有GRALLOC_USAGE_HW_FB来决定是从硬件缓冲中分配缓冲区还是从普通内存分配缓冲区。我们先看看从内存中分配缓冲区的。
如果不支持framebuffer,只能从普通内存中分配缓冲区。gc_gralloc_alloc函数是从普通内存中分配缓冲区,主要使用了匿名共享内存的方法。最后pHandle装了共享内存的fd。
另外从硬件缓冲区分配内存是调用了gralloc_alloc_framebuffer方法,这个函数主要调用了gralloc_alloc_framebuffer_locked方法
- static int gralloc_alloc_framebuffer_locked(alloc_device_t* dev, size_t size, int usage, buffer_handle_t* pHandle, int* stride, int* byte_stride)
- {
- private_module_t* m = reinterpret_cast<private_module_t*>(dev->common.module);
- // allocate the framebuffer
- if (m->framebuffer == NULL)//如果为空
- {
- // initialize the framebuffer, the framebuffer is mapped once and forever.
- int err = init_frame_buffer_locked(m);//分配一大块内存
- if (err < 0)
- {
- return err;
- }
- }
- const uint32_t bufferMask = m->bufferMask;
- const uint32_t numBuffers = m->numBuffers;
- /* framebufferSize is used for allocating the handle to the framebuffer and refers
- * to the size of the actual framebuffer.
- * alignedFramebufferSize is used for allocating a possible internal buffer and
- * thus need to consider internal alignment requirements. */
- const size_t framebufferSize = m->finfo.line_length * m->info.yres;
- const size_t alignedFramebufferSize = GRALLOC_ALIGN(m->finfo.line_length, 64) * m->info.yres;
- *stride = m->info.xres;
- if (numBuffers == 1)//如果是单缓冲,使用普通内存分配
- {
- // If we have only one buffer, we never use page-flipping. Instead,
- // we return a regular buffer which will be memcpy'ed to the main
- // screen when post is called.
- int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D;
- AWAR( "fallback to single buffering. Virtual Y-res too small %d", m->info.yres );
- *byte_stride = GRALLOC_ALIGN(m->finfo.line_length, 64);
- return alloc_backend_alloc(dev, alignedFramebufferSize, newUsage, pHandle);
- }
- if (bufferMask >= ((1LU<<numBuffers)-1))
- {
- // We ran out of buffers.
- return -ENOMEM;
- }
- uintptr_t framebufferVaddr = (uintptr_t)m->framebuffer->base;
- // find a free slot
- for (uint32_t i=0 ; i<numBuffers ; i++)//找到一块空闲的快
- {
- if ((bufferMask & (1LU<<i)) == 0)
- {
- m->bufferMask |= (1LU<<i);
- break;
- }
- framebufferVaddr += framebufferSize;
- }
- // The entire framebuffer memory is already mapped, now create a buffer object for parts of this memory
- private_handle_t* hnd = new private_handle_t(private_handle_t::PRIV_FLAGS_FRAMEBUFFER, usage, size,
- (void*)framebufferVaddr, 0, dup(m->framebuffer->fd),
- (framebufferVaddr - (uintptr_t)m->framebuffer->base), 0);
- /*
- * Perform allocator specific actions. If these fail we fall back to a regular buffer
- * which will be memcpy'ed to the main screen when fb_post is called.
- */
- if (alloc_backend_alloc_framebuffer(m, hnd) == -1)
- {
- delete hnd;
- int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D;
- AERR( "Fallback to single buffering. Unable to map framebuffer memory to handle:%p", hnd );
- *byte_stride = GRALLOC_ALIGN(m->finfo.line_length, 64);
- return alloc_backend_alloc(dev, alignedFramebufferSize, newUsage, pHandle);
- }
- *pHandle = hnd;
- *byte_stride = m->finfo.line_length;
- return 0;
- }
这个函数如果第一次调用会调用init_frame_buffer_locked来从Framebuffer设备上分配一大块共享内存,内存的大小是屏幕尺寸的整数倍。numBuffers是内存的块数,如果只有一块代表是单缓冲,单缓冲调用分配普通内存的函数(单内存只能分配普通内存也很好理解,因为framebuffer的内存要用于显示。重新分配的话只能就分配普通内存了)。多缓冲的话使用Framebuffer的内存。但是Framebuffer的缓冲区块数也是有限的,因此函数要找一块空闲的缓冲区。如果缓冲区分配完了,返回错误值-ENOMEM。
这篇关于Android6.0 显示系统(四) 图像显示相关的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!