Android6.0 显示系统(四) 图像显示相关
2016-09-29 11:30
302 查看
Linux通常使用Framebuffer来用作显示输出,Framebuffer就是一块内存区域,它通常是显示驱动的内部缓冲区在内存中的映射。一旦用户进程把图像数据复制到Framebuffer中,显示驱动会一个像素一个像素地扫描整个Framebuffer,并根据其中的值更新屏幕上像素点的颜色。驱动中这种更新屏幕的动作是固定的,它的周期就是我们常说的刷新率。
但是在屏幕更新一半时,用户进程更新了Framebuffer中的数据,将导致屏幕上画面的上半部分是前一帧的画面,下半部分变成了新的画面。当然错误会在下次刷新时纠正过来,但是这样也会有闪烁的感觉。这个可以使用双缓冲机制,双缓冲就是提供两块Framebuffer,一块用于显示,一块用于数据更新。数据准备好后,通过ioctl操作告诉显示设备切换用于显示的FrameBuffer,这样图像就能快速的显示出来。
但是双缓冲并没有完全解决问题,虽然双缓冲切换的速度很快,但是如果切换的时间点不对,在画面更新一半的时候切换,还是会出现闪烁的问题。当然,我们可以在底层进行控制,收到切换请求的时候,内部并不马上执行,等到刷新完成后再切换,这样完全避免了画面重叠问题。但是这样也有问题,如果用ioctl操作告诉底层可以进行切换了,但是缓冲区没有切换,这样应用层就不能确定何时可以再使用缓冲区,因此只能不断的通过ioctl来查询缓冲区的状态,一直到切换完成了。这种方式效率太低,拖慢了整个系统。解决这个问题就是底层固定发送信号给用户进程,通知进程切换的时机。这个信号就是VSync信号。
VSync信号是一个硬件信号,一般是显示设备刷新的周期到了会发送。
SurfaceFlinger中用HWComposer类来表示硬件显示设备,
通过loadHwcModule来装载硬件模块,如果成功,mHwc为true,needVSyncThread为false。如果不成功,needVsyncThread为true,然后就要创建VSyncThread对象了,它就是产生VSync信号的软件手段了。
VSyncThread是一个thread,在onFirstRef中会调用run函数,就是执行threadLoop,这个函数只要返回true就会一直执行。
这个函数会间隔模拟产生VSync的信号的原理是在固定时间发送消息给HWCompoer的消息对象mEventHandler,这个其实就到SurfaceFlinger的onVSyncReceived函数了。用软件模拟VSync信号在系统比较忙的时候可能会丢失一些信号。
Android源码再hardware/lib/libhardware/modules下有一个hwcomposer目录,里面是一个Android提供的缺省的硬件HWComposer模块的例子,这个例子只实现了一个open接口,并不能真正工作。在前面HWComposer的构造函数中,有如下代码
这里指定了vsync的回调函数是hook_vsync,如果硬件中产生了VSync信号,将通过这个函数来通知上层,看看它的代码:
然后又调用了vsync函数,这个函数最后也是调用了mEventHandler.onVSyncReceived函数,这个函数最后回到SurfaceFlinger中的onVsyncReceived函数中。
二、FrameBuffer工作原理
我们先来看下loadFbHalModule函数,hw_get_module是HAl框架中装载HAL模块的函数
我们再来看看framebuffer_open函数,
GRALLOC_HARDWARE_FB0 就是fb0
Gralloc模块在实际设备中有硬件厂商提供。我们来看下这个open函数
我们来看framebuffer_device_open函数,如果不支持framebuffer直接退出了(现在很多设备都开始不支持了)。如果支持framebuffer的话先是调用了init_frame_buffer函数来获取设备信息,通过mmap分配一块共享内存,然后设置FrameBuffer的操作函数等。
init_frame_buffer函数主要调用了init_frame_buffer_locked函数
我们来看看init_frame_buffer_locked函数,先打开设备列表中的一个设备即可,然后通过ioctl获取设备信息,把设备信息放到module中,后面通过mmap分配一块共享内存。
最后我们再来看看framebuffer的操作函数fb_post,这个函数根据PRIV_FLAGS_FRAMEBUFFER来判断Framebuffer是否支持多缓冲,如果不支持方法很简单,直接把buffer中的数据复制到Framebuffer中就可以了。
Filp是指使用ioctl的FBIOPUT_VSCREENINFO参数设置当前显示的buffer。通过将显示区域指向Framebuffer中的新的数据帧,能非常迅速地完成buffer的切换。单缓冲模式下数据复制到缓冲区还需要一定时间,会加重闪烁感,通过Filp的方式切换缓冲区就不存在这个问题了。
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。
但是在屏幕更新一半时,用户进程更新了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。
相关文章推荐
- [Android]视频浮层效果使用SurfaceView无法正常显示的问题排查和解决方案
- Android Graphics architecture
- Android surfaceflinger 源代码分析
- SurfaceFlinger 分析 一
- android fence sync
- SurfaceFlinger简介
- Android显示系统框架原理介绍
- Android4.4 增加屏幕旋转功能
- Android6.0 显示系统(一) Surface创建
- Android6.0 显示系统(二) SurfaceFlinger创建Surface
- Android6.0 显示系统(三) 管理图像缓冲区
- 图解Android - Android GUI 系统 - 概论
- Android中的GraphicBuffer同步机制Fence
- Android应用程序与SurfaceFlinger服务的关系概述和学习计划
- android graphic(13)—surface flinger中的“事务”
- android graphic(11)—底层初始化displays
- android graphic(9)—开发者选项关闭HW overlays
- android graphic(8)—surface申请GraphicBuffer过程
- android graphic(7)—gralloc分配图形缓冲区
- android graphic(6)—surfaceflinger和MessageQueue