亦洛夏歌 發表於 2026-2-10 07:27:00

dotnet Vortice 无需交换链与 DirectComposition 对接渲染层

<p>在 DirectComposition 里面提供了 Commit 机制,一次 Commit 的所有内容都能在相同的一帧在屏幕显示出来,如此可以非常方便地完成渲染对齐任务</p>
<p>通过 WaitForCommitCompletion 方法可以等待 Commit 内容完成渲染,此方法作用相当于等待交换链写法的等待垂直同步实现</p>
<p>在 上一篇博客 中,采用了传统的 DXGI 交换链与 DirectComposition 对接</p>

<p>在本文这里将去掉交换链,可以很大简化对接渲染的逻辑。采用 DXGI 交换链对接的方式,可以比较方便对接原有的程序,且可以实现更高帧率的控制。而采用 DirectComposition 的 Commit 写法,可以更好利用 DirectComposition 机制,实现多表面合成以及更加实时的合成器动画</p>
<p>本文将给出最简实现对接的代码逻辑,其步骤如下</p>
<ol>
<li>创建 Win32 窗口</li>
<li>创建 DirectComposition 设备和关联窗口,获取渲染表面</li>
<li>执行渲染逻辑</li>
</ol>
<p>为了保持本文简洁,我将不在正文部分贴出非关键部分的代码,在本文末尾给出全部核心代码。本文的全部核心代码部分不到 200 行,适合一口气完成。本文也使用了到了一些库,为了防止大家不知道项目如何配置的,在本文末尾也给出整个项目全部代码和配置的下载方法</p>
<h2 id="基础库">基础库</h2>
<p>按照 .NET 的惯例,开始之前先安装基础库,安装之后的 csproj 项目文件代码大概如下</p>
<pre><code class="language-xml">&lt;Project Sdk="Microsoft.NET.Sdk"&gt;

&lt;PropertyGroup&gt;
    &lt;OutputType&gt;Exe&lt;/OutputType&gt;
    &lt;TargetFramework&gt;net10.0&lt;/TargetFramework&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
    &lt;IsAotCompatible&gt;true&lt;/IsAotCompatible&gt;
    &lt;PublishAot&gt;true&lt;/PublishAot&gt;
&lt;/PropertyGroup&gt;

&lt;ItemGroup&gt;
    &lt;PackageReference Include="Vortice.Direct2D1" Version="3.8.2" /&gt;
    &lt;PackageReference Include="Vortice.Direct3D11" Version="3.8.2" /&gt;
    &lt;PackageReference Include="Vortice.DirectComposition" Version="3.8.2" /&gt;
    &lt;PackageReference Include="Vortice.DXGI" Version="3.8.2" /&gt;
    &lt;PackageReference Include="Vortice.Win32" Version="2.3.0" /&gt;

    &lt;PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.257"&gt;
      &lt;PrivateAssets&gt;all&lt;/PrivateAssets&gt;
      &lt;IncludeAssets&gt;runtime; build; native; contentfiles; analyzers&lt;/IncludeAssets&gt;
    &lt;/PackageReference&gt;

    &lt;PackageReference Include="MicroCom.Runtime" Version="0.11.0" /&gt;

&lt;/ItemGroup&gt;

&lt;/Project&gt;
</code></pre>
<h2 id="创建-win32-窗口">创建 Win32 窗口</h2>
<p>创建 Win32 窗口仅仅只是想拿到窗口句柄,不是本文重点,这里就忽略 CreateWindow 方法的实现</p>
<pre><code class="language-csharp">      // 创建窗口
      HWND window = CreateWindow();
      // 显示窗口
      ShowWindow(window, SHOW_WINDOW_CMD.SW_NORMAL);
</code></pre>
<p>以上代码的 ShowWindow 是标准的 Win32 方法,由 CsWin32 库生成。定义如下</p>
<pre><code class="language-csharp">               
               
                internal static extern winmdroot.Foundation.BOOL ShowWindow(winmdroot.Foundation.HWND hWnd, winmdroot.UI.WindowsAndMessaging.SHOW_WINDOW_CMD nCmdShow);
</code></pre>
<p>为了直接使用方法,在本文这里直接在命名空间引用静态类,代码如下</p>
<pre><code class="language-csharp">using static Windows.Win32.PInvoke;
</code></pre>
<h2 id="创建-directcomposition-设备">创建 DirectComposition 设备</h2>
<p>先使用以下快速地代码创建 ID3D11Device 设备。正常来说,还是会尝试遍历获取显示适配器用来手动创建设备。本文这里使用的是比较不稳妥的简化写法。正确且常用的写法代码稍多,在本文这里 ID3D11Device 不是主角。如对此感兴趣,请参阅 渲染博客导航</p>
<pre><code class="language-csharp">      var result = D3D11.D3D11CreateDevice(null, DriverType.Hardware, DeviceCreationFlags.BgraSupport,
            featureLevels: [], out ID3D11Device iD3D11Device, out var feature,
            out ID3D11DeviceContext iD3D11DeviceContext);
      result.CheckError();

      _ = feature;
      iD3D11DeviceContext.Dispose(); // 用不着就先释放。释放不代表立刻回收资源,只是表示业务层不需要用到,减少引用计数
</code></pre>
<p>在本文这里只用到了 ID3D11Device 设备,于是就选择立刻释放 ID3D11DeviceContext 的引用</p>
<p>拿到 ID3D11Device 设备,就可以调用 <code>DComp.DCompositionCreateDevice3</code> 创建 IDCompositionDevice 设备,代码如下</p>
<pre><code class="language-csharp">      IDCompositionDevice compositionDevice = DComp.DCompositionCreateDevice3&lt;IDCompositionDevice&gt;(iD3D11Device);
</code></pre>
<p>这里的 <code>IDCompositionDevice</code> 应该就是 <code>IDirectCompositionDevice</code> 的缩写</p>
<h2 id="对接窗口">对接窗口</h2>
<p>通过 CreateTargetForHwnd 方法可以将 DirectComposition 关联到窗口上,代码如下</p>
<pre><code class="language-csharp">      IDCompositionDevice compositionDevice = DComp.DCompositionCreateDevice3&lt;IDCompositionDevice&gt;(iD3D11Device);
      compositionDevice.CreateTargetForHwnd(window, topmost: true, out IDCompositionTarget compositionTarget);
</code></pre>
<p>这里的 CreateTargetForHwnd 的第二个参数比较迷惑,以上的 CreateTargetForHwnd 参数 topmost 不是值窗口置顶,而是:如果视觉树应显示在由 hwnd 参数指定的窗口子元素之上,则为 TRUE;否则,视觉树将显示在子元素之后。原文:</p>
<blockquote>
<p>TRUE if the visual tree should be displayed on top of the children of the window specified by the hwnd parameter; otherwise, the visual tree is displayed behind the children.</p>
</blockquote>
<p>详细请看 https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-idcompositiondevice-createtargetforhwnd</p>
<h2 id="创建视觉对象">创建视觉对象</h2>
<p>创建视觉对象之前,需要获取当前窗口的尺寸,代码如下</p>
<pre><code class="language-csharp">      RECT windowRect;
      PInvoke.GetClientRect(window, &amp;windowRect);
      var clientSize = new SizeI(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
</code></pre>
<p>创建视觉对象只需简单地调用 <code>IDCompositionDevice.CreateVisual</code> 方法,然而此时视觉对象还没有内容,可通过 <code>IDCompositionDevice.CreateVirtualSurface</code> 创建表面来作为内容,简单写法如下</p>
<pre><code class="language-csharp">      IDCompositionVisual compositionVisual = compositionDevice.CreateVisual();
      IDCompositionVirtualSurface surface = compositionDevice.CreateVirtualSurface((uint) clientSize.Width,
            (uint) clientSize.Height, Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied);
      compositionVisual.SetContent(surface);
</code></pre>
<p>创建表面时,可用 CreateVirtualSurface 或 CreateSurface 方法。两者不同的是 CreateVirtualSurface 创建的是稀疏表面,而 CreateSurface 是大数组(矩阵),在调用 BeginDraw 之前 IDCompositionVirtualSurface 不会分别空间,且 IDCompositionVirtualSurface 还能 Resize 重新设置大小。而 CreateSurface 则就不能。使用 CreateSurface 的例子如下</p>
<pre><code class="language-csharp">var createSurfaceResult = compositionDevice.CreateSurface((uint) clientSize.Width,
            (uint) clientSize.Height, Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied, out IDCompositionSurface? dCompositionSurface);
createSurfaceResult.CheckError();
</code></pre>
<p>创建表面需要传染颜色格式和对 AlphaMode 的处理,本文这里传入的是 Premultiplied 采用预乘方法。对 Premultiplied 简单来说就是最终输出的值里的 RGB 分量都乘以透明度。更多细节请参阅 支持的像素格式和 Alpha 模式 - Win32 apps - Microsoft Learn</p>
<p>传入 Premultiplied 预乘时,会更多占用 DWM 的资源,在 4K 的全屏窗口上,对比 Premultiplied 预乘与忽略 AlphaMode 的性能,可以看到预乘会比忽略占用多非常多的 DWM 资源。如果自己的应用是无需窗口背景透明的,还请设置为忽略 AlphaMode 模式</p>
<h2 id="配置视觉对象">配置视觉对象</h2>
<p>完成视觉对象创建之后,可将此视觉对象设置为窗口的根内容。在 DirectComposition 里可以设置视觉树,一个视觉对象上可以添加很多个视觉对象,但只有其中一个可以成为 IDCompositionTarget 的 Root 视觉对象</p>
<pre><code class="language-csharp">      compositionTarget.SetRoot(compositionVisual);
      compositionDevice.Commit(); // 非必须
</code></pre>
<h2 id="对接-d2d-渲染">对接 D2D 渲染</h2>
<p>以上代码就完成了视觉对象的创建和在窗口上显示的基础逻辑。为了能够绘制漂亮的界面,在本文这里将和 D2D 进行对接</p>
<p>为了能够和 D2D 进行对接,需要给 D2D 一个绘制表面。从 IDCompositionVirtualSurface 或 IDCompositionSurface 表面调用 BeginDraw 方法,即可获取到 IDXGISurface 表面,从而让 D2D 在此表面上绘制,基础逻辑如下</p>
<pre><code class="language-csharp">IDXGISurface dxgiSurface = surface.BeginDraw&lt;IDXGISurface&gt;(null, out var updateOffset);
</code></pre>
<p>再按照 D2D 的初始化方法,将 D2D 的 ID2D1RenderTarget 创建出来,代码如下</p>
<pre><code class="language-csharp">      // 工厂创建只需一次
      Vortice.Direct2D1.ID2D1Factory1 d2DFactory = Vortice.Direct2D1.D2D1.D2D1CreateFactory&lt;Vortice.Direct2D1.ID2D1Factory1&gt;();

      // 以下循环为每一帧执行
      while (!_isMainWindowClosed)
      {
            using IDXGISurface dxgiSurface = surface.BeginDraw&lt;IDXGISurface&gt;(null, out var updateOffset);

            var renderTargetProperties = new Vortice.Direct2D1.RenderTargetProperties()
            {
                PixelFormat = new PixelFormat(Format.B8G8R8A8_UNorm, Vortice.DCommon.AlphaMode.Premultiplied),
                Type = Vortice.Direct2D1.RenderTargetType.Hardware,
            };

            using Vortice.Direct2D1.ID2D1RenderTarget d2D1RenderTarget =
                d2DFactory.CreateDxgiSurfaceRenderTarget(dxgiSurface, renderTargetProperties);

            Vortice.Direct2D1.ID2D1RenderTarget renderTarget = d2D1RenderTarget;

            ... // 忽略其他代码
      }
</code></pre>
<p>拿到 ID2D1RenderTarget 即可进行 D2D 的绘制,在本文这里使用了名为 D2DRenderDemo 的辅助类进行绘制,具体代码可以在后文找到</p>
<pre><code class="language-csharp">      // 以下循环为每一帧执行
      while (!_isMainWindowClosed)
      {
            using IDXGISurface dxgiSurface = surface.BeginDraw&lt;IDXGISurface&gt;(null, out var updateOffset);

            var renderTargetProperties = new Vortice.Direct2D1.RenderTargetProperties()
            {
                PixelFormat = new PixelFormat(Format.B8G8R8A8_UNorm, Vortice.DCommon.AlphaMode.Premultiplied),
                Type = Vortice.Direct2D1.RenderTargetType.Hardware,
            };

            using Vortice.Direct2D1.ID2D1RenderTarget d2D1RenderTarget =
                d2DFactory.CreateDxgiSurfaceRenderTarget(dxgiSurface, renderTargetProperties);

            Vortice.Direct2D1.ID2D1RenderTarget renderTarget = d2D1RenderTarget;

            renderTarget.BeginDraw();

            // 在这里编写绘制逻辑
            renderTarget.Clear(new Color4(0f));
            d2DRenderDemo.Draw(renderTarget, clientSize);

            renderTarget.EndDraw();

            ... // 忽略其他代码
      }
</code></pre>
<p>当 D2D 绘制完成之后,需要调用 IDCompositionVirtualSurface 或 IDCompositionSurface 的 EndDraw 方法。再调用 IDCompositionDevice 的 Commit 方法将内容提交出去。当所有的窗口都完成绘制和 Commit 之后,调用 <code>IDCompositionDevice.WaitForCommitCompletion</code> 等待 DWM 消费。调用 <code>IDCompositionDevice.WaitForCommitCompletion</code> 约等于等待垂直同步,等待界面刷新</p>
<pre><code class="language-csharp">            renderTarget.BeginDraw();

            // 在这里编写绘制逻辑
            renderTarget.Clear(new Color4(0f));
            d2DRenderDemo.Draw(renderTarget, clientSize);

            renderTarget.EndDraw();
            surface.EndDraw();

            compositionDevice.Commit();
            compositionDevice.WaitForCommitCompletion();
</code></pre>
<p>在本文这里,由于只有一个窗口,于是在 Commit 之后即可立刻调用 WaitForCommitCompletion 方法了</p>
<p>整个渲染代码,即每一帧跑的代码如下</p>
<pre><code class="language-csharp">      Vortice.Direct2D1.ID2D1Factory1 d2DFactory = Vortice.Direct2D1.D2D1.D2D1CreateFactory&lt;Vortice.Direct2D1.ID2D1Factory1&gt;();

      var d2DRenderDemo = new D2DRenderDemo();

      while (!_isMainWindowClosed)
      {
            using IDXGISurface dxgiSurface = surface.BeginDraw&lt;IDXGISurface&gt;(null, out var updateOffset);
            _ = updateOffset;

            var renderTargetProperties = new Vortice.Direct2D1.RenderTargetProperties()
            {
                PixelFormat = new PixelFormat(Format.B8G8R8A8_UNorm, Vortice.DCommon.AlphaMode.Premultiplied),
                Type = Vortice.Direct2D1.RenderTargetType.Hardware,
            };

            using Vortice.Direct2D1.ID2D1RenderTarget d2D1RenderTarget =
                d2DFactory.CreateDxgiSurfaceRenderTarget(dxgiSurface, renderTargetProperties);

            Vortice.Direct2D1.ID2D1RenderTarget renderTarget = d2D1RenderTarget;

            renderTarget.BeginDraw();

            // 在这里编写绘制逻辑
            renderTarget.Clear(new Color4(0f));
            d2DRenderDemo.Draw(renderTarget, clientSize);

            renderTarget.EndDraw();
            surface.EndDraw();

            compositionDevice.Commit();
            compositionDevice.WaitForCommitCompletion();

            while (true)
            {
                var success = PInvoke.PeekMessage(out var message, window, 0, 0, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE);
                if (!success)
                {
                  break;
                }

                PInvoke.TranslateMessage(&amp;message);
                PInvoke.DispatchMessage(&amp;message);
            }
      }
</code></pre>
<p>以上代码也跑了 PeekMessage 方法防止窗口未响应</p>
<p>在每次进入绘制的时候调用 <code>renderTarget.Clear(new Color4(0f));</code> 可以解决双缓存带来的闪烁问题,即界面内容被分别不同步地绘制到两个纹理表面上。通过 Clear 确保每次都是重新绘制,解决此问题</p>
<p>什么时候可以不做清理而进行绘制?进行某些性能优化的时候,且此时应该确保绘制同步。或再开一个表面纹理,通过同步的方式再将最终界面绘制</p>
<h2 id="核心代码">核心代码</h2>
<p>核心的代码被我放在一个文件里面,代码如下</p>
<pre><code class="language-csharp">using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

using Vortice.DCommon;
using Vortice.Direct3D;
using Vortice.Direct3D11;
using Vortice.DirectComposition;
using Vortice.DXGI;
using Vortice.Mathematics;

using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.UI.WindowsAndMessaging;

using AlphaMode = Vortice.DXGI.AlphaMode;
using D2D = Vortice.Direct2D1;

namespace DijallnemrecerkuCheberewhibair;


class DirectCompositionDemo
{
    public unsafe void Run()
    {
      var window = CreateWindow();
      PInvoke.ShowWindow(window, SHOW_WINDOW_CMD.SW_MAXIMIZE);

      var result = D3D11.D3D11CreateDevice(null, DriverType.Hardware, DeviceCreationFlags.BgraSupport,
            featureLevels: [], out ID3D11Device iD3D11Device, out var feature,
            out ID3D11DeviceContext iD3D11DeviceContext);
      result.CheckError();

      _ = feature;
      iD3D11DeviceContext.Dispose(); // 用不着就先释放。释放不代表立刻回收资源,只是表示业务层不需要用到,减少引用计数

      IDCompositionDevice compositionDevice = DComp.DCompositionCreateDevice3&lt;IDCompositionDevice&gt;(iD3D11Device);
      compositionDevice.CreateTargetForHwnd(window, topmost: true, out IDCompositionTarget compositionTarget);
      // 以上的 CreateTargetForHwnd 参数 topmost 不是值窗口置顶,而是:如果视觉树应显示在由 hwnd 参数指定的窗口子元素之上,则为 TRUE;否则,视觉树将显示在子元素之后
      // &gt; TRUE if the visual tree should be displayed on top of the children of the window specified by the hwnd parameter; otherwise, the visual tree is displayed behind the children.
      // https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-idcompositiondevice-createtargetforhwnd

      // 创建视觉对象
      RECT windowRect;
      PInvoke.GetClientRect(window, &amp;windowRect);
      var clientSize = new SizeI(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);

      IDCompositionVisual compositionVisual = compositionDevice.CreateVisual();
      IDCompositionVirtualSurface surface = compositionDevice.CreateVirtualSurface((uint) clientSize.Width,
            (uint) clientSize.Height, Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied);

      // 创建 IDCompositionSurface 有两个方法,分别是 CreateVirtualSurface 和 CreateSurface 方法。两者不同的是 CreateVirtualSurface 创建的是稀疏表面,而 CreateSurface 是大数组(矩阵),在调用 BeginDraw 之前 IDCompositionVirtualSurface 不会分别空间,且 IDCompositionVirtualSurface 还能 Resize 重新设置大小。而 CreateSurface 则就不能
      //var createSurfaceResult = compositionDevice.CreateSurface((uint) clientSize.Width,
      //    (uint) clientSize.Height, Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied, out IDCompositionSurface? dCompositionSurface);
      //createSurfaceResult.CheckError();
      //surface = dCompositionSurface;

      compositionVisual.SetContent(surface);

      compositionTarget.SetRoot(compositionVisual);
      compositionDevice.Commit();

      Vortice.Direct2D1.ID2D1Factory1 d2DFactory = Vortice.Direct2D1.D2D1.D2D1CreateFactory&lt;Vortice.Direct2D1.ID2D1Factory1&gt;();

      var d2DRenderDemo = new D2DRenderDemo();

      while (!_isMainWindowClosed)
      {
            using IDXGISurface dxgiSurface = surface.BeginDraw&lt;IDXGISurface&gt;(null, out var updateOffset);
            _ = updateOffset;

            var renderTargetProperties = new Vortice.Direct2D1.RenderTargetProperties()
            {
                PixelFormat = new PixelFormat(Format.B8G8R8A8_UNorm, Vortice.DCommon.AlphaMode.Premultiplied),
                Type = Vortice.Direct2D1.RenderTargetType.Hardware,
            };

            using Vortice.Direct2D1.ID2D1RenderTarget d2D1RenderTarget =
                d2DFactory.CreateDxgiSurfaceRenderTarget(dxgiSurface, renderTargetProperties);

            Vortice.Direct2D1.ID2D1RenderTarget renderTarget = d2D1RenderTarget;

            renderTarget.BeginDraw();

            // 在这里编写绘制逻辑
            renderTarget.Clear(new Color4(0f));
            d2DRenderDemo.Draw(renderTarget, clientSize);

            renderTarget.EndDraw();
            surface.EndDraw();

            compositionDevice.Commit();
            compositionDevice.WaitForCommitCompletion();

            while (true)
            {
                var success = PInvoke.PeekMessage(out var message, window, 0, 0, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE);
                if (!success)
                {
                  break;
                }

                PInvoke.TranslateMessage(&amp;message);
                PInvoke.DispatchMessage(&amp;message);
            }
      }
    }

    private bool _isMainWindowClosed;

    private unsafe HWND CreateWindow()
    {
      PInvoke.DwmIsCompositionEnabled(out var compositionEnabled);

      if (!compositionEnabled)
      {
            Console.WriteLine($"无法启用透明窗口效果");
      }

      // (https://blog.lindexi.com/post/Windows-%E7%AA%97%E5%8F%A3%E6%A0%B7%E5%BC%8F-%E4%BB%80%E4%B9%88%E6%98%AF-WS_EX_NOREDIRECTIONBITMAP-%E6%A0%B7%E5%BC%8F.html )
      WINDOW_EX_STYLE exStyle = WINDOW_EX_STYLE.WS_EX_NOREDIRECTIONBITMAP;

      var style = WNDCLASS_STYLES.CS_OWNDC | WNDCLASS_STYLES.CS_HREDRAW | WNDCLASS_STYLES.CS_VREDRAW;

      var defaultCursor = PInvoke.LoadCursor(
            new HINSTANCE(IntPtr.Zero), new PCWSTR(PInvoke.IDC_ARROW.Value));

      var className = $"lindexi-{Guid.NewGuid().ToString()}";
      var title = "The Title";
      _wndProcDelegate = new WNDPROC(WndProc); // 仅用于防止 GC 回收。详细请看 https://github.com/lindexi/lindexi_gd/pull/85
      fixed (char* pClassName = className)
      fixed (char* pTitle = title)
      {
            var wndClassEx = new WNDCLASSEXW
            {
                cbSize = (uint) Marshal.SizeOf&lt;WNDCLASSEXW&gt;(),
                style = style,
                lpfnWndProc = _wndProcDelegate,
                hInstance = new HINSTANCE(PInvoke.GetModuleHandle(null).DangerousGetHandle()),
                hCursor = defaultCursor,
                hbrBackground = new HBRUSH(IntPtr.Zero),
                lpszClassName = new PCWSTR(pClassName)
            };
            ushort atom = PInvoke.RegisterClassEx(in wndClassEx);

            var dwStyle = WINDOW_STYLE.WS_OVERLAPPEDWINDOW | WINDOW_STYLE.WS_VISIBLE;

            var windowHwnd = PInvoke.CreateWindowEx(
                exStyle,
                new PCWSTR((char*) atom),
                new PCWSTR(pTitle),
                dwStyle,
                0, 0, 1900, 1000,
                HWND.Null, HMENU.Null, HINSTANCE.Null, null);

            return windowHwnd;
      }
    }

    private WNDPROC? _wndProcDelegate;

    private LRESULT WndProc(HWND hwnd, uint message, WPARAM wParam, LPARAM lParam)
    {
      if (message == PInvoke.WM_CLOSE)
      {
            _isMainWindowClosed = true;
      }

      return PInvoke.DefWindowProc(hwnd, message, wParam, lParam);
    }
}

class D2DRenderDemo
{
    // 此为调试代码,绘制一些矩形条
    private List&lt;D2DRenderInfo&gt;? _renderList;

    public void OnReSize()
    {
      _renderList = null;
    }

    public void Draw(D2D.ID2D1RenderTarget renderTarget, SizeI clientSize)
    {
      var rectWeight = 10;
      var rectHeight = 20;

      var margin = 5;

      if (_renderList is null)
      {
            _renderList = new List&lt;D2DRenderInfo&gt;();

            for (int top = margin; top &lt; clientSize.Height - rectHeight - margin; top += rectHeight + margin)
            {
                Rect rect = new Rect(margin, top, rectWeight, rectHeight);

                var color = new Color4(Random.Shared.NextSingle(), Random.Shared.NextSingle(),
                  Random.Shared.NextSingle());
                var step = Random.Shared.Next(1, 20);

                var renderInfo = new D2DRenderInfo(rect, step, color);
                _renderList.Add(renderInfo);
            }
      }

      for (var i = 0; i &lt; _renderList.Count; i++)
      {
            var renderInfo = _renderList;
            using var brush = renderTarget.CreateSolidColorBrush(renderInfo.Color);

            renderTarget.FillRectangle(renderInfo.Rect, brush);

            var nextRect = renderInfo.Rect with
            {
                Width = renderInfo.Rect.Width + renderInfo.Step
            };

            if (nextRect.Width &gt; clientSize.Width - margin * 2)
            {
                nextRect = nextRect with
                {
                  Width = rectWeight
                };
            }

            _renderList = renderInfo with
            {
                Rect = nextRect
            };
      }
    }

    private readonly record struct D2DRenderInfo(Rect Rect, int Step, Color4 Color);
}
</code></pre>
<h2 id="全部代码">全部代码</h2>
<p>本文代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快</p>
<p>先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码</p>
<pre><code>git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin daef15201848fb5c338f519d49f4879017590124
</code></pre>
<p>以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码</p>
<pre><code>git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin daef15201848fb5c338f519d49f4879017590124
</code></pre>
<p>获取代码之后,进入 DirectX/DirectComposition/DijallnemrecerkuCheberewhibair 文件夹,即可获取到源代码</p>
<h2 id="更多博客">更多博客</h2>
<p>渲染部分,关于 SharpDx 和 Vortice 的使用方法,包括入门级教程,请参阅:</p>
<ul>
<li>渲染博客导航</li>
<li>SharpDX 系列</li>
</ul>
<p>更多关于我博客请参阅 博客导航</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>博客园博客只做备份,博客发布就不再更新,如果想看最新博客,请访问 https://blog.lindexi.com/</p>

<p>如图片看不见,请在浏览器开启不安全http内容兼容</p>

<img alt="知识共享许可协议" style="border-width: 0" src="https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png"><br>本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名[林德熙](https://www.cnblogs.com/lindexi)(包含链接:https://www.cnblogs.com/lindexi ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我[联系](mailto:lindexi_gd@163.com)。<br><br>
来源:https://www.cnblogs.com/lindexi/p/19597758
頁: [1]
查看完整版本: dotnet Vortice 无需交换链与 DirectComposition 对接渲染层