kaa是kdrive的硬件加速的框架。
还是直接看代码吧。
hw/kdrive/src/kaa.c里面都是kaa的xserver这边的实现了,
然后就是driver那边的实现了。
我们硬件加速实际上就是实现driver那边,也就是说具体的工作实际上都是在driver里面做的。
注册回调函数到xserver那边,xserver那边会负责调用,然后就会调用到我们driver里面来。
这个driver和xserver是在同一个进程空间的,无论是kdrive还是xorg都一样。
对于kdrive来说,这个driver是直接编译到xserver里面也就是Xfbdev里面
对于xorg来说这个是一个单独的模块,xorg这边的框架是从kaa学习过来的,叫exa。
xorg这边加速的模块exa的实现都在xf86-video-ati等等类似这样的里面
具体下载的路径在
下面有很多video的驱动,基本上都是exa的实现。
这些东西编译完成之后都是so文件,
以intel的为例。
ailantian@vax:/mnt/sdb1/ubd/soft/xorg/temp/xorg-server-1.5.3$ dpkg -L xserver-xorg-video-intel
/.
/usr
/usr/share
/usr/share/doc
/usr/share/doc/xserver-xorg-video-intel
/usr/share/doc/xserver-xorg-video-intel/copyright
/usr/share/doc/xserver-xorg-video-intel/changelog.gz
/usr/share/doc/xserver-xorg-video-intel/README.gz
/usr/share/doc/xserver-xorg-video-intel/changelog.Debian.gz
/usr/share/xserver-xorg
/usr/share/xserver-xorg/pci
/usr/share/xserver-xorg/pci/intel.ids
/usr/share/man
/usr/share/man/man4
/usr/share/man/man4/intel.4.gz
/usr/share/bug
/usr/share/bug/xserver-xorg-video-intel
/usr/lib
/usr/lib/xorg
/usr/lib/xorg/modules
/usr/lib/xorg/modules/drivers
/usr/lib/xorg/modules/drivers/ch7017.so
/usr/lib/xorg/modules/drivers/ch7xxx.so
/usr/lib/xorg/modules/drivers/intel_drv.so
/usr/lib/xorg/modules/drivers/ivch.so
/usr/lib/xorg/modules/drivers/sil164.so
/usr/lib/xorg/modules/drivers/tfp410.so
/usr/lib/libI810XvMC.so.1.0.0
/usr/lib/libIntelXvMC.so.1.0.0
/usr/share/man/man4/i810.4.gz
/usr/share/bug/xserver-xorg-video-intel/script
/usr/lib/xorg/modules/drivers/i810_drv.so
/usr/lib/libI810XvMC.so
/usr/lib/libI810XvMC.so.1
/usr/lib/libIntelXvMC.so
/usr/lib/libIntelXvMC.so.1
ailantian@vax:/mnt/sdb1/ubd/soft/xorg/temp/xorg-server-1.5.3$
比如i810的驱动。
/usr/lib/xorg/modules/drivers/i810_drv.so
其他的xvmc等等这个是扩展的实现了,如果不关注的话,可以不看这些东西。
好的,说远了,xorg的另外分出来说吧,这里还是看看kdrive的,xorg和kdrive差不多。
static const GCOps kaaOps = {
kaaFillSpans,
KdCheckSetSpans,
KdCheckPutImage,
kaaCopyArea,
KdCheckCopyPlane,
KdCheckPolyPoint,
KdCheckPolylines,
KdCheckPolySegment,
miPolyRectangle,
KdCheckPolyArc,
miFillPolygon,
kaaPolyFillRect,
miPolyFillArc,
miPolyText8,
miPolyText16,
miImageText8,
miImageText16,
kaaImageGlyphBlt,
KdCheckPolyGlyphBlt,
KdCheckPushPixels,
};
GCFuncs kaaGCFuncs = {
kaaValidateGC,
miChangeGC,
miCopyGC,
miDestroyGC,
miChangeClip,
miDestroyClip,
miCopyClip
};
上面这些都是最基本的gc的函数了,实际上图形系统里面都是使用gc的,这样可以避免传递的参数太多,
如果感兴趣的可以看看microwindows的介绍。虽然很简陋但是基本的图形系统的设计还是可以看出来的。
对xserver上层来说调用的都是gc的操作函数,不同的gc是不一样的,所以这里有机会给我们注册这些基本的函数了。
我们还是找一个驱动来看看,我写的部分也是照抄的,因为模式都定的。
我们还是看ati的实现吧,开源的来说,ati和intel的做的不错。
ati_stub.c这个是给上层的一些最基本的接口。我们可以不用管。
一般来说,kaa的实现,都是在XXX_draw.c里面来实现的,比如ati_draw.c
我们先看看最核心的一个函数。这个drawinit是注册回调函数的。
如果大家觉得ati的做的太复杂,可以看看epson的实现,这个实现的加速比较少,所以看起来比较简单。
Bool
ATIDrawInit(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
ATICardInfo(pScreenPriv);
ErrorF("Screen: %d/%d depth/bpp\n", pScreenPriv->screen->fb[0].depth,
pScreenPriv->screen->fb[0].bitsPerPixel);
RegisterBlockAndWakeupHandlers(ATIBlockHandler, ATIWakeupHandler,
pScreen);
#ifdef USE_DRI
atis->using_dri = ATIDRIScreenInit(pScreen);
#endif /* USE_DRI */
memset(&atis->kaa, 0, sizeof(KaaScreenInfoRec));
atis->kaa.waitMarker = ATIWaitMarker;
atis->kaa.PrepareSolid = ATIPrepareSolid;
atis->kaa.Solid = ATISolid;
atis->kaa.DoneSolid = ATIDoneSolid;
atis->kaa.PrepareCopy = ATIPrepareCopy;
atis->kaa.Copy = ATICopy;
atis->kaa.DoneCopy = ATIDoneCopy;
/* Other acceleration will be hooked in in DrawEnable depending on
* what type of DMA gets initialized.
*/
atis->kaa.flags = KAA_OFFSCREEN_PIXMAPS;
if (atic->is_radeon) {
atis->kaa.offsetAlign = 1024;
atis->kaa.pitchAlign = 64;
} else {
/* Rage 128 compositing wants power-of-two pitches. */
atis->kaa.flags |= KAA_OFFSCREEN_ALIGN_POT;
atis->kaa.offsetAlign = 32;
/* Pitch alignment is in sets of 8 pixels, and we need to cover
* 32bpp, so 32 bytes.
*/
atis->kaa.pitchAlign = 32;
}
kaaInitTrapOffsets(8, sample_offsets_x, sample_offsets_y, 0.0, 0.0);
sample_count = (1 << 8) - 1;
if (!kaaDrawInit(pScreen, &atis->kaa))
return FALSE;
return TRUE;
}
可以看出,实际上kaa的加速分为三类操作,这个也是kaa的核心,无非是Solid,Copy,Composite
这三类操作如下解释
Solid,
就是填充一个区域,比如我们创建一个window的时候背景一般都是白色或者灰色,实际上这个是需要填充的,注意这里的solid并不是
我们想像的简单的solid,这里有mask,有fg,还有alu,怎么说呢,实际上图形学是比较复杂的,也就是说,我们往一个区域里面画的时候,实际上这个
东西应该说是一个状态机,比如画之前是什么状态,我画什么上去,画完之后是什么效果,这个东西我一句两句实在是说不清楚。
mask就是把某些位给mask掉,fg是前景色,alu,这个是画的方法,就是说,是or,xor等等,定义的比较多,如果大家像了解,需要先补一补图形学的只是,
这个不是这里要说明的要点,不过总之,一般pc的硬件可以很简单的处理这样的状况。
Copy.
我们简单的理解,就是比如我们有一个窗口,我们现在拿鼠标来拖动这个窗口,实际上在xserver上面是这么处理的,就是把这个区域copy到新的区域,当然这个copy
的速度很快,所以我们感觉不到闪,但是如果系统很慢,或者屏幕很大,或者没有任何硬件加速,我们拖动的时候就会看到大家所说的屏幕撕开的情况,其实就是copy的速度
太慢了,实际上copy来说就是一行一行的copy,这里的处理有些是linar buffer有些是tiled buffer,不过有些pc的硬件可以很好的处理这不同的状况。
屏幕大了之后,copy的速度没变的话,由于数据量的变大,导致我们绘图看起来比较慢,所以看到上部先显示,然后下部后显示,就是我们看到的屏幕撕裂了,当然这个说的是
没有硬件加速的情况,我们这里说的是online buffer的情况,没有swap buffer的情况,kaa里面没有double buffer的处理。当然copy和solid一样,
都要考虑采用什么画法。alu.
Composite
Composite是这三种操作里面最复杂的一种了,本身composite操作就种类多。可以参考如下链接
本身composite的操作就比较多,再加上状态处理,组合就更多了,另外composite里面还要加上trasform的处理,这样导致这个地方的处理很复杂,2d的engine一般
没法完全处理这些,只能靠3d的engine来处理。
当然对应的每个类别里面都有prepare, do, done三类处理,以solid为例
PrepareSolid
Solid
DoneSolid
composite复杂一些,会多一些操作,比如CheckComposite的操作,如果发现不支持的话,就会采用软件的实现。
如果没有硬件的实现,就是使用xserver自己的软件实现,以前都是在xserver里面实现,好像现在倾向于拿到pixman这个库里面去处理了。
所以这个库现在更新的很快,有很多cpu相关的优化,arm的优化也比较多0.15.10里面加入的noen优化指令比较多,其实这个优化最早是从maemo的项目,ti的omap的cpu里面来的。
因为ti的xv的硬件有问题,暂时只能靠软件来实现。不过ti的3d的驱动做的还不错,powerVR那边支持的很好.高通的相对落后不少。
剩下的还有一些函数
比如
UploadToScreen
UploadToScratch
自己感觉好像用的频率不高。
BlockHandler
WakeupHandler
这两个函数是如果硬件需要lock的时候就会用,不过好像有些硬件不需要。留空就行了。
然后我们要实现的就是上面的Solid,Copy,Composite函数了,
比如cairo的Xlib的后端就会调用XrenderComposite,然后就会调用Composite这个函数了,如果这个函数做的快,自然 cairo就快了。
当然cairo还可以使用glitz的后端,只要有opengl就可以了.不过embedded一般都没有opengl了,不过exa/kaa这个还是可以实现的。毕竟只是2d的东西
当然也可以使用3d的engine来实现。
至于具体的实现,这里不多说了,根据硬件的功能,完整这个目的就行了,这里只是说说架构。
不同的硬件是不一样的,另外就是对上层的接口也不一样。
举一个例子来说明调用流程
kaaCopyWindow这个是gc里面的函数,注册到xserver里面去了。
当copywindow发生的时候kaa的实现就会调用kaaCopyWindow
调用下面的函数。
fbCopyRegion (&pPixmap->drawable, &pPixmap->drawable,
0,
&rgnDst, dx, dy, kaaCopyNtoN, 0, 0);
会调用这个函数。
kaaCopyNtoN
这个函数里面就会调用copy的硬件回调函数了,具体如下kaa.c
这是一个标准的硬件处理流程,其他的流程也类似。
if (pScreenPriv->enabled &&
(pSrcPixmap = kaaGetOffscreenPixmap (pSrcDrawable, &src_off_x, &src_off_y)) &&
(pDstPixmap = kaaGetOffscreenPixmap (pDstDrawable, &dst_off_x, &dst_off_y)) &&
(*pKaaScr->info->PrepareCopy) (pSrcPixmap,
pDstPixmap,
dx,
dy,
pGC ? pGC->alu : GXcopy,
pGC ? pGC->planemask : FB_ALLONES))
{
while (nbox--)
{
(*pKaaScr->info->Copy) (pbox->x1 + dx + src_off_x,
pbox->y1 + dy + src_off_y,
pbox->x1 + dst_off_x, pbox->y1 + dst_off_y,
pbox->x2 - pbox->x1,
pbox->y2 - pbox->y1);
pbox++;
}
(*pKaaScr->info->DoneCopy) ();
kaaMarkSync (pDstDrawable->pScreen);
大家也能看到,一次preparecopy之后会有多次的copy,最后是donecopy。
如果没有硬件加速的话,走的就是这边
else
{
kaaWaitSync (pDstDrawable->pScreen);
fbCopyNtoN (pSrcDrawable, pDstDrawable, pGC,
pbox, nbox, dx, dy, reverse, upsidedown,
bitplane, closure);
}
然后就跑到pixman里面去了,fbimage,fbcopy等等里面的实现,现在看起来都慢慢移动到pixman里面去了,都是基本的像素操作,
以后xorg只管基本的逻辑了。具体的绘图或者加速,都在外边了。
因为driver一般都做成一个,所以xvideo等实现也一般就做在这个driver里面,核心的文件是ati_draw.c
因为所有的回调函数的具体实现都是在这个里面实现的,注册回调函数也是在这个函数里面实现的。
另外一个重要的文件就是ati.c这个和框架有点关系,但是也基本是一个数组,然后填充数据就行了。
kdrive和xorg不同,kdrive是直接操作fb空间的,所以这些驱动都是基于framebuffer的
kaa的内存管理也是一样。
KdCardFuncs ATIFuncs = {
ATICardInit, /* cardinit */
ATIScreenInit, /* scrinit */
ATIInitScreen, /* initScreen */
ATIFinishInitScreen, /* finishInitScreen */
ATICreateResources, /* createRes */
ATIPreserve, /* preserve */
ATIEnable, /* enable */
ATIDPMS, /* dpms */
ATIDisable, /* disable */
ATIRestore, /* restore */
ATIScreenFini, /* scrfini */
ATICardFini, /* cardfini */
ATICursorInit, /* initCursor */
ATICursorEnable, /* enableCursor */
ATICursorDisable, /* disableCursor */
ATICursorFini, /* finiCursor */
ATIRecolorCursor, /* recolorCursor */
ATIDrawInit, /* initAccel */
ATIDrawEnable, /* enableAccel */
ATIDrawDisable, /* disableAccel */
ATIDrawFini, /* finiAccel */
ATIGetColors, /* getColors */
ATIPutColors, /* putColors */
};
这里是所有将要被调用的函数,没有注册的就是空NULL,
ATIScreenInit
里面的主要函数就是这个函数调用,
#ifdef KDRIVEFBDEV
if (atic->use_fbdev) {
success = fbdevScreenInitialize(screen,
&atis->backend_priv.fbdev);
}
#endif
这个函数里面的主要实现
static Bool
ATIInitScreen(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATICardInfo(pScreenPriv);
#ifdef XV
ATIInitVideo(pScreen);
#endif
return atic->backend_funcs.initScreen(pScreen);
}
一个就是xvideo的初始化,大家看到其实就只有一行代码。
其实这里都有默认的函数,默认的就是fbdev里面的函数。
ati还是有硬件加速的reg的这些的处理,实际上对于之前我们实现的driver来说,这些东西都完全省略了,因为fb这边我基本不用任何处理。
只要使用MDP对fb来做上面的Solid,Copy,Composite的处理
对应init的函数我们也有fina的函数,比如deregister Xvideo
static void
ATIScreenFini(KdScreenInfo *screen)
{
ATIScreenInfo *atis = (ATIScreenInfo *)screen->driver;
ATICardInfo *atic = screen->card->driver;
#ifdef XV
ATIFiniVideo(screen->pScreen);
#endif
atic->backend_funcs.scrfini(screen);
xfree(atis);
screen->driver = 0;
}
这里其实很多函数都是不需要实现的,可以参考epson的实现,epson实现的功能比较简单。
ati的显卡相对来说复杂一些,但是对于嵌入式来讲没有那么复杂的显示芯片(2d,3d集成)。
然后大家可能问这些个回调函数都是在什么地方调用的。
这个就是kdrive了。
hw/kdrive/src/kdrive.c里面会调用。这个是kdrive的主程序。
比如kdrive的初始化screen的函数
void
KdInitScreen (ScreenInfo *pScreenInfo,
KdScreenInfo *screen,
int argc,
char **argv)
{
KdCardInfo *card = screen->card;
(*card->cfuncs->scrinit) (screen);
if (!card->cfuncs->initAccel)
screen->dumb = TRUE;
if (!card->cfuncs->initCursor)
screen->softCursor = TRUE;
}
比如下面,都会调用这些回调函数。
Bool
KdScreenInit(int index, ScreenPtr pScreen, int argc, char **argv)
{
KdScreenInfo *screen = kdCurrentScreen;
KdCardInfo *card = screen->card;
KdPrivScreenPtr pScreenPriv;
int fb;
/*
* note that screen->fb is set up for the nominal orientation
* of the screen; that means if randr is rotated, the values
* there should reflect a rotated frame buffer (or shadow).
*/
Bool rotated = (screen->randr & (RR_Rotate_90|RR_Rotate_270)) != 0;
int width, height, *width_mmp, *height_mmp;
KdAllocatePrivates (pScreen);
pScreenPriv = KdGetScreenPriv(pScreen);
if (!rotated)
{
width = screen->width;
height = screen->height;
width_mmp = &screen->width_mm;
height_mmp = &screen->height_mm;
}
else
{
width = screen->height;
height = screen->width;
width_mmp = &screen->height_mm;
height_mmp = &screen->width_mm;
}
screen->pScreen = pScreen;
pScreenPriv->screen = screen;
pScreenPriv->card = card;
for (fb = 0; fb < KD_MAX_FB && screen->fb[fb].depth; fb++)
pScreenPriv->bytesPerPixel[fb] = screen->fb[fb].bitsPerPixel >> 3;
pScreenPriv->dpmsState = KD_DPMS_NORMAL;
#ifdef PANORAMIX
dixScreenOrigins[pScreen->myNum] = screen->origin;
#endif
if (!monitorResolution)
monitorResolution = 75;
/*
* This is done in this order so that backing store wraps
* our GC functions; fbFinishScreenInit initializes MI
* backing store
*/
if (!fbSetupScreen (pScreen,
screen->fb[0].frameBuffer,
width, height,
monitorResolution, monitorResolution,
screen->fb[0].pixelStride,
screen->fb[0].bitsPerPixel))
{
return FALSE;
}
/*
* Set colormap functions
*/
pScreen->InstallColormap = KdInstallColormap;
pScreen->UninstallColormap = KdUninstallColormap;
pScreen->ListInstalledColormaps = KdListInstalledColormaps;
pScreen->StoreColors = KdStoreColors;
pScreen->SaveScreen = KdSaveScreen;
pScreen->CreateWindow = KdCreateWindow;
#if KD_MAX_FB > 1
if (screen->fb[1].depth)
{
if (!fbOverlayFinishScreenInit (pScreen,
screen->fb[0].frameBuffer,
screen->fb[1].frameBuffer,
width, height,
monitorResolution, monitorResolution,
screen->fb[0].pixelStride,
screen->fb[1].pixelStride,
screen->fb[0].bitsPerPixel,
screen->fb[1].bitsPerPixel,
screen->fb[0].depth,
screen->fb[1].depth))
{
return FALSE;
}
}
else
#endif
{
if (!fbFinishScreenInit (pScreen,
screen->fb[0].frameBuffer,
width, height,
monitorResolution, monitorResolution,
screen->fb[0].pixelStride,
screen->fb[0].bitsPerPixel))
{
return FALSE;
}
}
/*
* Fix screen sizes; for some reason mi takes dpi instead of mm.
* Rounding errors are annoying
*/
if (*width_mmp)
pScreen->mmWidth = *width_mmp;
else
*width_mmp = pScreen->mmWidth;
if (*height_mmp)
pScreen->mmHeight = *height_mmp;
else
*height_mmp = pScreen->mmHeight;
/*
* Plug in our own block/wakeup handlers.
* miScreenInit installs NoopDDA in both places
*/
pScreen->BlockHandler = KdBlockHandler;
pScreen->WakeupHandler = KdWakeupHandler;
#ifdef RENDER
if (!fbPictureInit (pScreen, 0, 0))
return FALSE;
#endif
if (card->cfuncs->initScreen)
if (!(*card->cfuncs->initScreen) (pScreen))
return FALSE;
if (!screen->dumb && card->cfuncs->initAccel)
if (!(*card->cfuncs->initAccel) (pScreen))
screen->dumb = TRUE;
if (screen->off_screen_base < screen->memory_size)
KdOffscreenInit (pScreen);
#ifdef PSEUDO8
(void) p8Init (pScreen, PSEUDO8_USE_DEFAULT);
#endif
if (card->cfuncs->finishInitScreen)
if (!(*card->cfuncs->finishInitScreen) (pScreen))
return FALSE;
#if 0
fbInitValidateTree (pScreen);
#endif
#if 0
pScreen->backingStoreSupport = Always;
miInitializeBackingStore (pScreen);
#endif
/*
* Wrap CloseScreen, the order now is:
* KdCloseScreen
* miBSCloseScreen
* fbCloseScreen
*/
pScreenPriv->CloseScreen = pScreen->CloseScreen;
pScreen->CloseScreen = KdCloseScreen;
pScreenPriv->CreateScreenResources = pScreen->CreateScreenResources;
pScreen->CreateScreenResources = KdCreateScreenResources;
if (screen->softCursor ||
!card->cfuncs->initCursor ||
!(*card->cfuncs->initCursor) (pScreen))
{
/* Use MI for cursor display and event queueing. */
screen->softCursor = TRUE;
miDCInitialize(pScreen, &kdPointerScreenFuncs);
}
if (!fbCreateDefColormap (pScreen))
{
return FALSE;
}
KdSetSubpixelOrder (pScreen, screen->randr);
/*
* Enable the hardware
*/
if (!kdEnabled)
{
kdEnabled = TRUE;
if(kdOsFuncs->Enable)
(*kdOsFuncs->Enable) ();
}
if (screen->mynum == card->selected)
{
if(card->cfuncs->preserve)
(*card->cfuncs->preserve) (card);
if(card->cfuncs->enable)
if (!(*card->cfuncs->enable) (pScreen))
return FALSE;
pScreenPriv->enabled = TRUE;
if (!screen->softCursor && card->cfuncs->enableCursor)
(*card->cfuncs->enableCursor) (pScreen);
KdEnableColormap (pScreen);
if (!screen->dumb && card->cfuncs->enableAccel)
(*card->cfuncs->enableAccel) (pScreen);
}
return TRUE;
}
那么KdInitScreen又是谁调用呢,
KdInitOutput,这个函数在我们每个显卡驱动里面都会被调用,在ati_stub.c里面
void
InitOutput(ScreenInfo *pScreenInfo, int argc, char **argv)
{
KdInitOutput(pScreenInfo, argc, argv);
}
所以这里一会在driver里面一会在xserver(kdrive)里面,大家都觉得比较混乱,
实际上就是这样的,因为kdrive自己会提供一些函数来给driver调用,而driver本来就是xserver一个进程空间的东西,所以就两边跳转,
这个其实和linux kernel里面的file system实现是很像的,cache的产生,就是在linux filesystem的框架和filesystem的driver之间跳转的时候
把内容放到cache里面去的。最新的内核不知道是怎么实现的。这个东西更新一直都很快。
每个driver都会提供InitOutput这个函数。这个是标准的driver的stub
而这个InitOutput是在Xserver初始化的时候会被调用的,会初始化input和output。
那么xserver的主入口在什么地方?
在dix/main.c里面
这个是xserver的主入口。
当然,xorg和kdrive都是一样的。
xserver实际上是一个死循环,while(1)的
这个死循环里面会处理各种消息,就是大家看到里面有dispatch的函数。
Xserver这个大框架和事件处理比较大,这里就不多说了。其实大多都是消息处理。输入,输出,window管理等等这些的。
输入输出和我们关系比较近,可以简单的说说,大家可能也比较关心,这个键盘鼠标等等是怎么工作的,比如从驱动里面到最上层的widget是怎么得到这个事件的。
打字还是很累的,尤其是用全拼输入法打这么多字。
阅读(2764) | 评论(1) | 转发(0) |