本文告訴大家如何使用 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 ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。如有任何疑問,請與我聯絡。