composite extension由两部分实现,一部分为X11 server,另一部分为compositor。两者之间通过扩展请求实现。
Compositor调用libXcomposite库实现composite扩展协议通信,X11 Server源码中集成了composite扩展功能,实现在源码树的composite目录下。
画布
图形系统的内容都渲染在画布上,如:X11的Pixmap,Wayland的wl_surface。图形系统启动时为每个屏幕创建一个画布,默认情况下,每个窗口都共享该画布,如下图所示。在X11 server中,在创建屏幕时调用CreateScreenResources回调函数创建画布,由miCreateScreenResources调用CreatePixmap创建画布。在创建窗口(Window)过程中,调CreateWindow回调函数,最终由fbCreateWindow函数将屏幕画布分配给窗口。
为了实现窗口合成功能,图形系统可为窗口创建新的画布,下图为compositor之后窗口与Pixmap的关系,为每个顶级窗口创建一个画布。以X11 Server为例,调用CreatePixmap回调函数,最终由fbCreatePixmap创建Pixmap;再调用SetWindowPixmap回调函数,最终由_fbSetWindowPixmap将窗口与pixmap关联。
Pixmap画布内存在fbCreatePixmap分配了一段连续的主机内存,当使用GPU渲染时,会调用以下操作分配GPU内存
// 申明分配GPU buffer
glGenBuffers
// 指定Buffer类型
glBindBuffer(GL_PIXEL_PACK_BUFFER)
// 指定尺寸创建GPU内存
glBufferData(GL_PIXEL_PACK_BUFFER)
// 指定GPU内存访问权限
glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE)
// 将GPU内存与framebuffer关联
glBindFramebuffer(GL_FRAMEBUFFER)X11 Server端的实现
请求扩展
compositor向X11 server发送请求,X11 server影响请求,并做响应处理。
compositor request | X11 server process function | description |
---|---|---|
XCompositeQueryVersion | ProcCompositeQueryVersion | 获取Composite扩展版本,当前版本为0.4 |
XCompositeRedirectWindow | ProcCompositeRedirectWindow | 创建指定窗口的CompWindow,一般为CompositeRedirectManual类型 |
XCompositeRedirectSubwindows | ProcCompositeRedirectSubwindows | 为指定窗口的直接子窗口创建CompWindow,一般为CompositeRedirectManual类型 |
XCompositeUnredirectWindow | ProcCompositeUnredirectWindow | 销毁指定窗口的CompWindow |
XCompositeUnredirectSubwindows | ProcCompositeUnredirectSubwindows | 销毁指定窗口的子窗口的CompWindow |
XCompositeCreateRegionFromBorderClip | ProcCompositeCreateRegionFromBorderClip | 从包含窗口边框的区域创建CompWindow区间 |
XCompositeNameWindowPixmap | ProcCompositeNameWindowPixmap | 获取指定窗口的pixmap |
XCompositeGetOverlayWindow | ProcCompositeGetOverlayWindow | 创建CompOverlayClient,若不存在overlay window,则创建一个,属性为(CWBackPixmap | CWOverrideRedirect) |
XCompositeReleaseOverlayWindow | ProcCompositeReleaseOverlayWindow | 销毁CompOverlayClient |
从mutter的实现看,MetaCompositorServer只调用了XCompositeRedirectSubwindows一个扩展接口,为根窗口的直接子窗口创建compWindow。而MetaCompositorX11调用了较多接口。
回调函数
X11 Server调用CompositeExtensionInit注册扩展协议的请求和回调函数。Composite扩展不支持8位仿真色彩。compScreenInit注册回调函数。
当窗口的visual,depth等属性与父窗口一致时,创建窗口时,该窗口共享与父窗口的pixmap,即窗口画图内容显示在父窗口的pixmap中。
自动生成CompWindow的条件:
visual与父窗口不同:创建一个类型为RedirectDrawAutomatic的CompWindow,窗口在MapWindow操作中分配新的pixmap
修改窗口属性,backingStore设置为WhenMapped或Always,创建CompWindow;设置为NotUseful,销毁CompWindow。client可以在CreateWindow时指定窗口属性,或者同ChangeWindowAttributes请求修改窗口属性
另外一种场景时,compositor client调用XCompositeRedirectWindow或XCompositeRedirectSubwindows创建CompWindow。
callback | realize | description |
---|---|---|
CreateWindow | compCreateWindow | 当窗口的visual与父窗口不同时,创建窗口的CompWindow |
DestroyWindow | compDestroyWindow | 销毁CompWindow |
RealizeWindow | compRealizeWindow | 为具有CompWindow的窗口创建一个独立的pixmap |
UnrealizeWindow | compUnrealizeWindow | 释放pixmap副本 |
PositionWindow | compPositionWindow | 重新计算pixmap的位置 |
CopyWindow | compCopyWindow | 从CompWindow中的pixmap从拷贝数据至窗口中的pixmap |
MoveWindow | compMoveWindow | 销毁CompWindow中的pOldPixmap(旧的pixmap),与 |
ResizeWindow | compResizeWindow | 销毁CompWindow中的pixmap,先旧的pixmap销毁,再在ConfigNotify缓冲窗口的pixmap |
ReparentWindow | compReparentWindow | 销毁与原父窗口的CompWindow,以及CompSubwindows,创建与新父窗口的CompWindow,以及CompSubwindows |
ConfigNotify | compConfigNotify | 窗口大小有变化时,将窗口的pixmap交给CompWindow缓冲,然后重新分配pixmap |
ClipNotify | compClipNotify | 转换CompWindow Clip区域坐标 |
ChangeWindowAttributes | compChangeWindowAttributes | 修改CWBackingStore掩码窗口属性时,创建或销毁CompWindow |
ChangeBorderWidth | compChangeBorderWidth | 销毁CompWindow缓冲的pixmap |
InstallColormap | compInstallColormap | 检查色彩映射表是否存在,若存在,则不再安装 |
SourceValidate | compSourceValidate | 合成子窗口内容,将子窗口的pixmap合入窗口的pixmap |
CloseScreen | compCloseScreen | 关闭屏幕 |
Picture Screen Callback
屏幕图形操作回调包括几何形状,图元等操作,有不同的实现,与机器无关的实现,帧缓冲,glamor和damage相关实现,其中,glamor的实现替换帧缓冲和机器无关实现,帧缓冲替换机器无关实现。damage封装了glamor的实现。
callback | machine independence | framebuffer | glamor | damage |
---|---|---|---|---|
RealizeGlyph | miRealizeGlyph | |||
UnrealizeGlyph | miUnrealizeGlyph | fbUnrealizeGlyph | ||
Composite | fbComposite | glamor_composite | damageComposite | |
Glyphs | miGlyphs | fbGlyphs | glamor_composite_glyphs | damageGlyphs |
CompositeRects | miCompositeRects | glamor_composite_rectangles | ||
Trapezoids | fbTrapezoids | glamor_trapezoids | ||
Triangles | fbTriangles | glamor_triangles | ||
RasterizeTrapezoid | fbRasterizeTrapezoid | |||
AddTraps | fbAddTraps | glamor_add_traps | damageAddTraps | |
AddTriangles | fbAddTriangles | |||
TriStrip | miTriStrip | |||
TriFan | miTriFan | |||
CreatePicture | miCreatePicture | |||
DestroyPicture | miDestroyPicture | |||
ChangePictureClip | miChangePictureClip | |||
DestroyPictureClip | miDestroyPictureClip | |||
ChangePicture | miChangePicture | |||
ValidatePicture | miValidatePicture | |||
InitIndexed | miInitIndexed | |||
CloseIndexed | miCloseIndexed | |||
UpdateIndexed | miUpdateIndexed | |||
ChangePictureTransform | miChangePictureTransform | |||
ChangePictureFilter | miChangePictureFilter |
mutter compositor实现
compositor根据窗口层级创建和销毁CompWindow,其创建的CompWindow类型为CompositeRedirectManual,共有3种创建CompWindow的操作。
compositor在初始化后,创建一个composite window,属性为CWOverrideRedirect,对X11的根窗口发送XCompositeRedirectSubwindows,对根窗口的所有直接子窗口创建CompWindow。
在mutter中,一个MetaSurfaceActorX11对应的window会调用XCompositeRedirectWindow创建一个CompWindow。由调用ClutterActor::constructed回调函数meta_window_actor_constructed实现,meta_window_actor_constructed ->init_surface_actor ->meta_surface_actor_x11_new,这个过程只有使用MetaCompositorX11时才会调用,而非根模式下使用MetaCompositorServer和MetaWaylandCompositor,不会调用该过程,
MetaSurfaceActorX11更新pixmap时,调用XCompositeNameWindowPixmap获取指定Window的pixmap,最终创建CoglTexturePixmapX11与pixmap关联,在非根模式下,使用的时MetaWindowActorWayland。
Wayland compositor使用XCompositeRedirectSubwindows对根窗口的子窗口创建CompWindow,具体作用说明如下:
为根窗口创建一个CompClientWindow(ccw)和CompSubwindows(csw),update均设置为CompositeRedirectManual,csw管理ccw链表。ccw的client为wayland compositor
调用compRedirectWindow为每个顶级窗口创建一个CompWindow(cw)以及CompClientWindow(ccw),其update均为CompositeRedirectManual,cw管理ccw链表,将窗口的redirectDraw设置为RedirectDrawManual。
CreateWindow时,调用compCreateWindow对每个顶层窗口(根窗口的子窗口)创建一个CompWindow(cw)以及CompClientWindow(ccw)
ReparentWindow时,调用compReparentWindow,销毁原顶层窗口的CompWindow(cw)以及CompClientWindow(ccw)
从上面的实现看,Wayland compositor对窗口管理器的页框和override direct窗口创建了CompWindow,其它窗口未管理。在X11 server处理窗口堆叠处理时,从miValidateTree实现看到,redirectDraw为RedirectDrawManual的窗口作为透明窗口处理,不覆盖显示区域。该函数计算父窗口的子窗口的可剪接区域以及各窗口的exposed和borderExposed区域。
// mutter检测xwayland已经启动后,对x11 display进行初始化,做为X11 server的client,初始化相关扩展
// 扩展包括:xsync, xshape, xcomposite, xdamage, xfixes, xi
gboolean meta_display_init_x11_finish (MetaDisplay *display, GAsyncResult *result, GError **error){
MetaX11Display *x11_display = meta_x11_display_new (display, error);
if (!x11_display)
return FALSE;
display->x11_display = x11_display;
g_signal_emit (display, display_signals[X11_DISPLAY_SETUP], 0);
meta_x11_display_create_guard_window (x11_display);
if (!display->display_opening){
g_signal_emit (display, display_signals[X11_DISPLAY_OPENED], 0);
// 创建composite window,属性为CWOverrideRedirect,保存在x11_display->wm_cm_selection_window
meta_x11_display_set_cm_selection (x11_display);
// 对已有的顶层窗口,增加页框窗口。(窗口管理器功能)
meta_display_manage_all_xwindows (display);
// 对根窗口发送XCompositeRedirectSubwindows,为其子窗口创建CompWindow
meta_compositor_redirect_x11_windows (display->compositor);
}
return TRUE;
}ProcCompositeRedirectSubwindows
ProcCompositeRedirectSubwindows调用compRedirectSubwindows实现其功能
// update一般为CompositeRedirectManual
int compRedirectSubwindows(ClientPtr pClient, WindowPtr pWin, int update){
CompSubwindowsPtr csw = GetCompSubwindows(pWin);
CompClientWindowPtr ccw;
WindowPtr pChild;
// Only one Manual update is allowed
if (csw && update == CompositeRedirectManual)
for (ccw = csw->clients; ccw; ccw = ccw->next)
if (ccw->update == CompositeRedirectManual)
return BadAccess;
// Allocate per-client per-window structure
// The client *could* allocate multiple, but while supported, it is not expected to be common
ccw = malloc(sizeof(CompClientWindowRec));
if (!ccw)
return BadAlloc;
ccw->id = FakeClientID(pClient->index);
ccw->update = update;
// Now make sure there's a per-window structure to hang this from
if (!csw) {
csw = malloc(sizeof(CompSubwindowsRec));
if (!csw) {
free(ccw);
return BadAlloc;
}
csw->update = CompositeRedirectAutomatic;
csw->clients = 0;
dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, csw);
}
// Redirect all existing windows,相反方向遍历,compRedirectWindow涉及窗口栈的变化
for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib) {
// 创建window的compWindow,以及独立的pixmap
int ret = compRedirectWindow(pClient, pChild, update);
if (ret != Success) {
// skip
}
}
// Hook into subwindows list
ccw->next = csw->clients;
csw->clients = ccw;
if (!AddResource(ccw->id, CompositeClientSubwindowsType, pWin))
return BadAlloc;
if (ccw->update == CompositeRedirectManual) {
csw->update = CompositeRedirectManual;
// tell damage extension that damage events for this client are critical output
DamageExtSetCritical(pClient, TRUE);
pWin->inhibitBGPaint = TRUE;
}
return Success;
}
// 为pWin创建compWindow,update为CompositeRedirectManual
int compRedirectWindow(ClientPtr pClient, WindowPtr pWin, int update){
CompWindowPtr cw = GetCompWindow(pWin);
CompClientWindowPtr ccw;
CompScreenPtr cs = GetCompScreen(pWin->drawable.pScreen);
WindowPtr pLayerWin;
Bool anyMarked = FALSE;
if (pWin == cs->pOverlayWin) {
return Success;
}
if (!pWin->parent)
return BadMatch;
// Only one Manual update is allowed
if (cw && update == CompositeRedirectManual)
for (ccw = cw->clients; ccw; ccw = ccw->next)
if (ccw->update == CompositeRedirectManual)
return BadAccess;
// Allocate per-client per-window structure
// The client *could* allocate multiple, but while supported, it is not expected to be common
ccw = malloc(sizeof(CompClientWindowRec));
if (!ccw)
return BadAlloc;
ccw->id = FakeClientID(pClient->index);
ccw->update = update;
// Now make sure there's a per-window structure to hang this from
if (!cw) {
cw = malloc(sizeof(CompWindowRec));
if (!cw) {
free(ccw);
return BadAlloc;
}
// 创建damage
cw->damage = DamageCreate(compReportDamage, compDestroyDamage, DamageReportNonEmpty,
FALSE, pWin->drawable.pScreen, pWin);
if (!cw->damage) {
free(ccw);
free(cw);
return BadAlloc;
}
// 标记与pWin重叠的窗口,有重叠则返回true
// miMarkOverlappedWindows深度遍历pWin和相邻窗口以及所有子窗口是否有重叠
// miMarkWindow记录与pWin有重叠的窗口尺寸
anyMarked = compMarkWindows(pWin, &pLayerWin);
RegionNull(&cw->borderClip);
cw->update = CompositeRedirectAutomatic;
cw->clients = 0;
cw->oldx = COMP_ORIGIN_INVALID;
cw->oldy = COMP_ORIGIN_INVALID;
cw->damageRegistered = FALSE;
cw->damaged = FALSE;
cw->pOldPixmap = NullPixmap;
dixSetPrivate(&pWin->devPrivates, CompWindowPrivateKey, cw);
}
ccw->next = cw->clients;
cw->clients = ccw;
if (!AddResource(ccw->id, CompositeClientWindowType, pWin))
return BadAlloc;
if (ccw->update == CompositeRedirectManual) {
if (!anyMarked)
anyMarked = compMarkWindows(pWin, &pLayerWin);
if (cw->damageRegistered) {
DamageUnregister(cw->damage);
cw->damageRegistered = FALSE;
}
cw->update = CompositeRedirectManual;
}
else if (cw->update == CompositeRedirectAutomatic && !cw->damageRegistered) {
if (!anyMarked)
anyMarked = compMarkWindows(pWin, &pLayerWin);
}
// 创建或者销毁pixmap,此创建为window创建一个pixmap
if (!compCheckRedirect(pWin)) {
FreeResource(ccw->id, RT_NONE);
return BadAlloc;
}
if (anyMarked)
// 有重叠,则处理重叠窗口部分
compHandleMarkedWindows(pWin, pLayerWin);
return Success;
}
static void compHandleMarkedWindows(WindowPtr pWin, WindowPtr pLayerWin) {
ScreenPtr pScreen = pWin->drawable.pScreen;
// miValidateTree,根据窗口树,计算需要重绘的窗口及其区域
(*pScreen->ValidateTree) (pLayerWin->parent, pLayerWin, VTOther);
// miHandleValidateExposures,深度优先,先绘制边框,再绘制背景,然后发送Expose事件给客户端绘制内容
(*pScreen->HandleExposures) (pLayerWin->parent);
// xf86XVPostValidateTree,视频扩展协议使用,该场景不处理
if (pScreen->PostValidateTree)
(*pScreen->PostValidateTree) (pLayerWin->parent, pLayerWin, VTOther);
}damage
damage记录画布变化的区域,以及相关操作。每个画布有一个damage列表,保存在PixmapPtr的damagePixPrivateKey私有属性中,不同的damage可以响应不同操作。每个window有额外的damage列表,保存在WindowPtr的damageWinPrivateKey私有属性中,主要原因是一个窗口树可能共享一个画布,即子窗口没有独立的画布,使用父窗口的画布,而子窗口有独立的damage操作。window与pixmap的关系可以通过SetWindowPixmap回调函数进行修改
typedef struct _damage {
DamagePtr pNext; // pixmap的damage列表
DamagePtr pNextWin; // window的damage列表
RegionRec damage; // 变化区域,初始化为空
DamageReportLevel damageLevel; // 触发report的级别,DamageCreate时赋值
Bool isInternal; // DamageCreate时赋值
void *closure; // DamageCreate时赋值
Bool isWindow; // 是否为窗口的damage,既可以是Window,也可以是pixmap,DamageRegister时赋值
DrawablePtr pDrawable; // 画布指针(window or pixmap),DamageRegister时赋值
DamageReportFunc damageReport;
DamageDestroyFunc damageDestroy;
Bool reportAfter;
RegionRec pendingDamage; // will be flushed post submission at the latest
ScreenPtr pScreen;
} DamageRec;
// 触发report的级别
typedef enum _damageReportLevel {
DamageReportRawRegion, // 传递新增的变化区域给damageReport,每次都触发report
DamageReportDeltaRegion, // 有变化,传递变化区域
DamageReportBoundingBox, // 边界有变化,传递整个damage区域
DamageReportNonEmpty, // 由空到非空变化,传递整个damage区域
DamageReportNone
} DamageReportLevel;
// Damage的回调函数
const DamageScreenFuncsRec miFuncs = {
miDamageCreate, miDamageRegister, miDamageUnregister, miDamageDestroy
};
void miDamageCreate(DamagePtr pDamage) {}
void miDamageRegister(DrawablePtr pDrawable, DamagePtr pDamage){
if (pDrawable->type == DRAWABLE_WINDOW)
// 深度优先遍历窗口,更新窗口的序列号:pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER;
TraverseTree((WindowPtr)pDrawable, damageRegisterVisit, NULL);
else
pDrawable->serialNumber = NEXT_SERIAL_NUMBER;
}
void miDamageUnregister(DrawablePtr pDrawable, DamagePtr pDamage) {
if (pDrawable->type == DRAWABLE_WINDOW)
TraverseTree((WindowPtr)pDrawable, damageRegisterVisit, NULL);
else
pDrawable->serialNumber = NEXT_SERIAL_NUMBER;
}
void miDamageDestroy(DamagePtr pDamage) {}CompositeRedirectAutomatic与damage
在CreateWindow时,compCreateWindow在compRedirectWindow函数中调用DamageCreate为CompWindow创建Damage,保存在CompWindowPtr->damage中,并在compCheckRedirect函数中调用compAllocPixmap分配一个新的画布,并将damage注册至该window的画布中。然而,在CompositeRedirectManual类型下,未调用DamageRegister将damage注册注册至画布中。由X11 server实现compositor功能时,下面过程才有效。
// DamageReportNonEmpty,可显示区域变化时,当区域不为空时,调用compReportDamage
// cw->damage = DamageCreate(compReportDamage,compDestroyDamage,DamageReportNonEmpty,FALSE, pWin->drawable.pScreen, pWin);
static void compReportDamage(DamagePtr pDamage, RegionPtr pRegion, void *closure){
WindowPtr pWin = (WindowPtr) closure;
ScreenPtr pScreen = pWin->drawable.pScreen;
CompScreenPtr cs = GetCompScreen(pScreen);
CompWindowPtr cw = GetCompWindow(pWin);
if (!cs->pendingScreenUpdate) {
// 插入工作队列,在主线程下次迭代时处理
QueueWorkProc(compScreenUpdate, serverClient, pScreen);
cs->pendingScreenUpdate = TRUE;
}
cw->damaged = TRUE;
// 标记祖父窗口,子孙窗口已被修改,即pWin->damagedDescendants = TRUE;
compMarkAncestors(pWin);
}
static Bool compScreenUpdate(ClientPtr pClient, void *closure){
ScreenPtr pScreen = closure;
CompScreenPtr cs = GetCompScreen(pScreen);
// 递归调用深度优先递归调用,若窗口有变化,调用compWindowUpdateAutomatic更新窗口
compPaintChildrenToWindow(pScreen->root);
cs->pendingScreenUpdate = FALSE; // Next damage will restore the worker
return TRUE;
}
void compPaintChildrenToWindow(WindowPtr pWin){
WindowPtr pChild;
if (!pWin->damagedDescendants)
return;
// 将所有子窗口内容合成至父窗口中
for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
compPaintWindowToParent(pChild);
pWin->damagedDescendants = FALSE;
}
static void compPaintWindowToParent(WindowPtr pWin){
// 递归处理窗口的所有子窗口
compPaintChildrenToWindow(pWin);
if (pWin->redirectDraw != RedirectDrawNone) {
CompWindowPtr cw = GetCompWindow(pWin);
if (cw->damaged) {
// 将窗口内容合成至父窗口中
compWindowUpdateAutomatic(pWin);
cw->damaged = FALSE;
}
}
}
static void compWindowUpdateAutomatic(WindowPtr pWin) {
CompWindowPtr cw = GetCompWindow(pWin);
ScreenPtr pScreen = pWin->drawable.pScreen;
WindowPtr pParent = pWin->parent;
PixmapPtr pSrcPixmap = (*pScreen->GetWindowPixmap) (pWin);
PictFormatPtr pSrcFormat = PictureWindowFormat(pWin);
PictFormatPtr pDstFormat = PictureWindowFormat(pWin->parent);
int error;
RegionPtr pRegion = DamageRegion(cw->damage);
PicturePtr pSrcPicture = CreatePicture(0, &pSrcPixmap->drawable, pSrcFormat, 0, 0, serverClient, &error);
XID subwindowMode = IncludeInferiors;
PicturePtr pDstPicture = CreatePicture(0, &pParent->drawable, pDstFormat, CPSubwindowMode, &subwindowMode, serverClient, &error);
// First move the region from window to screen coordinates
RegionTranslate(pRegion, pWin->drawable.x, pWin->drawable.y);
// Clip against the "real" border clip
RegionIntersect(pRegion, pRegion, &cw->borderClip);
// Now translate from screen to dest coordinates
RegionTranslate(pRegion, -pParent->drawable.x, -pParent->drawable.y);
// Clip the picture
SetPictureClipRegion(pDstPicture, 0, 0, pRegion);
// And paint
CompositePicture(PictOpSrc, pSrcPicture, 0, pDstPicture, 0, 0, 0, 0, pSrcPixmap->screen_x - pParent->drawable.x, pSrcPixmap->screen_y - pParent->drawable.y, pSrcPixmap->drawable.width, pSrcPixmap->drawable.height);
FreePicture(pSrcPicture, 0);
FreePicture(pDstPicture, 0);
// Empty the damage region. This has the nice effect of rendering the translations above harmless
DamageEmpty(cw->damage);
}
void CompositePicture(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst,
INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask,
INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) {
PictureScreenPtr ps = GetPictureScreen(pDst->pDrawable->pScreen);
ValidatePicture(pSrc);
if (pMask)
ValidatePicture(pMask);
ValidatePicture(pDst);
op = ReduceCompositeOp(op, pSrc, pMask, pDst, xSrc, ySrc, width, height);
if (op == PictOpDst)
return;
// 调用damageComposite -> glamor_composite 合成图像
(*ps->Composite) (op, pSrc, pMask, pDst,
xSrc, ySrc, xMask, yMask, xDst, yDst, width, height);
}CompositeRedirectManual与damage
xwayland采用无根模式,由Wayland compositor实现compositor功能,xwayland在realize window时对顶层窗口创建并注册damage,实现在xwl_realize_window函数中,其调用register_damage创建和注册damage,将damage保存在Window的xwl_damage_private_key私有属性中。
static Bool register_damage(WindowPtr window) {
// 为window创建damage,当damage区域由空至非空时,触发report操作
DamagePtr damage = DamageCreate(damage_report, damage_destroy, DamageReportNonEmpty, FALSE, window->drawable.pScreen, window);
if (damage == NULL) {
ErrorF("Failed creating damage\n");
return FALSE;
}
// 为window注册damage
DamageRegister(&window->drawable, damage);
DamageSetReportAfterOp(damage, TRUE);
dixSetPrivate(&window->devPrivates, &xwl_damage_private_key, damage);
return TRUE;
}
// window显示内容有变化,则加入xwl_screen->damage_window_list列表中,在blockHandler回调中处理
static void damage_report(DamagePtr pDamage, RegionPtr pRegion, void *data){
WindowPtr window = data;
struct xwl_window *xwl_window = xwl_window_get(window);
struct xwl_screen *xwl_screen;
if (!xwl_window)
return;
xwl_screen = xwl_window->xwl_screen;
if (xwl_window->present_flipped) {
// This damage is from a Present flip, which already committed a new buffer for the surface,
// so we don't need to do anything in response
RegionEmpty(DamageRegion(pDamage));
xorg_list_del(&xwl_window->link_damage);
xwl_window->present_flipped = FALSE;
return;
}
if (xorg_list_is_empty(&xwl_window->link_damage))
xorg_list_add(&xwl_window->link_damage, &xwl_screen->damage_window_list);
}
// 在主线程中由BlockHandler调用,处理上次迭代时等待处理的事务
static void block_handler(void *data, void *timeout) {
struct xwl_screen *xwl_screen = data;
xwl_screen_post_damage(xwl_screen);
xwl_dispatch_events (xwl_screen);
}
// 向Wayland compositor发送damage请求,由Wayland compositor处理damage内容
static void xwl_screen_post_damage(struct xwl_screen *xwl_screen){
struct xwl_window *xwl_window, *next_xwl_window;
struct xorg_list commit_window_list;
xorg_list_init(&commit_window_list);
// 遍历damage_window_list列表中所有的window,这些window的显示内容均有变化
xorg_list_for_each_entry_safe(xwl_window, next_xwl_window, &xwl_screen->damage_window_list, link_damage) {
// If we're waiting on a frame callback from the server, don't attach a new buffer.
if (xwl_window->frame_callback)
continue;
if (!xwl_window->allow_commits)
continue;
if (xwl_screen->glamor && !xwl_glamor_allow_commits(xwl_window))
continue;
// 向Wayland compositor发送damage请求,更新damage区域,并将自身的damage区域清空
xwl_window_post_damage(xwl_window);
// 从damage_window_list列表中删除window
xorg_list_del(&xwl_window->link_damage);
// 并将window加入commit_window_list临时列表中
xorg_list_append(&xwl_window->link_damage, &commit_window_list);
}
if (xorg_list_is_empty(&commit_window_list))
return;
if (xwl_glamor_needs_buffer_flush(xwl_screen))
glamor_block_handler(xwl_screen->screen);
// 遍历commit_window_list提交列表,向Wayland compositor提交请求,处理damage内容
xorg_list_for_each_entry_safe(xwl_window, next_xwl_window, &commit_window_list, link_damage) {
wl_surface_commit(xwl_window->surface);
// 从commit_window_list列表中删除window
xorg_list_del(&xwl_window->link_damage);
}
}Graphic Context ops
图形上下文操作子主要对图形和图元的渲染操作,glamor在glamor_init函数中替换了framebuffer的GCOps回调函数,damage在DamageSetup函数中包装了glamor的回调函数。
name | framebuffer | glamor | damage |
---|---|---|---|
FillSpans | fbFillSpans | glamor_fill_spans | damageFillSpans |
SetSpans | fbSetSpans | glamor_set_spans | damageSetSpans |
PutImage | fbPutImage | glamor_put_image | damagePutImage |
CopyArea | fbCopyArea | glamor_copy_area | damageCopyArea |
CopyPlane | fbCopyPlane | glamor_copy_plane | damageCopyPlane |
PolyPoint | fbPolyPoint | glamor_poly_point | damagePolyPoint |
Polylines | fbPolyLine | glamor_poly_lines | damagePolylines |
PolySegment | fbPolySegment | glamor_poly_segment | damagePolySegment |
PolyRectangle | fbPolyRectangle | miPolyRectangle | damagePolyRectangle |
PolyArc | fbPolyArc | miPolyArc | damagePolyArc |
FillPolygon | miFillPolygon | miFillPolygon | damageFillPolygon |
PolyFillRect | fbPolyFillRect | glamor_poly_fill_rect | damagePolyFillRect |
PolyFillArc | fbPolyFillArc | miPolyFillArc | damagePolyFillArc |
PolyText8 | miPolyText8 | glamor_poly_text8 | damagePolyText8 |
PolyText16 | miPolyText16 | glamor_poly_text16 | damagePolyText16 |
ImageText8 | miImageText8 | glamor_image_text8 | damageImageText8 |
ImageText16 | miImageText16 | glamor_image_text16 | damageImageText16 |
ImageGlyphBlt | fbImageGlyphBlt | miImageGlyphBlt | damageImageGlyphBlt |
PolyGlyphBlt | fbPolyGlyphBlt | glamor_poly_glyph_blt | damagePolyGlyphBlt |
PushPixels | fbPushPixels | glamor_push_pixels | damagePushPixels |
Graphic Context Functions
图形上下文函数主要对图形上下文进行操作,glamor替换了framebuffer的GCFuncs,damage包装了glamor的GCFuncs回调函数。
name | framebuffer | glamor | damage |
---|---|---|---|
ValidateGC | fbValidateGC | glamor_validate_gc | damageValidateGC |
ChangeGC | miChangeGC | miChangeGC | damageChangeGC |
CopyGC | miCopyGC | miCopyGC | damageCopyGC |
DestroyGC | miDestroyGC | glamor_destroy_gc | damageDestroyGC |
ChangeClip | miChangeClip | miChangeClip | damageChangeClip |
DestroyClip | miDestroyClip | miDestroyClip | damageDestroyClip |
CopyClip | miCopyClip | miCopyClip | damageCopyClip |
Damage extension
在DamageExtensionInit函数中注册Damage的扩展请求和事件。由client创建和销毁Damage,Damage发生变化时,发送DamageNotify事件通知。
/* Requests */
#define X_DamageQueryVersion 0
#define X_DamageCreate 1
#define X_DamageDestroy 2
#define X_DamageSubtract 3
#define X_DamageAdd 4
/* Events */
#define XDamageNotify 0
#define XDamageNumberEvents (XDamageNotify + 1)
/* Constants */
#define XDamageReportRawRectangles 0 // 有新的区域
#define XDamageReportDeltaRectangles 1 // 区域有变化
#define XDamageReportBoundingBox 2 // 边界有变化
#define XDamageReportNonEmpty 3 // 空至非空转换
static int (*ProcDamageVector[XDamageNumberRequests]) (ClientPtr) = {
ProcDamageQueryVersion, ProcDamageCreate, ProcDamageDestroy,
ProcDamageSubtract, ProcDamageAdd,
};
// 提交渲染命令前调用该函数,二选一:记录或者报告;可由调用者选择。
1. 报告:指定报告触发条件,以及报告回调函数,当该drawable满足条件时回调通知
2. 记录:记录drawable所有的变化区域。如:modesetting记录screen pixmap的变化,然后更新显示。
void DamageRegionAppend(DrawablePtr pDrawable, RegionPtr pRegion);
// 提交渲染命令后调用该函数,处理reportAfter的damageReport,或者记录变化区域
void DamageRegionProcessPending(DrawablePtr pDrawable); |