Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android6.0 顯示系統(四) 圖像顯示相關

Android6.0 顯示系統(四) 圖像顯示相關

編輯:關於Android編程

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& 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(
            const_cast(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(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(dev->flags) = 0;
	const_cast(dev->width) = m->info.xres;
	const_cast(dev->height) = m->info.yres;
	const_cast(dev->stride) = stride;
	const_cast(dev->format) = m->fbFormat;
	const_cast(dev->xdpi) = m->xdpi;
	const_cast(dev->ydpi) = m->ydpi;
	const_cast(dev->fps) = m->fps;
	const_cast(dev->minSwapInterval) = 0;
	const_cast(dev->maxSwapInterval) = 1;
	const_cast(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;
}</framebuffer_device_t*>

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(buffer);
	private_module_t* m = reinterpret_cast(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(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<framebuffer->base;
	// find a free slot
	for (uint32_t i=0 ; ibufferMask |= (1LU<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。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved