基于Hi3516AV200/Hi3519V101的Qt绘图优化

首先,Qt5.X移植到海思平台以及linuxfb插件的修改可以参考https://www.cnblogs.com/chay/p/10431403.html一文,本文介绍的优化过程与之相似,只是结合了具体的项目进行了分析和改进,本文思路应广泛适用于海思平台。

本文包含了大量的个人见解,如果有理解错误或者片面的地方,还请大家不吝惜时间和精力给予指正。

  • 概述

Qt的源码中通过 Q<pluginType>Factory、Q<pluginType>Plugin 和 Q<pluginType> 这三个类实现了Qt的插件加载机制,

这个机制可用于加载特定种类的插件。Arm上Qt绘图一般会使用linuxfb来进行画图,由于Hi3519V101/Hi3516AV200的cpu主频较低和Qt绘图效率的问题,在需要绘制全屏UI或者UI刷新率较高时,会有明显地切屏和撕裂现象,在四核的Hi3559AV100平台上,切屏现象是非常不明显的。

  • 优化点

将linux frambuffer替换成Hisilicon Framebuffer,并使用TDE进行快速位图搬移/缩放

从qt源码可以看出,linuxfb只map出了一片绘图缓存区并没有特别的buffer管理机制,这意味着你的画图过程将会全程展现在使用者面前,在高性能的cpu下这个过程可能并不明显,但是在Hi3519V101/Hi3516AV200平台下就会出现很明显的切屏(理论上大核主频也有1G了,这里我不是很理解,难道是Qt本身的绘图机制效率低了?)。

    uchar *data = (unsigned char *)mmap(0, mMmap.size, PROT_READ | PROT_WRITE, MAP_SHARED, mFbFd, 0);
    if ((long)data == -1) {
        qErrnoWarning(errno, "Failed to mmap framebuffer");
        return false;
    }

    mMmap.offset = geometry.y() * mBytesPerLine + geometry.x() * mDepth / 8;
    mMmap.data = data + mMmap.offset;

    QFbScreen::initializeCompositor();
    mFbScreenImage = QImage(mMmap.data, geometry.width(), geometry.height(), mBytesPerLine, mFormat);

下面贴下使用hifb的特性,摘自海思的《HIFB开发指南》

HiFB 支持以下的 Linux Framebuffer 标准功能:
将物理显存映射(或解除映射)到虚拟内存空间。
像操作普通文件一样操作物理显存。
设置硬件显示分辨率和象素格式,每个叠加图形层的支持的最大分辨率和象素格
式可以通过支持能力接口获取。

从物理显存的任何位置进行读、写、显示等操作。
在叠加图形层支持索引格式的情况下,支持设置和获取 256 色的调色板。
HiFB 增加以下的扩展功能:
设置和获取叠加图形层的 Alpha 值。
设置和获取叠加图形层的 colorkey 值。
设置当前叠加图形层的起始位置(相对于屏幕原点的偏移)。
设置和获取当前叠加图形层的显示状态(显示/隐藏)。
通过模块加载参数配置 HiFB 的物理显存大小和管理叠加图形层的数目。
设置和获取抗闪烁功能的状态。
设置和获取预乘模式。
设置和获取压缩模式的状态。
设置和获取内存检测的状态。
设置/获取图形层刷新类型(0 buffer、1 buffer 与 2 buffer)。
支持软鼠标的一系列操作。
 

下面详细介绍优化的点

  1. 使用带cache的mmz内存提高buffer的存取效率。
    //给Qt绘制的位图buffer(虚拟地址)申请一块物理地址 图像格式ARGB8888
        QImageData * QtPic = mScreenImage->data_ptr();
    	free(QtPic->data);
    	//QFbScreen::setComposite();
    
    	if (HI_FAILURE == HI_MPI_SYS_MmzAlloc_Cached(&g_Phyaddr, ((void**)&(QtPic->data)),
    										  NULL, NULL, width * height * 4))
    
    	{
    		qWarning("allocate memory (maxW*maxH*2 bytes) failed\n");
    		close(mFbFd);
    		return false;
    	}
    //给hifb的画布buffer申请一块物理内存 图像格式ARGB1555
    if (HI_FAILURE == HI_MPI_SYS_MmzAlloc_Cached(&g_CanvasAddr, ((void**)&pBuf),
    									  NULL, NULL, width * height * 2))
    	{
    		qWarning("allocate memory (maxW*maxH*2 bytes) failed\n");
    		close(mFbFd);
    		return false;
    	}			
  2. 使用hifb提供的图形层刷新模式。

    HiFB 为上层用户提供了一套完整的刷新方案,称为 FB 的扩展模式。在对系统性能、内存、图形显示效果各方面综合衡量的基础上,可根据需要选择合适的刷新方案。目前提供的刷新类型有:

    0 buffer(即 HIFB_LAYER_BUF_NONE)

    上层用户的绘制 buffer 即为显示 buffer。该刷新类型可以节省内存消耗,速度也最快,但是用户会看到图形的绘制过程。

    1 buffer(即 HIFB_LAYER_BUF_ONE)

    显示 buffer 由 HIFB 提供,因此需要一定的内存。该刷新类型是对显示效果和内存需求的一种折中考虑。但是会有锯齿。

    2 buffer(即HIFB_LAYER_BUF_DOUBLE和 HIFB_LAYER_BUF_DOUBLE_IMMEDIATE)

    显示 buffer 由 HIFB 提供。和前面的刷新类型相比,其要求内存最多,但图形显示效果最好                                                                 
    	stLayerinfo.BufMode = HIFB_LAYER_BUF_DOUBLE_IMMEDIATE;
    	stLayerinfo.u32Mask = HIFB_LAYERMASK_BUFMODE;
    	stLayerinfo.u32CanvasWidth = width;
    	stLayerinfo.u32CanvasHeight = height;
    	stLayerinfo.u32DisplayWidth = width;
    	stLayerinfo.u32DisplayHeight = height;
    	stLayerinfo.s32XPos = 0;
    	stLayerinfo.s32YPos = 0;
    	stLayerinfo.u32Mask |= HIFB_LAYERMASK_DISPSIZE | HIFB_LAYERMASK_POS|HIFB_LAYERMASK_CANVASSIZE;
    
    	if (ioctl(mFbFd, FBIOPUT_LAYER_INFO, &stLayerinfo) < 0)
    	{
    		printf("FBIOPUT_LAYER_INFO failed!\n");
    		close(mFbFd);
    		return HI_NULL;
    	}
    
    	/*enable fb to play*/
    	bShow = HI_TRUE;
    	if (ioctl(mFbFd, FBIOPUT_SHOW_HIFB, &bShow) < 0)
    	{
    		printf("FBIOPUT_SHOW_HIFB failed!\n");
    		close(mFbFd);
    		return HI_NULL;
    	}	
    
    
    

使用TDE进行快速位图缩放(色彩空间转换)来节省带宽和加速。Qt的QImage类支持的图像格式包括QImage::Format_RGB32(ARGB8888),QImage::Format_RGB888,QImage::Format_RGB16(RGB565),QImage::Format_RGB555,QImage::Format_RGB444。而hifb支持的图形层格式有ARGB8888,ARGB1555,ARGB4444,由于项目需求,绘制的UI一般都带有透明度分量,那么在没有进过特殊处理的情况下,我们只能使用ARGB8888的方式来进行画图。那么我们的显存就需要申请显示分辩率x4的大小,在相同的图形下,也意味着绘图效率的倍增。而TDE(Two Dimensional Engine)利用硬件为 OSD(On Screen Display)和 GUI(Graphics User Interface)提供快速的图形绘制功能。其接口可以直接操作物理地址,将Qt的绘制buffer直接快速搬到hifb的画布上

QRegion QHiFbScreen::doRedraw()
{
    QRegion touched = QFbScreen::doRedraw();

    TDE2_RECT_S stSrcRect, stDstRect;
    TDE2_SURFACE_S stSrc, stDst;	
    HI_S32 s32Ret;
    HI_U32 u32HideScreenPhy = 0;
	HI_U32 u32showScreenPhy = 0;
    HI_U32 u32FixScreenStride = 0;
    TDE_HANDLE s32Handle;
    HIFB_BUFFER_S stCanvasBuf;
	static HIFB_ROTATE_MODE_E prev_rotate = HIFB_ROTATE_NONE;
	struct timeval start;
	struct timeval end;
    if (touched.isEmpty())
        return touched;

	QImageData * QtPic = mScreenImage->data_ptr();

    fb_var_screeninfo vinfo;
    fb_fix_screeninfo fix;
	
    if (ioctl(mFbFd, FBIOGET_FSCREENINFO, &fix) != 0) {
        qErrnoWarning(errno, "Error reading fixed information");
    }
	if (ioctl(mFbFd, FBIOGET_VSCREENINFO, &vinfo)) {
		qErrnoWarning(errno, "Error reading variable information");
	}	
    stCanvasBuf.stCanvas.u32PhyAddr = g_CanvasAddr;
    stCanvasBuf.stCanvas.u32Height = vinfo.yres;
    stCanvasBuf.stCanvas.u32Width = vinfo.xres;

	stCanvasBuf.stCanvas.u32Pitch = vinfo.xres * 2;
	stCanvasBuf.stCanvas.enFmt = HIFB_FMT_ARGB1555;


	const QVector<QRect> rects = touched.rects();


	int x,y,w,h;
	rects[0].getRect(&x,&y,&w,&h);
	HIFB_ROTATE_MODE_E rotate;


	/* 0. open tde */
	stSrcRect.s32Xpos = x;
	stSrcRect.s32Ypos = y;
	stSrcRect.u32Height = h;
	stSrcRect.u32Width = w;

	stDstRect.s32Xpos = x;
	stDstRect.s32Ypos = y;
			


		
	stDstRect.u32Height = h;
	stDstRect.u32Width = w;

	stDst.enColorFmt = TDE2_COLOR_FMT_ARGB1555;
	stDst.u32Stride = vinfo.xres*2;
	stSrc.enColorFmt = TDE2_COLOR_FMT_ARGB8888;
	stSrc.u32Stride = vinfo.xres*4;

	stDst.u32Width = vinfo.xres;
	stDst.u32Height = vinfo.yres;
	stDst.u32PhyAddr = stCanvasBuf.stCanvas.u32PhyAddr;
		
	stSrc.u32Width = vinfo.xres;
	stSrc.u32Height = vinfo.yres;
	stSrc.u32PhyAddr = g_Phyaddr;
	stSrc.bAlphaExt1555 = HI_TRUE;
	stSrc.bAlphaMax255 = HI_TRUE;
	stSrc.u8Alpha0 = 0XFF;
	stSrc.u8Alpha1 = 0XFF;
	

	s32Ret = ioctl(mFbFd, FBIOGET_VBLANK_HIFB);

	s32Handle = HI_TDE2_BeginJob();

	HI_MPI_SYS_MmzFlushCache(g_CanvasAddr, (void*)pBuf,1920*1080*2);

	if (HI_ERR_TDE_INVALID_HANDLE == s32Handle)
	{
		printf("start job failed!\n");
		HI_MPI_SYS_MmzFree(g_Phyaddr, QtPic->data);
		g_Phyaddr = 0;
		
		HI_MPI_SYS_MmzFree(g_CanvasAddr, pBuf);
		g_CanvasAddr = 0;

		close(mFbFd);
	}
//开启TDE快速位图缩放任务,支持位图从src到dst进行缩放,图像格式转换
	s32Ret = HI_TDE2_QuickResize(s32Handle, &stSrc, &stSrcRect, &stDst, &stDstRect);
	if (s32Ret < 0)
	{
		printf("HI_TDE2_QuickCopy:%d failed,ret=0x%x!\n", __LINE__, s32Ret);
		HI_TDE2_CancelJob(s32Handle);
		HI_MPI_SYS_MmzFree(g_Phyaddr, QtPic->data);
		g_Phyaddr = 0;
		
		HI_MPI_SYS_MmzFree(g_CanvasAddr, pBuf);
		g_CanvasAddr = 0;

		close(mFbFd);
	}
	
	/* 3. submit job */
	s32Ret = HI_TDE2_EndJob(s32Handle, HI_FALSE, HI_FALSE, -1);
	if (s32Ret < 0)
	{
		printf("Line:%d,HI_TDE2_EndJob failed,ret=0x%x!\n", __LINE__, s32Ret);
		HI_TDE2_CancelJob(s32Handle);
		HI_MPI_SYS_MmzFree(g_Phyaddr, QtPic->data);
		g_Phyaddr = 0;
		
		HI_MPI_SYS_MmzFree(g_CanvasAddr, pBuf);
		g_CanvasAddr = 0;

		close(mFbFd);
	}

//设置当前画布的大小	
	stCanvasBuf.UpdateRect.x = x;
	stCanvasBuf.UpdateRect.y = y;

	stCanvasBuf.UpdateRect.w = w;
	stCanvasBuf.UpdateRect.h = h;


	HI_MPI_SYS_MmzFlushCache(g_CanvasAddr, (void*)pBuf,1920*1080*2);

	s32Ret = ioctl(mFbFd, FBIO_REFRESH, &stCanvasBuf);


    return touched;
}
  • 不足

结合我自己的实践,HIFB_LAYER_BUF_DOUBLE_IMMEDIATE模式下式绝对不会出现切屏的现象,带相对的牺牲的了画图速率,如果对刷新率和内存没有什么要求,这种模式是最保险的。其他三种模式的速度差不多,但是当图形刷新率特别高的时候还是会出现没画完就显示的情况,估计还是cpu本身性能不够,导致图形渲染速度慢,如果实时性要求高的UI建议还是用VGS模块去绘制。

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值