title author date CreateTime categories
win10 uwp 渲染原理 DirectComposition 渲染
lindexi
2019-03-08 09:18:16 +0800
2018-04-16 11:38:37 +0800
UWP DirectComposition win2d 渲染

本文来告诉大家一个新的技术DirectComposition,在 win7 之后(实际上是 vista),微软正在考虑一个新的渲染机制。

在 Windows Vista 就引入了一个服务,桌面窗口管理器Desktop Window Manager,虽然从借助 C++ 进行 Windows 开发博客可以看到 DWM 不是一个好的方法,但是比之前好。

在 win8 的时候,微软提出了 DirectComposition ,这是一个新的方法。

在软件的渲染一直都是两个阵营,一个是使用直接渲染模式。直接渲染的例子是使用 Direct2D 和 Direct3D ,而直接通过 Dx api 的方式当然需要使用 C++ 和底层的 API ,这开发效率比较差。

在 1511 发布,微软告诉大家可以使用底层的 DirectComposition 接口,这样大家就可以通过 DirectComposition 做出好看的效果

在原来的 UWP 应用,大家很容易使用 xaml 来写一个界面,但是如果没有 xaml 那么如何创建一个界面。

我不会告诉大家去 new 一个控件,因为这样和使用之前的方法差不多。我会告诉大家如何从一个 Visual 开始画。

在 UWP 可以通过下面几个方式显示界面

  • 通过 xaml 或者后台新建控件显示。这是最推荐的方法,本文下面的方法是不推荐的,但是可以让大家知道原理。使用 xaml 显示的元素一般都是继承 UIElement ,创建出来的元素可以带交互。

  • 如果需要高性能的画图,通过 win2d 是一个很好的方法。大家也知道创建的win2d只是显示,不会有交互,如果需要交互需要自己写。虽然写一个交互很简单,但是如果没有使用框架,重复代码很多。

  • 使用 DirectX APIs 来画 3d 的图片,但是现在需要一些 C++ 代码。

在 UWP 的显示,推荐使用 xaml 来写界面,原因是 xaml 是一个界面无关的代码,也就是无论是 C# 和 C++ 都可以使用。如果使用 C# 来写界面,那么代码就和 C# 合在一起,不能很好在 C++ 运行。而且使用xaml 写简单比使用C#更简单,在 vs 实时编译器可以看到界面效果。

也许大家会关系 fds 是如何做出来的,对于微软的设计,所有的 xaml 或者 win2d 的显示都是位图。这里的位图不是大家想的 bitmapImage 而是显示的一个说法,微软对所有的位图输出到 DirectComposition 。微软的 DirectComposition 在官方是这样说 “DirectComposition 组件使开发者能够进行高性能的位图合成,并附加变换、特效以及动画等各种效果,以此打造出更为复杂、生动、流畅的用户界面。DirectComposition 利用图形硬件的加速特性可以进行与 UI 线程无关的渲染处理,支持 2D 仿射变换、3D 透视变换等多种变换,以及剪切、不透明等基本特效”。

翻译参见 Windows Composition API 指南 - 认识 Composition API 感谢大神。

那么是不是可以通过Composition显示元素,自己来写 UWP 框架。

在开始告诉大家写 UWP 框架之前,先给大家一个简单的例子,如何应用 DirectComposition 的 API 写出界面框架。需要知道 DirectComposition 虽然很好用,但是开发的技术要求是 C++ 和 COM 开发难度很高,在 fall creators update 16299 以上的版本,可以使用 Windows.UI.Composition 的方法,可以直接在 xaml 中写出调用 DirectComposition 的方法,同时后台代码可以使用 C# 写

虽然本文想直接告诉小伙伴如何使用 C++ 和 COM 写一个 DirectComposition 的应用,但是因为发现难度太大了,同时微软也建议小伙伴使用 Windows.UI.Composition 而不是使用 DirectComposition 写应用,所以下面将告诉大家如何使用 Windows.UI.Composition 从零开始写一个应用

例子

之前写的一个简单的动画是一个好看效果,请看 win10 uwp 进度条 WaveProgressControl

下面来通过删除所有 xaml 文件,从头自己写。

创建工程

首先创建一个 UWP 项目,注意选择比较高的目标。最低支持要求是 16299 的系统,这里的 16299 指的是系统版本

如何写显示

现在创建项目,删除所有的 app 和 mainpage 类。重新创建一个类。

只要支持显示,那么就可以完成一半了,因为 UWP 的元素显示都是通过布局找到元素显示的位置。当然这里不会提到 Translate 等。然后元素通过调用DrawContex告诉显卡需要显然的图形。然后在加上用户的输入,就构成了框架。

虽然一个框架比上面说的复杂很多,但是在写 Avalonial 的时候,大神告诉我,实际上一个界面框架主要的就是显示和交互。本文不会告诉大家如何写交互,只是告诉大家如何显示。

删除了所有的自动生成的代码,现在创建一个类 View ,用来显示。

下面代码的意思请看 【Win 10 应用开发】UI Composition 札记(一):视图框架的实现 - 东邪独孤 - 博客园

using System.Numerics;
using Windows.ApplicationModel.Core;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Core; namespace HmeucHsvv
{
internal class View : IFrameworkView, IFrameworkViewSource
{
public void SetWindow(CoreWindow window)
{
_compositor = new Compositor(); _compositionTarget = _compositor.CreateTargetForCurrentView(); // 创建一个容器,用来向他的 Children 添加 Visual 显示复杂的元素
var container = _compositor.CreateContainerVisual();
_compositionTarget.Root = container; // 创建 SpriteVisual ,这个类不仅是一个容器,同时本身也是可以画出来
var visual = _compositor.CreateSpriteVisual(); // 告诉这个元素的大小和左上角,所以这个就是一个矩形,而且设置颜色
visual.Size = new Vector2(100, 100);
visual.Offset = new Vector3(10, 10, 0); visual.Brush = _compositor.CreateColorBrush(Colors.Red); // 添加元素,添加进去的元素就会被显示
container.Children.InsertAtTop(visual);
} public void Run()
{
//启动窗口需要激活
var window = CoreWindow.GetForCurrentThread();
window.Activate();
//调度方式使用 Dispatcher 通过这个就可以获得消息
window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
} public void Initialize(CoreApplicationView applicationView)
{
} public void Load(string entryPoint)
{
} public void Uninitialize()
{
} public IFrameworkView CreateView()
{
return this;
} private CompositionTarget _compositionTarget;
private Compositor _compositor; private static void Main()
{
CoreApplication.Run(new View());
}
}
}

上面代码有一些注释,通过这个方式就可以创建一个显示矩形

实际上从上面代码很容易就知道,只需要一个类继承IFrameworkView, IFrameworkViewSource,然后使用CreateView返回他自己,这时这个类就可以显示。

但是还需要使用主函数告诉软件启动的类是哪个,在运行启动窗口,如果注释掉window.Activate那么就会看到只有一个欢迎的图片不会显示矩形。

那么是什么时候窗口支持渲染的?

核心代码是CreateTargetForCurrentView这个函数只能调用一次,如果你尝试调用他两次,那么就会出现异常。因为调用这个函数就会告诉 DirectComposition 创建元素。

           _compositor = new Compositor();

            _compositionTarget = _compositor.CreateTargetForCurrentView();

显示的矩形是通过创建 SpriteVisual 来显示。那么下面再写一个 SpriteVisual ,让两个加起来。

        public void SetWindow(CoreWindow window)
{
_compositor = new Compositor(); _compositionTarget = _compositor.CreateTargetForCurrentView(); // 创建一个容器,用来向他的 Children 添加 Visual 显示复杂的元素
var container = _compositor.CreateContainerVisual();
_compositionTarget.Root = container; // 创建 SpriteVisual ,这个类不仅是一个容器,同时本身也是可以画出来
var visual = _compositor.CreateSpriteVisual(); // 告诉这个元素的大小和左上角,所以这个就是一个矩形,而且设置颜色
visual.Size = new Vector2(100, 100);
visual.Offset = new Vector3(10, 10, 0); visual.Brush = _compositor.CreateColorBrush(Colors.Red); // 添加元素,添加进去的元素就会被显示
container.Children.InsertAtTop(visual); var visual1 = _compositor.CreateSpriteVisual(); // 创建一个重叠元素
visual1.Size = new Vector2(100, 100);
visual1.Offset = new Vector3(20, 20, 0); visual1.Brush = _compositor.CreateColorBrush(
Color.FromArgb(128 /*透明*/, 0, 255, 0));
container.Children.InsertAtTop(visual1);
}

使用这个方法就可以创建多个矩形,而且通过指定位置就和大小就可以决定他在哪显示。

上面用到了三个东西第一个是 Visual ,这是一个基础的类。有 ContainerVisual 继承 Visual ,实际上他只是可以存在子元素。最后一个是 SpriteVisual ,这个类和 ContainerVisual 一样,但是他可以使用笔刷。

那么 SpriteVisual 设置的笔刷是什么,他可以设置三个不同的笔刷。第一个就是刚才给大家看的 CompositionColorBrush ,这是一个纯色笔刷。 第二个是比较复杂的,可以使用特效的 CompositionEffectBrush 笔刷,最后一个是 CompositionSurfaceBrush 可以和 dx 交互数据。

从上面代码实际只是画了普通的矩形,如果要写文字,画线,那么怎么办。这时就需要使用 CompositionSurfaceBrush ,这是最复杂的。通过这个类可以使用 d2d 来画,在 UWP 简单使用的方法是 win2d 所以下面告诉大家如何使用 win2d 来画。

但是 UWP 底层是直接使用d2d没有经过 win2d 的封装。从我的博客WPF 使用 SharpDX 在 D3DImage 显示可以知道,在 WPF 使用 d2d 是比较难的,因为很难集合两个在一个界面。但是 UWP 通过这个类就可以把底层渲染放在指定层级。这就是为什么说 UWP 可以做出比较高性能,因为 WPF 是很难修改他的渲染,即使使用D3DImage也是把渲染位图作为图片显示,需要先在显卡渲染然后把位图复制到内存,让WPF画出图片。但是 UWP 可以直接画出,不需要使用 WPF 这样的方法。我看来 UWP 在这里是很大提升,这就是我看到很多大神说不在 WPF 添加 win2d ,从底层技术实现是不相同。

CompositionSurfaceBrush

首先需要安装 win2d ,然后在 SetWindow 使用 CompositionSurfaceBrush 。还是和上面代码一样,但是需要先创建一个函数,用来创建 win2d ,请看下面

        private void GetCanvasAndGraphicsDevices()
{
var canvasDevice = CanvasDevice.GetSharedDevice(); _graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(
_compositor, canvasDevice); _graphicsDevice.RenderingDeviceReplaced += OnRenderingDeviceReplaced;
}

通过这个方法就可以拿到 graphicsDevice ,这个就是用来做 CompositionSurfaceBrush 。

如果需要创建 CompositionSurfaceBrush 那么就需要一个 CompositionDrawingSurface ,而 CompositionDrawingSurface 可以通过 graphicsDevice 创建,代码很简单

            _compositor = new Compositor();

            _compositionTarget = _compositor.CreateTargetForCurrentView();

            // 创建 win2d 用于渲染
GetCanvasAndGraphicsDevices(); _drawingSurface = _graphicsDevice.CreateDrawingSurface(
new Size(600, 600),
DirectXPixelFormat.B8G8R8A8UIntNormalized,
DirectXAlphaMode.Premultiplied); var brush = _compositor.CreateSurfaceBrush(
_drawingSurface);

那么创建的 CompositionSurfaceBrush 如何显示?刚才讲到SpriteVisual可以显示笔刷,那么就创建这个类来显示。

            var drawingVisual = _compositor.CreateSpriteVisual();
drawingVisual.Size = new Vector2(600, 600); drawingVisual.Brush = brush;

然后把他加入视觉,和上面的代码一样,只是把 Brush 的创建写了其他的代码

            var containerVisual = _compositor.CreateContainerVisual();
_compositionTarget.Root = containerVisual; containerVisual.Children.InsertAtTop(drawingVisual);

下面就是让 win2d 画出矩形。

        private void Redraw()
{
using (var drawingSession = CanvasComposition.CreateDrawingSession(
_drawingSurface))
{
drawingSession.FillRectangle(
new Rect(10, 10, 200, 200),
Colors.Red); drawingSession.FillRectangle(
new Rect(300, 300, 200, 200),
Color.FromArgb(255,126,50,50));
}
}

什么时候可以调用这个函数?实际上在刚才的函数最后调用就可以了。

现在的界面就是两个矩形

所有的代码

    internal class View : IFrameworkView, IFrameworkViewSource
{
public void SetWindow(CoreWindow window)
{
_compositor = new Compositor(); _compositionTarget = _compositor.CreateTargetForCurrentView(); // 创建 win2d 用于渲染
GetCanvasAndGraphicsDevices(); _drawingSurface = _graphicsDevice.CreateDrawingSurface(
new Size(600, 600),
DirectXPixelFormat.B8G8R8A8UIntNormalized,
DirectXAlphaMode.Premultiplied); var brush = _compositor.CreateSurfaceBrush(
_drawingSurface); var drawingVisual = _compositor.CreateSpriteVisual();
drawingVisual.Size = new Vector2(600, 600); drawingVisual.Brush = brush; var containerVisual = _compositor.CreateContainerVisual();
_compositionTarget.Root = containerVisual; containerVisual.Children.InsertAtTop(drawingVisual); Redraw();
} public void Run()
{
//启动窗口需要激活
var window = CoreWindow.GetForCurrentThread();
window.Activate();
//调度方式使用 Dispatcher 通过这个就可以获得消息
window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
} public void Initialize(CoreApplicationView applicationView)
{
} public void Load(string entryPoint)
{
} public void Uninitialize()
{
} public IFrameworkView CreateView()
{
return this;
} private CompositionTarget _compositionTarget;
private Compositor _compositor;
private CompositionGraphicsDevice _graphicsDevice;
private CompositionDrawingSurface _drawingSurface; private static void Main()
{
CoreApplication.Run(new View());
} private void GetCanvasAndGraphicsDevices()
{
var canvasDevice = CanvasDevice.GetSharedDevice(); _graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(
_compositor, canvasDevice); //_graphicsDevice.RenderingDeviceReplaced += OnRenderingDeviceReplaced;
} private void OnRenderingDeviceReplaced(
CompositionGraphicsDevice sender, RenderingDeviceReplacedEventArgs args)
{
Redraw();
} private void Redraw()
{
using (var drawingSession = CanvasComposition.CreateDrawingSession(
_drawingSurface))
{
drawingSession.FillRectangle(
new Rect(10, 10, 200, 200),
Colors.Red); drawingSession.FillRectangle(
new Rect(300, 300, 200, 200),
Color.FromArgb(255,126,50,50));
}
}
}

那么尝试使用 win2d 写文字就请看win10 uwp win2d 入门 看这一篇就够了

修改函数和普通使用 win2d 没有不同

        using (var drawingSession = CanvasComposition.CreateDrawingSession(
_drawingSurface))
{
drawingSession.Clear(Colors.White);
drawingSession.DrawText("lindexi", new Vector2(100, 100), Color.FromArgb(0xFF, 100, 100, 100));
}

还有如何使用动画和特效,我这里就不说了。

代码参考 图形和动画 - Windows 组合支持 10 倍缩放

参考:

图形和动画 - Windows 组合支持 10 倍缩放

【Win 10 应用开发】UI Composition 札记(一):视图框架的实现 - 东邪独孤 - 博客园

借助 C++ 进行 Windows 开发 - 使用 Windows 组合引擎实现高性能窗口分层

借助 C++ 进行 Windows 开发 - 使用 Windows 组合引擎

Windows, UI and Composition (the Visual Layer) – Mike Taulty

Windows with C++ - DirectComposition: A Retained-Mode API to Rule Them All

2019-3-8-win10-uwp-渲染原理-DirectComposition-渲染的更多相关文章

  1. win10 uwp 渲染原理 DirectComposition 渲染

    本文来告诉大家一个新的技术DirectComposition,在 win7 之后(实际上是 vista),微软正在考虑一个新的渲染机制 在 Windows Vista 就引入了一个服务,桌面窗口管理器 ...

  2. 【浏览器渲染原理】渲染树构建之渲染树和DOM树的关系(转载 学习中。。。)

    在DOM树构建的同时,浏览器会构建渲染树(render tree).渲染树的节点(渲染器),在Gecko中称为frame,而在webkit中称为renderer.渲染器是在文档解析和创建DOM节点后创 ...

  3. Win10 UWP开发系列:使用VS2015 Update2+ionic开发第一个Cordova App

    安装VS2015 Update2的过程是非常曲折的.还好经过不懈的努力,终于折腾成功了. 如果开发Cordova项目的话,推荐大家用一下ionic这个框架,效果还不错.对于Cordova.PhoneG ...

  4. Win10 UWP开发实现Bing翻译

    微软在WP上的发展从原来的Win7到Win8,Win8.1,到现在的Win10 UWP,什么是UWP,UWP即Windows 10 中的Universal Windows Platform简称.即Wi ...

  5. 【Win10 UWP】后台任务与动态磁贴

    动态磁贴(Live Tile)是WP系统的大亮点之一,一直以来受到广大用户的喜爱.这一讲主要研究如何在UWP应用里通过后台任务添加和使用动态磁贴功能. 从WP7到Win8,再到Win10 UWP,磁贴 ...

  6. win10 uwp 线程池

    原文:win10 uwp 线程池 如果大家有开发 WPF 或以前的程序,大概知道线程池不是 UWP 创造的,实际上在很多技术都用到线程池. 为什么需要线程池,他是什么?如何在 UWP 使用线程池,本文 ...

  7. win10 uwp win2d CanvasVirtualControl 与 CanvasAnimatedControl

    本文来告诉大家 CanvasVirtualControl ,在什么时候使用这个控件. 在之前的入门教程win10 uwp win2d 入门 看这一篇就够了我直接用的是CanvasControl,实际上 ...

  8. 【Web动画】CSS3 3D 行星运转 && 浏览器渲染原理

    承接上一篇:[CSS3进阶]酷炫的3D旋转透视 . 最近入坑 Web 动画,所以把自己的学习过程记录一下分享给大家. CSS3 3D 行星运转 demo 页面请戳:Demo.(建议使用Chrome打开 ...

  9. Win10 UWP开发系列:实现Master/Detail布局

    在开发XX新闻的过程中,UI部分使用了Master/Detail(大纲/细节)布局样式.Win10系统中的邮件App就是这种样式,左侧一个列表,右侧是详情页面.关于这种 样式的说明可参看MSDN文档: ...

  10. Win10/UWP开发—使用Cortana语音与App后台Service交互

    上篇文章中我们介绍了使用Cortana调用前台App,不熟悉的移步到:Win10/UWP开发—使用Cortana语音指令与App的前台交互,这篇我们讲讲如何使用Cortana调用App的后台任务,相比 ...

随机推荐

  1. webpack学习之—— Loaders

    loader 用于对模块的源代码进行转换.loader 可以使你在 import 或"加载"模块时预处理文件.因此,loader 类似于其他构建工具中“任务(task)”,并提供了 ...

  2. Hdu 3603

    Coach Yehr’s punishment Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/ ...

  3. Codeforces 300C

    题目链接: http://codeforces.com/contest/300/problem/C 本来是道不难的题目,还是自己的数学功底不扎实. 从该题又一次巩固了关于乘法逆的概念,在剩余系中,如果 ...

  4. 【python小随笔】pycharm的永久破解

    PS:这里有人会遇到第一次输入补丁的破解命令后,重启后启动不了软件,这个时候需要卸载(unstall把配置都得删除了),然后重新下载软件,再用这个步骤就OK了~~版本一定要低于最新版本两个以上,最好用 ...

  5. 项目管理知识图谱OR架构图

    做项目管理,心中一定要有知识图谱,科学的知识储备+100%执行力=好的管理者. 德鲁克所言:  领导是“做正确的事”,管理是“把事做正确”.

  6. pandas使用手册

    工欲善其事必先利其器,在使用Python做数据挖掘和数据分析时,一大必不可少的利器就是Pandas库了.pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的,其纳入了大量库 ...

  7. 【JZOJ3875】【NOIP2014八校联考第4场第2试10.20】星球联盟(alliance)

    fg 在遥远的S星系中一共有N个星球,编号为1-N.其中的一些星球决定组成联盟,以方便相互间的交流. 但是,组成联盟的首要条件就是交通条件.初始时,在这N个星球间有M条太空隧道.每条太空隧道连接两个星 ...

  8. virtualenv安装 以及在PyCharm中的使用

    1.安装前条件 python3.7和 pip(可以使用这个命令升级python -m pip install --upgrade pip) 2.安装virtualenv pip install vir ...

  9. element-ui select 二级联动

    在使用select 选择框时,2个select 怎么关联在一起(第一个值发生变化,第二个select值随第一个变化而不同)  两个输入框代码 <el-form :inline="tru ...

  10. springMVC controller间跳转 重定向 传递参数的方法

    springMVC controller间跳转 重定向 传递参数的方法 spring MVC框架controller间跳转,需重定向.有几种情况:不带参数跳转,带参数拼接url形式跳转,带参数不拼接参 ...