Composite extension

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函数将屏幕画布分配给窗口。

image-20240305-004124.png

为了实现窗口合成功能,图形系统可为窗口创建新的画布,下图为compositor之后窗口与Pixmap的关系,为每个顶级窗口创建一个画布。以X11 Server为例,调用CreatePixmap回调函数,最终由fbCreatePixmap创建Pixmap;再调用SetWindowPixmap回调函数,最终由_fbSetWindowPixmap将窗口与pixmap关联。

image-20240305-004206.png

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

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的条件:

  1. visual与父窗口不同:创建一个类型为RedirectDrawAutomatic的CompWindow,窗口在MapWindow操作中分配新的pixmap

  2. 修改窗口属性,backingStore设置为WhenMapped或Always,创建CompWindow;设置为NotUseful,销毁CompWindow。client可以在CreateWindow时指定窗口属性,或者同ChangeWindowAttributes请求修改窗口属性

另外一种场景时,compositor client调用XCompositeRedirectWindow或XCompositeRedirectSubwindows创建CompWindow。

callback

realize

description

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

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

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

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事件通知。