版本比较

密钥

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

...

Compositor调用libXcomposite库实现composite扩展协议通信,X11 Server源码中集成了composite扩展功能,实现在源码树的composite目录下。

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

画布

图形系统的内容都渲染在画布上,如: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内存

代码块
languagecpp
// 申明分配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一个扩展接口。而MetaCompositorX11调用了较多接口。从mutter的实现看,MetaCompositorServer只调用了XCompositeRedirectSubwindows一个扩展接口,为根窗口的直接子窗口创建compWindow。而MetaCompositorX11调用了较多接口。

回调函数

X11 Server调用CompositeExtensionInit注册扩展协议的请求和回调函数。Composite扩展不支持8位仿真色彩。compScreenInit注册回调函数Server调用CompositeExtensionInit注册扩展协议的请求和回调函数。Composite扩展不支持8位仿真色彩。compScreenInit注册回调函数。

当窗口的visual,depth等属性与父窗口一致时,创建窗口时,该窗口共享与父窗口的pixmap,即窗口画图内容显示在父窗口的pixmap中。

...

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

  2. 修改窗口属性,backingStore设置为WhenMapped或Always,窗口CompWindow;设置为NotUseful,销毁CompWindow。client可以在CreateWindow时指定窗口属性,或者同ChangeWindowAttributes请求修改窗口属性修改窗口属性,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中的pixmap,why???销毁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

CompositeRectsmiCompositeRects

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,具体合成功能在X11 server中完成。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区域。

代码块
languagejava
// 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;
}

damage

在compRedirectWindow函数中调用DamageCreate为CompWindow创建Damage。在CompositeRedirectManual下,未主动调用DamageRegister注册damage。

代码块
languagejava
// 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;
}
 
// 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) {}

Graphic Context ops

...

  • ::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区域。

代码块
languagejava
// 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实现其功能

代码块
languagejava
// 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回调函数进行修改

代码块
languagecpp
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功能时,下面过程才有效。

代码块
languagecpp
// 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私有属性中。

代码块
languagecpp

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回调函数图形上下文函数主要对图形上下文进行操作,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事件通知。

代码块
languagejava
/* 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
ProcDamageCreate, 
    ProcDamageDestroy
ProcDamageDestroy,
    ProcDamageSubtract, 
    ProcDamageAdd
ProcDamageAdd,
};

// 提交渲染命令前调用该函数,二选一:记录或者报告;可由调用者选择。
1. 报告:指定报告触发条件,以及报告回调函数,当该drawable满足条件时回调通知
2. 记录:记录drawable所有的变化区域。如:modesetting记录screen pixmap的变化,然后更新显示。
void DamageRegionAppend(DrawablePtr pDrawable, RegionPtr pRegion);

// 提交渲染命令后调用该函数,处理reportAfter的damageReport,或者记录变化区域
void DamageRegionProcessPending(DrawablePtr pDrawable);