1.pixman
pixman作为一个独立的第三方绘图库,其主要作用是根据操作模式及相应的数学算法对图形进行二次处理。常见的有Gradient(颜色渐变)、ROP(光栅操作)、矢量图操作、Alpha compositing、线性变换等。
我们涉及的有基本的ROP操作(主要是像素的位运算)、Alpha compositing以及缩放用到的线性变换等。
下图是
Alpha compositing的效果图:
这里的
Alpha compositing满足如下描述:
opaque采取:
over:SRC独占取SRC,DST独占取DST,重叠部分视具体操作而定
src-over:
SRC独占取SRC,DST独占取DST,重叠部分取SRC
dst-over:
SRC独占取SRC,DST独占取DST,重叠部分取DST
in:只取交集部分
src-in :取交集的SRC
dst
-in :取交集的DST
out:只取DST or SRC的区域,并且重叠部分去掉
src-out :只取SRC区域,并去掉交集部分
dst-out :只取DST区域,并去掉交集部分
atop:
只取DST or SRC的区域,而交集部分取对方
src-atop:取DST区域,但交集部分取SRC
dst
-atop:取SRC区域,但交集部分取DST
xor:顾名思义,即全取SRC and DST,去掉其交集部分
(图中可以很清楚的看到这些)
Partially Transparent类似
opaque,只不过多引入了Alpha
Src :SRC的色值
Dst :DST的色值
Sa :SRC的Alpha值,一般是(0~255),这里便于理解视作(0~1)
Da :DST的Alpha值,一般是(0~255),这里便于理解视作(0~1)
SRC独占,则有: Src * (1 - Da)
DST独占,则有: Dst * (1 - Sa)
重叠部分,取SRC,则有: Src * Da
重叠部分,取DST,则有: Dst * Sa
以src-over为例,则最终该点像素值为:
Src * (1 - Da) +
Src * Da +
Dst * (1 - Sa)
=Src +
Dst * (1 - Sa)
一般该点像素值满足公式: Color = Src * As + Dst * Ad
(
As和
Ad分别为Src跟Dst在该点的权重系数
)
线性变换:
采用3 x 3矩阵对图像进行线性缩放。
pixman_transform_init_scale()初始化一个3 x 3矩阵,用于缩放
pixman_transform_translate()涉及两个矩阵,一个是
pixman_transform_init_scale()获得的缩放倍数的矩阵,另一个用于操作Z轴,达到平移的目的。这两个矩阵平移矩阵作为左项,缩放矩阵作为右项。
现以像素点(10,10)为例,现在要将其按适量(4,5)进行平移,并进行放大两倍的操作,则有:
而填充的像素采用线性插值法,即初始点跟终点确定一条直线y = kx,可以得知其斜率,则可以根据这条直线在
初始点跟终点之间插入数值,例如:
x1 = 10, y1 = 10; x2 = 10, y2 = 20;之间插入4个点
则(x1, y1)与(x2, y2)确定一条直线 x = 10,则有这4个点:
(10,12),
(10,14),
(10,16),
(10,18)
当两个像素之间线性插值的时候,还以4个(红色部分)为例,在 0 到 10插入,则有:
0 2 4 6 8 10
2. 绘图流程
spice-gtk通过handle_message解析display channel SpiceMsgIN中的绘图命令
(draw_copy、draw-fill、draw-text、
draw_alpha_blend
等)、视频流命令
以及一些显示控制指令(primary-create、primary-destroy、display-invalidate、display-mark)、图像数据以及附带的图像信息(
格式、
大小、坐标、是否cache等信息),根据绘图命令做出相应的绘图操作。
spice-gtk与spice server建立连接,初始化各通道,monitor 在display channel 创建的时候根据monitor-config消息获取初始化参数。此时伴随的是primary canvas的创建(
surface 0),这是用户最终看到的结果界面。这块主canvas通过共享内存的形式创建,spice跟X window两个进程都可见,由于TDE绘图跟CPU是异步执行的,硬件绘图会待提交的TDE任务全部执行完毕
多出同步canvas的一个操作,每次有region_add()统计更新区域,
待所有的绘图操作结束完后发出invalidate信号会告诉Xorg去更新哪块region,同步更新主canvas的image buffer,通过XShmPutImage与Xserver交互,更新对应的
显示
区域。
3. 预备知识
TDE绘图操作流程:
因之前接触过HISI3716 这款芯片,对TDE还是有些认识,所以用起来有驾轻就熟的感觉。它的任务就是实现2D图形的加速,我们的硬件加速也正是基于TDE这个模块。
绘图操作:
Solid:不用多讲,即色值填充。
Blit:顾名思义,即图片的直接搬移,但这种搬移为了避免数据覆盖导致数据错误,所以规定了其方向性,这里分四种情况:
Tile:图片填充,即用一张基图片顺序的铺满目标区域,TDE的tile并不是设想的可以按照格式直接填充,而是将tile image剪切,将剪切后的tile image进行填充的,所以,这里需要额外对tile做细分。
Blend:前边介绍Pixman的时候涉及到了。
Convert:色彩转换
Colorkey:根据给定的色彩值,SRC等于该色值的地方将被透过去,显示DST内容
Stipple:画文字,字模做mask,用global_Alpha填充
Stipple-tiled:位图mask,用于类似艺术字这种不规则图形
Composite:三元操作,src+dst+mask
色彩格式:
ARGB、XRGB、RGB565.RGB1555、A1(多用于文字)、A8
SpiceImage以bitmap、解码后pixman image、cache、from surface四种形式存在,因此
涉及操作的有4个Hash table:canvas、palette、image、image lossy。通过操作这几个Hash table(canvas中包含绘图操作,其余主要是类似put、get)来完成对image cache的管理。
cache管理采用的lru(
近期最少使用
)算法,对应于cache_used(),
在前面几条指令中使用频繁的页面很可能在后面的几条指令中频繁使用。反过来说,已经很久没有使用的页面很可能在未来较长的一段时间内不会被用到。这个,就是著名的
局部性原理
。因此,我们只需要在每次调换时,找到最少使用的那个页面调出内存。这就是LRU算法的全部内容。
获取image 三种途径:从canvas、从image cache以及解码得到的
4.实现关键点
a. cache操作是主要的难点,实现在hisilicon_set_gpu_domain()、vram_prepare_cpu_access(),这里的理解不是很清晰,只做简单介绍。CPU跟TDE交互访问pixman image的时候,由于CPU跟TDE是异步执行的,这里要考虑cache是如何保证数据的同步的。当TDE要读写MMZ的时候,要确保MMZ中的内容是最新的,这时候需要CPU的cache做clean操作,将结果回写到内存,确保TDE读到的是最新内容,当CPU要访问TDE画好的image的时候,要invalidate这段cache,使其将内存中最新的绘图结果重新读到cache里面去,而当TDE跟CPU都只读这段MMZ的时候,不需要额外操作。
b. 影响
画图时间主要有三部分,socket wait、handle massage、sync,而sync是影响硬件绘图的主要部分,软件绘图不涉及sync canvas;
c. render_inertance()用于判断使用软件绘图还是硬件绘图,
硬件绘图跟软件绘图效率上持平的界点大概在 150 x 150(像素)大小,作为软硬件的切换,但问题是切换的时候会涉及cache操作HI_MMZ_Flush(clean、invalidate),这是一个很耗时的操作,而spice-gtk桌面绘图一般操作中的实际情况是,满足硬件绘图条件的部分很少(1/10左右),甚者,这个区分软硬件效率高低的条件(150x150)目前是依据region划分的,这些region有时候可能还会细分为更多个rects,TDE是根据rects来操作的,这样的话硬件快于软件的部分将会少之又少,这是一个不可规避的问题。这期间可能是刚切换到硬件绘图执行了一条绘图指令又迅速切换至软件绘图,这样的效果可想而知。
硬件要初始化一个绘图命令并提交内核去执行这个过程也是耗时的,即
使TDE真正绘图极快忽略不计时间的话,那这个初始化过程的时间还是不可避免的,打个比方,以solid一张图片大小50x50倘若初始化一条solid指令耗时100us,真正的TDE绘图时间忽略不计,而软件solid可能只需要15us,当图片为200x200时可能hard solid要300us,软件要350us,试想一下,满足软件快的部分是绝大部分,而满足硬件快的假设占1/10,软件15*90%+350*10%=48.5,硬件100*90%+300*10%=120;这样看来硬件明显慢于软件的,更甚至满足硬件快的可能连1/10都不可能达到(region细分rects)。
d. 如何解释CPU利用率硬件比软件高?
e.
去掉的那些sync部分为什么没有对跑WinTach产生明显的影响?
:这应该是整个的Paint可能需要swap的次数比较少,因此呢Flush所占用的时间相对整个Paint过程所用的时间比例就很小了,比如跑一个paint过程用10s而整个多出来的Flush可能只占200ms,200/10/1000=2%,可见对跑wintach而言,影响不大就说得通了。Paint过程有一段大量用stoke操作,stoke中包含很多极小的rects,用到的TDE是solid操作。