1.3.1 Composite extension
- 1 画布
- 1.1 X11 Server端的实现
- 1.1.1 请求扩展
- 1.1.2 回调函数
- 1.1.3 Picture Screen Callback
- 1.2 mutter compositor实现
- 1.3 ProcCompositeRedirectSubwindows
- 1.4 damage
- 1.5 Graphic Context ops
- 1.6 Graphic Context Functions
- 1.7 Damage extension
- 1.1 X11 Server端的实现
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回调函数进行修改
CompositeRedirectAutomatic与damage
在CreateWindow时,compCreateWindow在compRedirectWindow函数中调用DamageCreate为CompWindow创建Damage,保存在CompWindowPtr->damage中,并在compCheckRedirect函数中调用compAllocPixmap分配一个新的画布,并将damage注册至该window的画布中。然而,在CompositeRedirectManual类型下,未调用DamageRegister将damage注册注册至画布中。由X11 server实现compositor功能时,下面过程才有效。
CompositeRedirectManual与damage
xwayland采用无根模式,由Wayland compositor实现compositor功能,xwayland在realize window时对顶层窗口创建并注册damage,实现在xwl_realize_window函数中,其调用register_damage创建和注册damage,将damage保存在Window的xwl_damage_private_key私有属性中。
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事件通知。