本文告诉大家如何使用 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 Docscdecl 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();
}
}

本文所有代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

WPF 使用 Silk.NET 进行 DirectX 渲染入门的更多相关文章

  1. .NET实时2D渲染入门·动态时钟

    .NET实时2D渲染入门·动态时钟 从小以来"坦克大战"."魂斗罗"等游戏总令我魂牵梦绕.这些游戏的基础就是2D实时渲染,以前没意识,直到后来找到了Direct ...

  2. WPF 使用 Composition API 做高性能渲染

    在 WPF 中很多小伙伴都会遇到渲染性能的问题,虽然 WPF 的渲染可以甩浏览器渲染几条街,但是还是支持不了游戏级的渲染.在 WPF 使用的 DX 只是优化等级为 9 和 DX 9 差不多的性能,微软 ...

  3. (十分钟视频教程)nodejs基础实战教程3:react服务端渲染入门篇

    视频截图如下: (具体视频见文末) 前言: 这是小猫的第三篇node教程,本篇内容是由公众号粉丝票选得出的,相信大家对这篇教程是抱有较大希望的,这篇教程由小猫和一位多年的好朋友合作完成(笔名:谷雨,博 ...

  4. DirectX学习入门笔记(一)

    原文:https://blog.csdn.net/butcher986115/article/details/50595937  什么是DirectX? DirectX是游戏制作者的API(Appli ...

  5. DirectX渲染时Clear无效的原因(造成叠影)

    最近在开发D3D程序的过程中,发现一件很奇怪的事情,就是在Render的时候,纹理总是留有"残影"(即上次Render后的帧):如上图,是一副纹理绕中心点旋转的向日葵,但是可以看到 ...

  6. 8天入门wpf(转)

    8天入门wpf—— 第一天 基础概念介绍 8天入门wpf—— 第二天 xaml详解 8天入门wpf—— 第三天 样式 8天入门wpf—— 第四天 模板 8天入门wpf—— 第五天 数据绑定 8天入门w ...

  7. WPF简单入门总结

    WPF简单总结 最近看了点关于WPF的东西,总结了点点入门的东西. XAML语法基础 1.  定义样式 <Window.Resources><!--窗体资源的定义--> < ...

  8. WPF 初识

    1.WPF 与Winform比较 1.1.WPF所有的操作都不依赖于GDI和GDI+,而是间接依赖于强大的Direct3D,这就意味着通过WPF可以做出以前WinFrom无法想象的视觉效果,包括3D效 ...

  9. WPF 使用 Direct2D1 画图 绘制基本图形

    本文来告诉大家如何在 Direct2D1 绘制基本图形,包括线段.矩形.椭圆 本文是一个系列 WPF 使用 Direct2D1 画图入门 WPF 使用 Direct2D1 画图 绘制基本图形 本文的组 ...

随机推荐

  1. 【Linux】【Shell】【text】awk

    基本用法:gawk [options] 'program' FILE ...             program: PATTERN{ACTION STATEMENTS}               ...

  2. vue cli3.0 首次加载优化

    项目经理要求做首页加载优化,打包后从十几兆优化到两兆多,记下来怕下次忘记 运行report脚本 可看到都加载了那些内容,在从dist文件中index.html 查看首次加载都加载了那些东西,如下图:然 ...

  3. 35、搜索插入位置 | 算法(leetode,附思维导图 + 全部解法)300题

    零 标题:算法(leetode,附思维导图 + 全部解法)300题之(35)搜索插入位置 一 题目描述 二 解法总览(思维导图) 三 全部解法 1 方案1 1)代码: // 方案1 "无视要 ...

  4. 《手把手教你》系列技巧篇(五十一)-java+ selenium自动化测试-字符串操作-下篇(详解教程)

    1.简介 自动化测试中进行断言的时候,我们可能经常遇到的场景.从一个字符串中找出一组数字或者其中的某些关键字,而不是将这一串字符串作为结果进行断言.这个时候就需要我们对字符串进行操作,宏哥这里介绍两种 ...

  5. 按照eslint的规则格式化代码

    1.下载eslint. 2.首选项->设置,然后搜索eslint,点击在setting.json中设置.设置内容如下: "editor.codeActionsOnSave": ...

  6. 象群游牧算法-Matlab

    1. 适应度函数: function z=chaffer(x)%chaffer函数x=(0...0) f(x)=0 x[-10,10]%%没测 n=10; s1=0; for i=1:n s1=s1+ ...

  7. 自动化集成:Jenkins管理工具详解

    前言:该系列文章,围绕持续集成:Jenkins+Docker+K8S相关组件,实现自动化管理源码编译.打包.镜像构建.部署等操作:本篇文章主要描述Jenkins基础用法. 一.Jenkins安装 1. ...

  8. LuoguP2378 因式分解II 题解

    Content 输入一个多项式 \(x^2+ax+b\)(不保证 \(a,b\neq0\)),请对这个多项式进行因式分解(形式为 \((x-x_1)(x-x_2)\),其中 \(x_1>x_2\ ...

  9. java 多线程:线程安全问题synchronized关键字解决

    背景: 多个线程同时修改一个变量时,有概率导致两次修改其中某些次被覆盖. 例如:如下案例一个变量值为3,三个线程同时对其-1,如果按顺序执行,每次减完的结果应该是2,1,0.但实际运行中有可能变为0, ...

  10. 开启ipv6支持

    CentOS6 开启ipv6模块操作在/etc/sysconfig/modules  目录下创建一个脚本,比如叫做 ipv6.modules,脚本中内容如下:#!/bin/shif [ ! -c /p ...