本文告訴大家如何使用 dotnet 基金會新開源的 Silk.NET 庫調用 DirectX 進行渲染的方法。此庫是對 DirectX 的底層基礎封裝,用上了 dotnet 和 C# 的各個新特性,相對來說基礎性能較好,也許后續可以考慮作為 SharpDx 的代替
本文將告訴大家如何使用 Silk.NET 創建 DirectX 的各個對象,進行初始化邏輯,再對接 Direct2D 進行界面繪制。當前是 2021.12.23 此時 Silk.NET 還沒有完成 Direct2D 的封裝,為了方便演示,本文使用了 SharpDx 的 D2D 代替
本文非新手友好,如果是剛接觸 DirectX 那我推薦先閱讀 WPF 使用 SharpDx 渲染博客導航
當前 SharpDx 已不維護,我正在找代替的項目,詳細請看 SharpDx 的代替項目
剛好找到了 dotnet 基金會下的 Silk.NET 庫,此庫是新寫的,用上了很多 dotnet 和 C# 的新特性,例如通過 COM 調用 DirectX 的實現邏輯是通過了 delegate* unmanaged
新特性,這是 C# 9 的新特性,請看 Function pointers - C# 9.0 draft specifications Microsoft Docs
代碼的寫法如下
public ID3D11Device
(
void** lpVtbl = null
) : this()
{
if (lpVtbl is not null)
{
LpVtbl = lpVtbl;
}
}
public void** LpVtbl;
public readonly unsafe int QueryInterface(Guid* riid, void** ppvObject)
{
var @this = (ID3D11Device*) Unsafe.AsPointer(ref Unsafe.AsRef(in this));
int ret = default;
ret = ((delegate* unmanaged[Cdecl]<ID3D11Device*, Guid*, void**, int>)LpVtbl[0])(@this, riid, ppvObject);
return ret;
}
通過以上的代碼,特別是 ((delegate* unmanaged[Cdecl]<ID3D12Device*, Guid*, void**, int>)LpVtbl[0])(@this, riid, ppvObject);
這句如此復雜的代碼,即可減少 COM 默認 dotnet 封裝的 RCW 封裝層的封送損耗。當然了,這部分不是本文的重點,細節請看 Runtime Callable Wrapper Microsoft Docs
大家只需要知道,此庫的實現里面,可以很大減少調用 COM 時的額外損耗。但這也帶來了一點坑,例如調用方也只能采用不安全代碼調用,寫法也有點詭異
根據 Surface sharing between Windows graphics APIs - Win32 apps 文檔,為了在 WPF 的 D3DImage 上進行 D2D 繪制,就需要通過 D3D11 進行轉接,好在此轉接也只是指針的傳輸而已,基本沒有啥性能損耗。為了在 WPF 上使用到 D2D 就需要執行如下步驟:
- 創建 D3D11 設備
- 通過 DXGI 關聯 D2D 設備
- 創建 D3D9 設備
如官方文檔的轉換圖
使用 DirectX 時,初始化參數的代碼將會特別多。由於 Silk.NET 只是對 DirectX 的底層封裝,沒有細節隱藏,也就是說使用過程的復雜度也會特別多
在開始之前,先准備一個空 WPF 項目,基於 dotnet 6 框架。安裝好如下庫,可編輯 csproj 文件,修改為如下代碼
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" />
<PackageReference Include="Silk.NET.Direct3D11" Version="2.11.0" />
<PackageReference Include="Silk.NET.Direct3D9" Version="2.11.0" />
<PackageReference Include="Silk.NET.DXGI" Version="2.11.0" />
</ItemGroup>
</Project>
以上代碼關鍵在於 AllowUnsafeBlocks 需要開啟,用於開啟不安全代碼給 Silk.NET 調用代碼所使用。當前 Silk.NET 還沒有完成 D2D 封裝,本文將使用 SharpDX.Direct2D1 庫輔助編寫 D2D 的代碼
在 XAML 界面添加 D3DImage 如下面代碼
<Window x:Class="RawluharkewalQeaninanel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RawluharkewalQeaninanel"
xmlns:interop="clr-namespace:System.Windows.Interop;assembly=PresentationCore"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Image>
<Image.Source>
<interop:D3DImage x:Name="D3DImage"></interop:D3DImage>
</Image.Source>
</Image>
</Grid>
</Window>
為了等待窗口等初始化完成,將在 Loaded 時進行實際的初始化代碼
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
在 MainWindow_Loaded 上添加本文的關鍵邏輯
按照順序,先創建 D3D11 設備和初始化。開始前,考慮到命名空間十分復雜,為了方便理解,先定義引用,如以下代碼
using Silk.NET.Core.Native;
using D3D11 = Silk.NET.Direct3D11;
using D3D9 = Silk.NET.Direct3D9;
using DXGI = Silk.NET.DXGI;
using D2D = SharpDX.Direct2D1;
using SharpDXDXGI = SharpDX.DXGI;
using SharpDXMathematics = SharpDX.Mathematics.Interop;
雖然加上此命名空間引用會讓代碼寫的時候,稍微復雜一點,但好在清晰
定義完成之后,開始創建 D3D11 設備。 創建過程中,需要先設置參數,代碼如下
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// 根據 [Surface sharing between Windows graphics APIs - Win32 apps](https://docs.microsoft.com/en-us/windows/win32/direct3darticles/surface-sharing-between-windows-graphics-apis?WT.mc_id=WD-MVP-5003260 ) 文檔
var width = ImageWidth;
var height = ImageHeight;
// 2021.12.23 不能在 x86 下運行,會炸掉。參閱 https://github.com/dotnet/Silk.NET/issues/731
var texture2DDesc = new D3D11.Texture2DDesc()
{
BindFlags = (uint) (D3D11.BindFlag.BindRenderTarget | D3D11.BindFlag.BindShaderResource),
Format = DXGI.Format.FormatB8G8R8A8Unorm, // 最好使用此格式,否則還需要后續轉換
Width = (uint) width,
Height = (uint) height,
MipLevels = 1,
SampleDesc = new DXGI.SampleDesc(1, 0),
Usage = D3D11.Usage.UsageDefault,
MiscFlags = (uint) D3D11.ResourceMiscFlag.ResourceMiscShared,
// The D3D11_RESOURCE_MISC_FLAG cannot be used when creating resources with D3D11_CPU_ACCESS flags.
CPUAccessFlags = 0, //(uint) D3D11.CpuAccessFlag.None,
ArraySize = 1
};
// 忽略代碼
}
private int ImageWidth => (int) ActualWidth;
private int ImageHeight => (int) ActualHeight;
需要特別說明以上代碼的一個注釋,當前 Silk.NET 對 X86 的支持較弱,調試模式下運行將會炸掉應用,非調試模式下沒啥問題。其原因是 Silk.NET 對於 COM 封裝在定義上是不對的,我給官方報告了此問題,請看 https://github.com/dotnet/Silk.NET/issues/731
問題的原因是在 Silk.NET 里面,定義對 DirectX 的調用,使用的是 Cdecl 方式調用,然而在 DirectX 的定義里,需要采用 Stdcall 來調用才是正確的。此行為將在 X86 下導致調用棧的內容不對,本應該清理的內容沒有正確清理。這部分細節請參閱 stdcall Microsoft Docs 和 cdecl Microsoft Docs 官方文檔
創建參數里,為了方便在 WPF 里使用,要求最好使用 FormatB8G8R8A8Unorm
格式。以上參數差不多是固定寫法,各個參數的細節請看 DirectX 官方文檔
接下來通過 D3D11 類型的 GetApi 方法獲取 D3D11 對象,此對象的獲取是 Silk.NET 的封裝,不屬於 DirectX 的內容
D3D11.D3D11 d3D11 = D3D11.D3D11.GetApi();
因為 Silk.NET 的封裝特別底層,需要開啟不安全代碼才能創建對象,為了方便編寫代碼,將在 class 上加上 unsafe 讓此類的所有代碼在使用不安全代碼,不需要再加上 unsafe 即可使用
public unsafe partial class MainWindow : Window
{
}
創建 D3D11 設備的代碼如下
D3D11.ID3D11Device* pD3D11Device;
D3D11.ID3D11DeviceContext* pD3D11DeviceContext;
D3DFeatureLevel pD3DFeatureLevel = default;
var hr = d3D11.CreateDevice((DXGI.IDXGIAdapter*) IntPtr.Zero, D3DDriverType.D3DDriverTypeHardware,
Software: 0,
Flags: (uint) D3D11.CreateDeviceFlag.CreateDeviceBgraSupport,
(D3DFeatureLevel*) IntPtr.Zero,
FeatureLevels: 0, // D3DFeatureLevel 的長度
SDKVersion: 7,
(D3D11.ID3D11Device**) &pD3D11Device, // 參閱 [C# 從零開始寫 SharpDx 應用 聊聊功能等級](https://blog.lindexi.com/post/C-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%86%99-SharpDx-%E5%BA%94%E7%94%A8-%E8%81%8A%E8%81%8A%E5%8A%9F%E8%83%BD%E7%AD%89%E7%BA%A7.html )
ref pD3DFeatureLevel,
(D3D11.ID3D11DeviceContext**) &pD3D11DeviceContext
);
SilkMarshal.ThrowHResult(hr);
可以看到代碼里面大量用到不安全代碼
在創建完成了 D3D11 設備之后,即可開始創建 Texture 對象。咱的步驟是創建出 Texture 用來共享和給 D2D 繪制用,但 D2D 繪制在的是 Texture 的 IDXGISurface 平面上
創建 Texture2D 代碼如下
D3D11.ID3D11Texture2D* pD3D11Texture2D;
hr = pD3D11Device->CreateTexture2D(ref texture2DDesc, (D3D11.SubresourceData*) IntPtr.Zero, &pD3D11Texture2D);
SilkMarshal.ThrowHResult(hr);
此 ID3D11Texture2D 就是作為后續 D2D 繪制的 IDXGISurface 對象
var renderTarget = pD3D11Texture2D;
DXGI.IDXGISurface* pDXGISurface;
var dxgiSurfaceGuid = DXGI.IDXGISurface.Guid;
renderTarget->QueryInterface(ref dxgiSurfaceGuid, (void**) &pDXGISurface);
接下來部分就是 SharpDx 的啦,當前 Silk.NET 還沒有封裝好 D2D 部分,於是這里就和 WPF 使用 SharpDX 博客的方法差不多,只是創建 SharpDX 的 Surface 代碼稍微修改而已
var surface = new SharpDXDXGI.Surface(new IntPtr((void*) pDXGISurface));
其他邏輯如下
var d2DFactory = new D2D.Factory();
var renderTargetProperties =
new D2D.RenderTargetProperties(new D2D.PixelFormat(SharpDXDXGI.Format.Unknown, D2D.AlphaMode.Premultiplied));
_d2DRenderTarget = new D2D.RenderTarget(d2DFactory, surface, renderTargetProperties);
private D2D.RenderTarget _d2DRenderTarget;
拿到了 D2D.RenderTarget 就可以進行 D2D 繪制。但是在開始前,還需要關聯到 WPF 的 D3DImage 才能渲染。為了關聯 D3DImage 就需要繼續創建 D3D9 設備,如下面代碼,調用 SetRenderTarget 將 D3D11 創建的 ID3D11Texture2D 作為 D3D9 的共享紋理,從而讓 D2D 的內容可以在 D3DImage 上使用
SetRenderTarget(renderTarget);
在 SetRenderTarget 的代碼是從 ID3D11Texture2D 轉到 IDirect3DSurface9 上,將 IDirect3DSurface9 作為 D3DImage 的 BackBuffer 給 WPF 使用
private void SetRenderTarget(D3D11.ID3D11Texture2D* target)
{
}
從 ID3D11Texture2D 轉到 IDirect3DSurface9 上有如下步驟:
- 獲取共享指針
- 創建 D3D9 設備
- 通過 D3D9 設備,使用共享指針創建紋理,通過紋理獲取平面
獲取共享指針是為了讓 D3D9 的紋理共享 D3D11 的資源,獲取代碼如下
DXGI.IDXGIResource* pDXGIResource;
var dxgiResourceGuid = DXGI.IDXGIResource.Guid;
target->QueryInterface(ref dxgiResourceGuid, (void**) &pDXGIResource);
void* sharedHandle;
var hr = pDXGIResource->GetSharedHandle(&sharedHandle);
SilkMarshal.ThrowHResult(hr);
創建 D3D9 之前,需要使用 Silk.NET 的 D3D9 類的 GetApi 對象獲取 D3D9 對象。這是 Silk.NET 的設計,可以看到此庫很多類型都有 GetApi 方法
var d3d9 = D3D9.D3D9.GetApi();
創建 D3D9 設備之前,需要先創建 IDirect3D9Ex 對象
D3D9.IDirect3D9Ex* pDirect3D9Ex;
hr = d3d9.Direct3DCreate9Ex(SDKVersion: 32, &pDirect3D9Ex);
SilkMarshal.ThrowHResult(hr);
var d3DContext = pDirect3D9Ex;
創建 D3D9 設備之前,也需要初始化參數,有一些參數需要和 D3D11 創建的參數相同,需要先獲取 D3D11 的參數
D3D11.Texture2DDesc texture2DDescription = default;
target->GetDesc(ref texture2DDescription);
初始化創建 D3D9 的創建參數
var presentParameters = new D3D9.PresentParameters()
{
Windowed = 1,// true
SwapEffect = D3D9.Swapeffect.SwapeffectDiscard,
HDeviceWindow = GetDesktopWindow(),
PresentationInterval = D3D9.D3D9.PresentIntervalDefault,
};
// 設置使用多線程方式,這樣的性能才足夠
uint createFlags = D3D9.D3D9.CreateHardwareVertexprocessing | D3D9.D3D9.CreateMultithreaded | D3D9.D3D9.CreateFpuPreserve;
[DllImport("user32.dll", SetLastError = false)]
public static extern IntPtr GetDesktopWindow();
拿到創建參數,創建 D3D9 設備
D3D9.IDirect3DDevice9Ex* pDirect3DDevice9Ex;
hr = d3DContext->CreateDeviceEx(Adapter: 0,
DeviceType: D3D9.Devtype.DevtypeHal,// 使用硬件渲染
hFocusWindow: IntPtr.Zero,
createFlags,
ref presentParameters,
pFullscreenDisplayMode: (D3D9.Displaymodeex*) IntPtr.Zero,
&pDirect3DDevice9Ex);
SilkMarshal.ThrowHResult(hr);
var d3DDevice = pDirect3DDevice9Ex;
拿到 D3D9 設備,開始創建紋理
D3D9.IDirect3DTexture9* pDirect3DTexture9;
hr = d3DDevice->CreateTexture(texture2DDescription.Width, texture2DDescription.Height, Levels: 1,
D3D9.D3D9.UsageRendertarget,
D3D9.Format.FmtA8R8G8B8, // 這是必須要求的顏色,不能使用其他顏色
D3D9.Pool.PoolDefault,
&pDirect3DTexture9,
&sharedHandle);
SilkMarshal.ThrowHResult(hr);
_renderTarget = pDirect3DTexture9;
private D3D9.IDirect3DTexture9* _renderTarget;
紋理有要求顏色格式,也要求尺寸和 D3D11 的相同
通過紋理可以拿到 IDirect3DSurface9 對象
D3D9.IDirect3DSurface9* pDirect3DSurface9;
_renderTarget->GetSurfaceLevel(0, &pDirect3DSurface9);
_pDirect3DSurface9 = pDirect3DSurface9;
將 IDirect3DSurface9 作為 D3DImage 的 BackBuffer 即可完成初始化
D3DImage.Lock();
D3DImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, new IntPtr(pDirect3DSurface9));
D3DImage.Unlock();
在 MainWindow_Loaded 設置將一個視圖數組綁定到管道的光柵化階段
var viewport = new D3D11.Viewport(0, 0, width, height, 0, 1);
pD3D11DeviceContext->RSSetViewports(NumViewports: 1, ref viewport);
開始測試 D2D 的渲染,通過測試 D2D 即可了解是否創建初始化成功。在 WPF 的 CompositionTarget 的 Rendering 進行 D2D 繪制
CompositionTarget.Rendering += CompositionTarget_Rendering;
private void CompositionTarget_Rendering(object? sender, EventArgs e)
{
_d2DRenderTarget.BeginDraw();
OnRender(_d2DRenderTarget);
_d2DRenderTarget.EndDraw();
D3DImage.Lock();
D3DImage.AddDirtyRect(new Int32Rect(0, 0, D3DImage.PixelWidth, D3DImage.PixelHeight));
D3DImage.Unlock();
}
在 OnRender 方法加上 D2D 的繪制內容,這就是測試邏輯,請根據自己的需求編寫
private void OnRender(D2D.RenderTarget renderTarget)
{
var brush = new D2D.SolidColorBrush(_d2DRenderTarget, new SharpDXMathematics.RawColor4(1, 0, 0, 1));
renderTarget.Clear(null);
renderTarget.DrawRectangle(new SharpDXMathematics.RawRectangleF(_x, _y, _x + 10, _y + 10), brush);
_x = _x + _dx;
_y = _y + _dy;
if (_x >= ActualWidth - 10 || _x <= 0)
{
_dx = -_dx;
}
if (_y >= ActualHeight - 10 || _y <= 0)
{
_dy = -_dy;
}
}
private float _x;
private float _y;
private float _dx = 1;
private float _dy = 1;
按照微軟官方的推薦,在 CompositionTarget_Rendering 里,如果進行 DirectX 的邏輯,需要判斷是否進入了多次,但本文這里只是測試邏輯,忽略官方給出的邏輯
運行代碼即可看到界面上有一個矩形顯示
也許后續我會封裝一個 Silk.NET 的 DirectX 給 WPF 使用的控件
#nullable disable
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using Silk.NET.Core.Native;
using D3D11 = Silk.NET.Direct3D11;
using D3D9 = Silk.NET.Direct3D9;
using DXGI = Silk.NET.DXGI;
using D2D = SharpDX.Direct2D1;
using SharpDXDXGI = SharpDX.DXGI;
using SharpDXMathematics = SharpDX.Mathematics.Interop;
namespace RawluharkewalQeaninanel
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public unsafe partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// 根據 [Surface sharing between Windows graphics APIs - Win32 apps](https://docs.microsoft.com/en-us/windows/win32/direct3darticles/surface-sharing-between-windows-graphics-apis?WT.mc_id=WD-MVP-5003260 ) 文檔
var width = ImageWidth;
var height = ImageHeight;
// 2021.12.23 不能在 x86 下運行,會炸掉。參閱 https://github.com/dotnet/Silk.NET/issues/731
var texture2DDesc = new D3D11.Texture2DDesc()
{
BindFlags = (uint) (D3D11.BindFlag.BindRenderTarget | D3D11.BindFlag.BindShaderResource),
Format = DXGI.Format.FormatB8G8R8A8Unorm, // 最好使用此格式,否則還需要后續轉換
Width = (uint) width,
Height = (uint) height,
MipLevels = 1,
SampleDesc = new DXGI.SampleDesc(1, 0),
Usage = D3D11.Usage.UsageDefault,
MiscFlags = (uint) D3D11.ResourceMiscFlag.ResourceMiscShared,
// The D3D11_RESOURCE_MISC_FLAG cannot be used when creating resources with D3D11_CPU_ACCESS flags.
CPUAccessFlags = 0, //(uint) D3D11.CpuAccessFlag.None,
ArraySize = 1
};
D3D11.ID3D11Device* pD3D11Device;
D3D11.ID3D11DeviceContext* pD3D11DeviceContext;
D3DFeatureLevel pD3DFeatureLevel = default;
D3D11.D3D11 d3D11 = D3D11.D3D11.GetApi();
var hr = d3D11.CreateDevice((DXGI.IDXGIAdapter*) IntPtr.Zero, D3DDriverType.D3DDriverTypeHardware,
Software: 0,
Flags: (uint) D3D11.CreateDeviceFlag.CreateDeviceBgraSupport,
(D3DFeatureLevel*) IntPtr.Zero,
FeatureLevels: 0, // D3DFeatureLevel 的長度
SDKVersion: 7,
(D3D11.ID3D11Device**) &pD3D11Device, // 參閱 [C# 從零開始寫 SharpDx 應用 聊聊功能等級](https://blog.lindexi.com/post/C-%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%86%99-SharpDx-%E5%BA%94%E7%94%A8-%E8%81%8A%E8%81%8A%E5%8A%9F%E8%83%BD%E7%AD%89%E7%BA%A7.html )
ref pD3DFeatureLevel,
(D3D11.ID3D11DeviceContext**) &pD3D11DeviceContext
);
SilkMarshal.ThrowHResult(hr);
Debugger.Launch();
Debugger.Break();
_pD3D11Device = pD3D11Device;
_pD3D11DeviceContext = pD3D11DeviceContext;
D3D11.ID3D11Texture2D* pD3D11Texture2D;
hr = pD3D11Device->CreateTexture2D(ref texture2DDesc, (D3D11.SubresourceData*) IntPtr.Zero, &pD3D11Texture2D);
SilkMarshal.ThrowHResult(hr);
var renderTarget = pD3D11Texture2D;
_pD3D11Texture2D = pD3D11Texture2D;
DXGI.IDXGISurface* pDXGISurface;
var dxgiSurfaceGuid = DXGI.IDXGISurface.Guid;
renderTarget->QueryInterface(ref dxgiSurfaceGuid, (void**) &pDXGISurface);
_pDXGISurface = pDXGISurface;
var d2DFactory = new D2D.Factory();
var renderTargetProperties =
new D2D.RenderTargetProperties(new D2D.PixelFormat(SharpDXDXGI.Format.Unknown, D2D.AlphaMode.Premultiplied));
var surface = new SharpDXDXGI.Surface(new IntPtr((void*) pDXGISurface));
_d2DRenderTarget = new D2D.RenderTarget(d2DFactory, surface, renderTargetProperties);
SetRenderTarget(renderTarget);
var viewport = new D3D11.Viewport(0, 0, width, height, 0, 1);
pD3D11DeviceContext->RSSetViewports(NumViewports: 1, ref viewport);
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
private void CompositionTarget_Rendering(object? sender, EventArgs e)
{
_d2DRenderTarget.BeginDraw();
OnRender(_d2DRenderTarget);
_d2DRenderTarget.EndDraw();
D3DImage.Lock();
D3DImage.AddDirtyRect(new Int32Rect(0, 0, D3DImage.PixelWidth, D3DImage.PixelHeight));
D3DImage.Unlock();
}
private void OnRender(D2D.RenderTarget renderTarget)
{
var brush = new D2D.SolidColorBrush(_d2DRenderTarget, new SharpDXMathematics.RawColor4(1, 0, 0, 1));
renderTarget.Clear(null);
renderTarget.DrawRectangle(new SharpDXMathematics.RawRectangleF(_x, _y, _x + 10, _y + 10), brush);
_x = _x + _dx;
_y = _y + _dy;
if (_x >= ActualWidth - 10 || _x <= 0)
{
_dx = -_dx;
}
if (_y >= ActualHeight - 10 || _y <= 0)
{
_dy = -_dy;
}
}
private float _x;
private float _y;
private float _dx = 1;
private float _dy = 1;
private void SetRenderTarget(D3D11.ID3D11Texture2D* target)
{
DXGI.IDXGIResource* pDXGIResource;
var dxgiResourceGuid = DXGI.IDXGIResource.Guid;
target->QueryInterface(ref dxgiResourceGuid, (void**) &pDXGIResource);
D3D11.Texture2DDesc texture2DDescription = default;
target->GetDesc(ref texture2DDescription);
void* sharedHandle;
var hr = pDXGIResource->GetSharedHandle(&sharedHandle);
SilkMarshal.ThrowHResult(hr);
var d3d9 = D3D9.D3D9.GetApi();
D3D9.IDirect3D9Ex* pDirect3D9Ex;
hr = d3d9.Direct3DCreate9Ex(SDKVersion: 32, &pDirect3D9Ex);
SilkMarshal.ThrowHResult(hr);
var d3DContext = pDirect3D9Ex;
_pDirect3D9Ex = pDirect3D9Ex;
var presentParameters = new D3D9.PresentParameters()
{
Windowed = 1,// true
SwapEffect = D3D9.Swapeffect.SwapeffectDiscard,
HDeviceWindow = GetDesktopWindow(),
PresentationInterval = D3D9.D3D9.PresentIntervalDefault,
};
// 設置使用多線程方式,這樣的性能才足夠
uint createFlags = D3D9.D3D9.CreateHardwareVertexprocessing | D3D9.D3D9.CreateMultithreaded | D3D9.D3D9.CreateFpuPreserve;
D3D9.IDirect3DDevice9Ex* pDirect3DDevice9Ex;
hr = d3DContext->CreateDeviceEx(Adapter: 0,
DeviceType: D3D9.Devtype.DevtypeHal,// 使用硬件渲染
hFocusWindow: IntPtr.Zero,
createFlags,
ref presentParameters,
pFullscreenDisplayMode: (D3D9.Displaymodeex*) IntPtr.Zero,
&pDirect3DDevice9Ex);
SilkMarshal.ThrowHResult(hr);
var d3DDevice = pDirect3DDevice9Ex;
D3D9.IDirect3DTexture9* pDirect3DTexture9;
hr = d3DDevice->CreateTexture(texture2DDescription.Width, texture2DDescription.Height, Levels: 1,
D3D9.D3D9.UsageRendertarget,
D3D9.Format.FmtA8R8G8B8, // 這是必須要求的顏色,不能使用其他顏色
D3D9.Pool.PoolDefault,
&pDirect3DTexture9,
&sharedHandle);
SilkMarshal.ThrowHResult(hr);
_renderTarget = pDirect3DTexture9;
D3D9.IDirect3DSurface9* pDirect3DSurface9;
_renderTarget->GetSurfaceLevel(0, &pDirect3DSurface9);
_pDirect3DSurface9 = pDirect3DSurface9;
D3DImage.Lock();
D3DImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, new IntPtr(pDirect3DSurface9));
D3DImage.Unlock();
}
// 這些字段的另一個作用是防止回收
private D2D.RenderTarget _d2DRenderTarget;
private D3D11.ID3D11Device* _pD3D11Device;
private D3D11.ID3D11DeviceContext* _pD3D11DeviceContext;
private D3D11.ID3D11Texture2D* _pD3D11Texture2D;
private DXGI.IDXGISurface* _pDXGISurface;
private D3D9.IDirect3D9Ex* _pDirect3D9Ex;
private D3D9.IDirect3DTexture9* PDirect3DTexture9 => _renderTarget;
private D3D9.IDirect3DTexture9* _renderTarget;
private D3D9.IDirect3DSurface9* _pDirect3DSurface9;
private int ImageWidth => (int) ActualWidth;
private int ImageHeight => (int) ActualHeight;
[DllImport("user32.dll", SetLastError = false)]
public static extern IntPtr GetDesktopWindow();
}
}
可以通過如下方式獲取本文的源代碼,先創建一個空文件夾,接着使用命令行 cd 命令進入此空文件夾,在命令行里面輸入以下代碼,即可獲取到本文的代碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin f4c2f884b3fb006676aeef7e249055c5e2d8766d
以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
獲取代碼之后,進入 RawluharkewalQeaninanel 文件夾
本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我聯系。