win10 uwp 渲染原理 DirectComposition 渲染

来源:https://www.cnblogs.com/lindexi/archive/2018/05/30/win10-uwp-DirectComposition.html
-Advertisement-
Play Games

本文來告訴大家一個新的技術DirectComposition,在 win7 之後(實際上是 vista),微軟正在考慮一個新的渲染機制 ...


本文來告訴大家一個新的技術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 。

例子

之前寫的一個簡單的動畫是一個好看效果,請看 win10 uwp 進度條 WaveProgressControl

下麵來通過刪除所有 xaml 文件,從頭自己寫。

創建工程

首先創建一個 UWP 項目,註意選擇比較高的目標。

如何寫顯示

現在創建項目,刪除所有的 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

我搭建了自己的博客 https://lindexi.gitee.io/ 歡迎大家訪問,裡面有很多新的博客。只有在我看到博客寫成熟之後才會放在csdn或博客園,但是一旦發佈了就不再更新

如果在博客看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎大家加入

知識共用許可協議
本作品採用知識共用署名-非商業性使用-相同方式共用 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發佈,但務必保留文章署名林德熙(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我聯繫


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 用法:對於一個可迭代的(iterable)/可遍歷的對象(如列表、字元串),enumerate將其組成一個索引序列,利用它可以同時獲得索引和值; 常用for迴圈列印: 輸出: 0 11 22 3 一些練習: 列表見上例; ...
  • 代碼上面應該沒有什麼問題,Python使用的是版本2.7,但是在運行的時候出現了異常錯誤UnicodeEncodeError: 本來以為數據讀取錯誤,我特將fp.write改成print,結果數據全部讀取並顯示在命令控制臺上了,證明代碼是沒有問題的,仔細看了下異常信息,貌似是因為編碼問題:Unico ...
  • google官網英文原版地址: http://google.github.io/styleguide/cppguide.html 中文線上版本地址: http://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/ ...
  • 對於非線性的鏈表,可以參見相關的其他數據結構,例如樹、圖。另外有一種基於多個線性鏈表的數據結構:跳錶,插入、刪除和查找等基本操作的速度可以達到O(nlogn),和平衡二叉樹一樣。 ...
  • lock free ...
  • const 標識符 在c++中作為常量修飾符 用來修飾 函數 變數 指針 const 修飾的變數不可以改變值 const 在修飾指針的時候 const 標識符出現在*的左邊表示 指向的變數為常量不能通過指針去改變該對象的值 叫做常量指針即const int a = 0; const int* ipt ...
  • 坐標南京 211 cs專業 工作經驗算上實習1年多,外包公司,員工各種離職,項目越做越難,工資不漲怒而離職。目前陷入找工作的泥潭,記錄一些知識點的不足來督促自己吧。 2018/5/29 zn公司 : 很棒的公司,環境好,員工都是年輕人,大牛很隨和,問了很多題目,回答上來一部分,還答錯了一部分。 1、 ...
  • 字典的key是一個可哈希的值,字典的value可以是任何值。 keys():返回鍵 values():返回鍵對應的值 items():返回鍵和值 keys遍歷出來 values遍歷出來 items遍歷出來 get():根據key獲取值,key不存在時,可以指定預設值(None) pop() popi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...