基本概念

注意

对于 Windows 10 上的应用,建议使用 Windows.UI.Composition API 而不是 DirectComposition。 有关详细信息,请参阅 使用可视化层实现桌面应用的现代化

本主题概述了 Microsoft DirectComposition 的基本概念。 它包含以下部分:

组合

DirectComposition 将 合成 定义为位图的集合,这些位图通过应用各种转换、效果和动画来在应用程序 UI 中生成视觉结果进行组合和操作。 DirectComposition 仅适用于位图内容;它不支持向量或文本。 DirectComposition 不提供位图内容。 相反,它提供接口,用户可以在其中使用 D2D、DXGI 绘制或上传自己的纹理内容。

DirectComposition 应用程序创建两组对象来组合场景:组合在一起的位图和定义位图之间的空间关系的视觉对象。 有关 DirectComposition 支持的位图对象的详细信息,请参阅 Bitmap 对象

视觉对象

视觉对象 (或视觉 对象) 是 DirectComposition 的基本元素。 它们是用于在应用程序 UI 中创建合成和动画的基本构建基块。

在编程术语中,视觉对象是具有一组属性的对象,并公开用于设置属性值的接口。 视觉对象的 Content 属性将特定位图与视觉对象相关联,而其他属性控制 DirectComposition 在呈现到屏幕时如何定位和操作视觉对象。

有关详细信息,请参阅 视觉对象的属性

可视化树

DirectComposition 从称为可视化 的视觉对象的分层集合创建组合。 树根处的视觉对象称为 根视觉对象 ,它可以有一个或多个与之关联的 子视觉对象 。 子视觉对象可以有一个或多个自己的子视觉对象。 具有关联子视觉对象的任何视觉 对象称为父视觉对象,共享同一父视觉对象的所有子 视觉对象称为同级视觉对象。 特定视觉对象及其所有子视觉对象和后代视觉对象称为 可视化子树

视觉对象在树中的位置有助于确定其屏幕位置和相对于合成中其他视觉对象的 z 顺序。 根视觉对象相对于呈现合成的目标窗口工作区的左上角进行定位。 所有子视觉对象都相对于其父视觉对象的左上角 (或 TransformParent 属性指定的视觉对象) 放置,并且始终以 z 顺序显示在父视觉对象的前面。

下图显示了视觉对象的组合以及用于生成合成的可视化树的结构。 视觉对象 1 是根视觉对象,也是子视觉对象 2 和子视觉对象 3 的父级,它们是同级视觉对象。 视觉对象 3 有两个自己的子视觉对象:视觉对象 4 和 5。 视觉对象 3 到 5 共同构成视觉对象子树。

视觉对象和相应的可视化树的组合

父视觉对象维护其子视觉对象的有序列表。 当同级视觉对象的位置相互重叠时,DirectComposition 会根据同级视觉对象的子级列表中的显示顺序设置同级视觉对象的 z 顺序。 列表中稍后显示的同级放置在列表中前面出现的所有同级节点的前面。 下图显示了重叠子视觉对象的 z 顺序。

重叠子视觉对象的 z 顺序

视觉对象的属性

视觉对象公开一组属性,使你能够设置视觉对象的位图内容,并控制 DirectComposition 如何定位和操作视觉对象内容。 以下部分详细介绍了每个属性。

内容属性

视觉对象的 Content 属性指定与视觉对象关联的位图内容。 这是 DirectComposition 在合成中包含视觉对象时使用的位图。

通过调用 IDCompositionVisual::SetContent 方法设置视觉对象的 Content 属性。

有关 DirectComposition 支持的位图内容类型的详细信息,请参阅 Bitmap 对象

Clip 属性

视觉对象的 Clip 属性指定称为 剪裁区域的 矩形区域 (或 剪裁矩形) 。 呈现视觉对象时,仅显示位于剪辑区域内的视觉对象部分,而延伸至剪辑区域之外的任何内容将剪切 (即不显示) 。 DirectComposition 支持具有圆角或平方角的剪裁区域。

通过调用 IDCompositionVisual::SetClip 方法设置视觉对象的 Clip 属性。

有关详细信息,请参阅 剪辑

BorderMode 属性

BorderMode 属性指定如何组合与此视觉对象关联的位图和剪辑的边缘,或与此视觉对象关联的子树中的视觉对象。

当转换位图时,边框模式会影响位图边缘的组成方式,使边缘与整数坐标不对齐。 它还会影响内容在具有圆角的剪辑的角处剪裁的方式,以及经过转换的剪辑边缘,以便边缘不与整数坐标轴对齐。

有关详细信息,请参阅 IDCompositionVisual::SetBorderMode

BitmapInterpolationMode 属性

BitmapInterpolationMode 属性告诉 DirectComposition 如何在转换位图时编写位图,以便位图中的像素与屏幕上的像素之间没有一对一的对应关系。

通过调用 IDCompositionVisual::SetBitmapInterpolationMode 方法设置视觉对象的 BitmapInterpolationMode 属性。

CompositeMode 属性

CompositeMode 属性告知 DirectComposition 如何将视觉对象的位图内容与呈现目标混合。 有关支持的复合模式的说明,请参阅 DCOMPOSITION_COMPOSITE_MODE

通过调用 IDCompositionVisual::SetCompositeMode 方法设置视觉对象的 CompositeMode 属性。

OffsetX 和 OffsetY 属性

OffsetX 和 OffsetY 属性指示 DirectComposition 水平和垂直放置视觉对象的位置。 它们定义二维固定位置,从中计算视觉对象的所有转换和效果。

对于根视觉对象,OffsetX 和 OffsetY 属性定义相对于承载视觉对象的窗口左上角的点的 x 坐标和 y 坐标。 对于子视觉对象,坐标相对于父视觉对象的左上角;如果指定 了 TransformParent 属性 ,则相对于指定视觉对象的左上角。 呈现视觉对象时,将对其进行定位,使视觉对象的左上角与指定的坐标一致。

可以通过调用 IDCompositionVisual::SetOffsetX 和 SetOffsetY 方法设置视觉对象的 OffsetX 和 OffsetY 属性。

Effect 属性

Effect 属性允许你指定一个效果或一组效果,该效果将修改视觉对象及其子树的构成方式。 例如,可以指定控制视觉对象不透明度的效果,以各种方式将视觉对象与其他位图混合,并将透视转换应用于视觉对象。

通过调用 IDCompositionVisual::SetEffect 方法设置视觉对象的 Effect 属性。

有关详细信息,请参阅效果

变换属性

Transform 属性指定二维 (2D) 转换或一组 2D 转换,以便 DirectComposition 在视觉对象上执行。 转换 (或转换) 是一种操作,它修改视觉对象的坐标系相对于其父级或相对于 TransformParent 属性指定的视觉对象。 可以使用转换来更改视觉对象的位置、大小或性质,方法是将视觉对象移动到另一个位置 (平移) ,使其 (缩放) 变大或变小,使其 (旋转) ,扭曲其形状 (倾斜) 等。

可以通过调用 IDCompositionVisual::SetTransform 方法设置视觉对象的 Transform 属性。

有关详细信息,请参阅转换

TransformParent 属性

视觉对象的坐标系由 OffsetX、OffsetY 和 Transform 属性修改。 通常,这些属性定义视觉对象相对于其直接父级的坐标系。 若要使用父视觉对象以外的某些视觉对象作为子视觉对象的坐标系的基础,请使用 TransformParent 属性将其他视觉对象指定为“父级”以进行转换。

可以通过调用 IDCompositionVisual::SetTransformParent 方法设置视觉对象的 TransformParent 属性。

设备对象

若要使用 DirectComposition,必须创建和操作各种组件对象模型 (COM) 对象。 需要创建的第一个对象是 DirectComposition 设备对象 ,因为它充当用于创建合成中使用的所有其他对象的工厂。

通过调用 DCompositionCreateDevice 函数创建设备对象,该函数返回 IDCompositionDevice 接口指针。 此接口公开一组用于创建视觉对象、剪辑对象、动画对象、转换对象、效果对象等的方法。

设备对象除了用作用于创建其他对象的工厂外,还有一个用途。 它公开了一个名为 Commit 的方法,该方法将可视化树传递给 DirectComposition 进行处理。 有关详细信息,请参阅 事务性组合

请记住,虽然可以在应用程序中创建设备对象的多个实例,但在特定组合中使用的所有对象都必须由同一设备对象创建,但有一个例外:可以将不同设备对象的可视对象合并到同一个可视化树中。 执行此操作时,DirectComposition 将像平常一样处理可视化树,不同之处在于,仅当对创建视觉对象的设备对象调用 Commit 方法时,对树中特定可视对象的更改才会生效。

使用同一可视化树中不同设备中的视觉对象的功能使多个线程能够创建和操作单个可视化树,同时维护两个可用于异步提交更改的独立设备。 有关详细信息,请参阅 跨设备可视化树

组合目标窗口

必须先将可视化树绑定到窗口,然后才能在屏幕上显示该树的任何视觉对象。 该窗口称为 组合目标窗口,可以是顶级窗口或子窗口。 此外,组合目标窗口可以是分层窗口;也就是说,它可以具有 WS_EX_LAYERED 窗口样式。

DirectComposition 允许应用程序将最多两个可视化树绑定到每个窗口。 视觉对象树包括一个在窗口本身的顶部,但在窗口的所有子窗口后面组合的树,以及另一个在窗口顶部和子窗口顶部组合的树。 换句话说,每个窗口都有四个概念层,并且所有层都剪裁到目标窗口的可见区域。 下图显示了窗口的四个概念层。

窗口的概念层

事务组合

DirectComposition 使用事务模型,在该模型中,可以创建一组对视觉对象的批量更改,然后将该集“提交”到 DirectComposition 一次性进行处理。 可以修改同一 DirectComposition 视觉对象,并提交更改的任意次数。 当桌面窗口管理器 (DWM) 选取批时,它会选取所有挂起的批处理,并按提交顺序将它们应用到下一帧。

单个提交中的所有更改都保证应用于单个帧。 由于 DWM 每帧收集一次批,因此每个帧只能修改任何特定对象一次。 修改不同对象的后续提交也可能应用于当前帧,但 DirectComposition 不保证更改将在同一帧中发生。

使用 IDCompositionSurface::BeginDrawIDCompositionSurface::EndDraw 方法可将呈现更新与视觉更新同步。 例如,可以调用 IDCompositionSurface::BeginDraw,更新视觉对象的 OffsetX 和 Clip 属性,调用 IDCompositionDevice::Commit,使用 Microsoft DirectX 绘制内容,然后调用 IDCompositionSurface::EndDraw。 在这种情况下,Microsoft DirectComposition 可确保同时更新位图内容和视觉对象属性。

批处理

可以在同一帧内向同一视觉对象提交多个更改,或向不同视觉对象提交多个更改。 在同一帧内对同一视觉对象进行多次更改时,请记住以下几点:

  • 如果对视觉对象的同一属性进行多次更改,则仅应用最后一次更改。 例如,如果将不透明度设置为 0,然后将设置为 0.5,最后设置为 1.0,则仅将不透明度 1.0 应用于视觉对象。

  • 如果更改同一视觉对象的多个属性,DirectComposition 会先将更改应用到视觉对象,然后再应用到任何子视觉对象。 无论指定属性的顺序如何,这些属性都按以下顺序应用:

    1. Offset
    2. 转换
    3. 剪辑
    4. 效果

    下图显示了将所有四个属性应用于视觉对象的结果。

    将所有四个属性应用于视觉对象的结果

    请记住,所有更改都会在同一帧的上下文中一次性应用于视觉对象。 这意味着,从用户的角度来看,对视觉对象的更改会立即发生。

  • 对于 Transform 属性,可以使用 IDCompositionDevice::CreateTransformGroup 创建一组一次性应用于视觉对象的转换。 DirectComposition 按指定的顺序应用转换。

  • 对于 Effect 属性,可以使用 IDCompositionEffectGroup 应用一组效果。 DirectComposition 按指定的顺序应用效果。 此外,在应用当前视觉对象中的所有 3D 转换后,3D 透视转换会导致可视化树平展。 这有助于确保生成的视觉对象看起来尽可能接近 3D。

同步

应用程序可以同时从多个线程调用 DirectComposition。 对于顺序调用,可以保证执行顺序,但对于并发调用,则无法保证执行顺序。 例如,如果线程 A 修改视觉对象,而线程 B 同时提交批处理,则不定义该视觉对象更改是否包含在提交的批处理中,或者是否启动新批处理。 另一方面,如果应用程序使用其他同步机制来确保先调用一个方法,DirectComposition 将遵循调用顺序并处理它们,就好像两个调用都是从单个线程按该顺序发出的。

跨设备视觉树

DirectComposition 对象不是线程绑定的;可以使用多个线程来修改同一组对象。 但是,在共享同一设备对象时,请注意以下问题。

  • 两个线程都需要能够调用 IDCompositionDevice::Commit。 如果只有一个线程调用 IDCompositionDevice::Commit,则另一个线程无法将其任何更改提交到 DirectComposition。
  • 如果一个线程调用 IDCompositionDevice::Commit ,而另一个线程仍在进行旨在作为同一事务一部分的更改,则事务行为可能会丢失。

如果需要将多个同时事务提交到 DirectComposition,则必须使用多个设备对象(可能来自多个线程)。 在此方案中,同一可视化树由两个设备对象共享,并且每个设备对象提交其自己的事务。

下图显示了由两个设备对象共享的可视化树。 视觉对象 1、2、4 和 5 由一个或另一个设备拥有,但视觉对象 3 由两个设备共享,因此可用于将两个子树连接到单个更大的可视化树。 共享可视化树允许从两个不同的线程异步操作两个设备。

由两台设备共享的可视化树

为了说明在两个设备之间共享可视化树的有用性,请考虑一种支持低延迟触摸输入的体系结构。 该体系结构可以使用两个线程,一个处理大多数 UI 任务,另一个线程专用于处理触摸输入事件。 触摸线程根据用户输入手势更新特定视觉对象的转换。 通过更新转换,触摸线程可以使该视觉对象下的整个子树跟随用户的手指,在用户执行多点触控手势时纵向扩展或缩减,等等。 UI 线程保留大部分组合树的所有权,触摸线程仅拥有为异步触摸响应标记的少数视觉对象。 下图显示了此类组合树的简化版本:

在 ui 线程和触摸线程之间共享的可视化树

通常,UI 线程仅修改其独占的视觉对象,触摸线程仅修改共享视觉对象。 创建或销毁启用触摸的子树时,唯一会出现异常。

IDCompositionSurface::BeginDraw

IDCompositionSurface::EndDraw

IDCompositonDevice::Commit

DirectComposition 概念