一:背景 1.講故事 最近分享了好幾篇關於 非托管記憶體泄漏 的文章,有時候就是這麼神奇,來求助的都是這類型的dump,一飲一啄,莫非前定。讓我被迫加深對 NT堆, 頁堆 的理解,這一篇就給大家再帶來一篇記憶體泄漏。 前段時間有位朋友找到我,說他的程式出現了非托管泄漏,某一塊的操作會導致非托管記憶體上漲的 ...
本文告訴大家如何使用 Vortice 進行 D2D 的離屏渲染功能,本文將在一個純控制台無視窗的應用下,使用 Direct2D1 進行離屏繪製,將繪製結果保存為本地圖片文件
本文屬於使用 Vortice 調用 DirectX 系列博客,也屬於 DirectX 系列博客,本文屬於入門級博客,但在閱讀本文之前,期望大家瞭解了 DirectX 的基礎概念
本文使用的 Vortice 是 SharpDx 的代替品,是對 DirectX 的底層 C# 封裝。使用 Vortice 底層庫,能讓 C# 代碼比較方便的和 DirectX 對接。儘管本文使用的是 Vortice 庫來調用 DirectX 相關的介面,但不代表著只有 Vortice 庫能做此實現,可以將 Vortice 換成其他的對 DirectX 封裝的庫,例如 SharpDx 或者是 Silk.NET 等,更換之後只是調用的方法或者是參數等稍微有點不相同,但是實現思路都是相同的
使用 Direct2D1 離屏渲染技術,可以進行脫離具體的視窗調用渲染,可以不需要占用主線程的時間,採用後臺線程驅動執行渲染。可以實現在某些性能敏感的業務上,預先準備好渲染內容,從而提升性能等
新建一個 dotnet 6 的控制台項目,咱將在此新建的項目裡面完成一個簡單的 Direct2D1 離屏渲染的控制台應用。本文不會貼所有的代碼,如果按照本文給出的代碼構建不通過或者遇到其他問題,還請到本文末尾獲取所有源代碼,將源代碼拉到本地構建
按照慣例,在開始之前,先通過 NuGet 安裝必要的庫。在 dotnet 6 項目里,採用 SDK 風格的 csproj 項目文件格式,可以通過編輯項目文件的方式快速安裝 NuGet 庫。可以在 VisualStudio 里,右擊項目,點擊編輯項目文件,或者雙擊項目都可以,替換項目文件為以下代碼即可完成安裝
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Vortice.Direct2D1" Version="2.1.32" />
<PackageReference Include="Vortice.Win32" Version="1.6.2" />
</ItemGroup>
</Project>
以上代碼安裝了 Vortice.Direct2D1 庫用來對接 Direct2D1 的邏輯,安裝了 Vortice.Win32 用來輔助處理雜項邏輯
打開 Program.cs 文件,開始編寫離線渲染邏輯。開始編寫代碼之前,先引用命名空間
using Vortice.Mathematics;
using Vortice.WIC;
using D2D = Vortice.Direct2D1;
using PixelFormat = Vortice.DCommon.PixelFormat;
大家都知道,使用 D2D 時,最重要的就是獲取到 ID2D1RenderTarget 用來作為繪製的畫布。創建 ID2D1RenderTarget 的方法有很多個,本文是通過 ID2D1Factory1 工廠調用 CreateWicBitmapRenderTarget 方法,從一個 IWICBitmap 創建的
也就是說想要獲取到 ID2D1RenderTarget 進行繪製,就需要能先拿到 IWICBitmap 類型的對象。創建 IWICBitmap 類型的對象需要通過 WIC 工廠進行創建。於是先創建工廠
using var wicImagingFactory = new IWICImagingFactory();
通過 IWICImagingFactory 工廠創建 IWICBitmap 對象,需要調用 CreateBitmap 方法,傳入尺寸和顏色格式。顏色格式裡面只有一些是 D2D 支持的,本文這裡採用常用的 PixelFormat32bppPBGRA 格式
using IWICBitmap wicBitmap =
wicImagingFactory.CreateBitmap(1000, 1000, Win32.Graphics.Imaging.Apis.GUID_WICPixelFormat32bppPBGRA);
獲取到 IWICBitmap 類型的對象,即可開始通過 D2D 工廠創建 ID2D1RenderTarget 畫布。先創建 D2D 工廠
using D2D.ID2D1Factory1 d2DFactory = D2D.D2D1.D2D1CreateFactory<D2D.ID2D1Factory1>();
接著設置渲染參數
var renderTargetProperties = new D2D.RenderTargetProperties(PixelFormat.Premultiplied);
創建 ID2D1RenderTarget 對象
D2D.ID2D1RenderTarget d2D1RenderTarget =
d2DFactory.CreateWicBitmapRenderTarget(wicBitmap, renderTargetProperties);
以上就是最核心的步驟了,獲取到 ID2D1RenderTarget 對象,即可開始 D2D 的繪製邏輯,如下麵代碼,修改畫布顏色
using var renderTarget = d2D1RenderTarget;
// 開始繪製邏輯
renderTarget.BeginDraw();
// 隨意創建顏色
var color = new Color4((byte) Random.Shared.Next(255), (byte) Random.Shared.Next(255),
(byte) Random.Shared.Next(255));
renderTarget.Clear(color);
renderTarget.EndDraw();
如此即可將內容繪製到 IWICBitmap 上
接下來是將 IWICBitmap 的內容保存到本地的圖片,保存 IWICBitmap 需要先對 IWICBitmap 進行編碼,編碼時需要使用 WIC 工廠創建編碼器,接著傳入編碼的格式和編碼的輸出
先打開一個文件用來存放編碼的輸出
var file = @"D2D.png";
using (var fileStream = File.OpenWrite(file))
{
// 忽略代碼
}
通過 WIC 工廠創建編碼器,設置編碼的格式是 png 格式
using var wicBitmapEncoder =
wicImagingFactory.CreateEncoder(Win32.Graphics.Imaging.Apis.GUID_ContainerFormatPng);
設置編碼的輸出到文件
wicBitmapEncoder.Initialize(fileStream);
從編碼器創建出一張圖片
using var wicFrameEncode = wicBitmapEncoder.CreateNewFrame(out var _);
wicFrameEncode.Initialize();
將上文完成繪製的 IWICBitmap 輸入到編碼器裡面
wicFrameEncode.WriteSource(wicBitmap);
完成邏輯之後提交一下
wicFrameEncode.Commit();
wicBitmapEncoder.Commit();
如此執行完成,即可將繪製的內容保存到本地文件里。這就是本文的採用 D2D 進行離屏繪製的方法
想不開的話,可以測試一下調用渲染時是否能跑滿 GPU 資源,稍微更改一下渲染的代碼,從原本的調用 Clear 修改顏色,修改為以下邏輯
using var renderTarget = d2D1RenderTarget;
var stopwatch = Stopwatch.StartNew();
while (true)
{
// 開始繪製邏輯
renderTarget.BeginDraw();
// 隨意創建顏色
var color = new Color4((byte) Random.Shared.Next(255), (byte) Random.Shared.Next(255),
(byte) Random.Shared.Next(255));
renderTarget.Clear(color);
color = new Color4(GetRandom(), GetRandom(), GetRandom());
using var brush = renderTarget.CreateSolidColorBrush(color);
for (int i = 0; i < 1000; i++)
{
renderTarget.DrawEllipse(new D2D.Ellipse(new Vector2(GetRandom(), GetRandom()), 5, 5), brush, 2);
}
stopwatch.Stop();
Console.WriteLine($"Draw: {stopwatch.ElapsedMilliseconds}");
stopwatch.Restart();
renderTarget.EndDraw();
stopwatch.Stop();
Console.WriteLine($"EndDraw: {stopwatch.ElapsedMilliseconds}");
stopwatch.Restart();
byte GetRandom() => (byte) Random.Shared.Next(255);
}
嘗試運行代碼,看看任務管理器裡面,顯示當前進程是否有用到 GPU 資源,以及占用了多少 GPU 資源
可以通過如下方式獲取本文的源代碼,先創建一個空文件夾,接著使用命令行 cd 命令進入此空文件夾,在命令行裡面輸入以下代碼,即可獲取到本文的代碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin bb1f1f3db2cf7317341e830d1e3adb14df67a71e
以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令行繼續輸入以下代碼
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin bb1f1f3db2cf7317341e830d1e3adb14df67a71e
獲取代碼之後,進入 WakolerwhaKanicabirem 文件夾
渲染部分,關於 SharpDx 使用,包括入門級教程,請參閱:
在 WPF 框架的渲染部分,請參閱: WPF 底層渲染_lindexi_gd的博客-CSDN博客
更多關於我博客請參閱 博客導航
交流 Vortice 技術,歡迎加群: 622808968
博客園博客只做備份,博客發佈就不再更新,如果想看最新博客,請到 https://blog.lindexi.com/
本作品採用知識共用署名-非商業性使用-相同方式共用 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發佈,但務必保留文章署名[林德熙](https://www.cnblogs.com/lindexi)(包含鏈接:https://www.cnblogs.com/lindexi ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我[聯繫](mailto:[email protected])。