using D3D = Vortice.Direct3D;
using D3D11 = Vortice.Direct3D11;
using DXGI = Vortice.DXGI;
using D2D = Vortice.Direct2D1;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
using static Windows.Win32.PInvoke;
using static Windows.Win32.UI.WindowsAndMessaging.PEEK_MESSAGE_REMOVE_TYPE;
using static Windows.Win32.UI.WindowsAndMessaging.WNDCLASS_STYLES;
using static Windows.Win32.UI.WindowsAndMessaging.WINDOW_STYLE;
using static Windows.Win32.UI.WindowsAndMessaging.WINDOW_EX_STYLE;
using static Windows.Win32.UI.WindowsAndMessaging.SYSTEM_METRICS_INDEX;
using static Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD;
using Vortice.Mathematics;
using AlphaMode = Vortice.DXGI.AlphaMode;
using System.Diagnostics;
SizeI clientSize = new SizeI(1000, 1000);
var title = "lindexi D2D AOT";
var windowClassName = title;
WINDOW_STYLE style = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPSIBLINGS | WS_BORDER | WS_DLGFRAME | WS_THICKFRAME | WS_GROUP | WS_TABSTOP | WS_SIZEBOX;
right = clientSize.Width,
bottom = clientSize.Height
AdjustWindowRectEx(&rect, style, false, WS_EX_APPWINDOW);
int windowWidth = rect.right - rect.left;
int windowHeight = rect.bottom - rect.top;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
x = (screenWidth - windowWidth) / 2;
y = (screenHeight - windowHeight) / 2;
var hInstance = GetModuleHandle((string) null);
fixed (char* lpszClassName = windowClassName)
PCWSTR szCursorName = new((char*) IDC_ARROW);
var wndClassEx = new WNDCLASSEXW
cbSize = (uint) Unsafe.SizeOf<WNDCLASSEXW>(),
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
lpfnWndProc = new WNDPROC(WndProc),
hInstance = (HINSTANCE) hInstance.DangerousGetHandle(),
hCursor = LoadCursor((HINSTANCE) IntPtr.Zero, szCursorName),
hbrBackground = (Windows.Win32.Graphics.Gdi.HBRUSH) IntPtr.Zero,
hIcon = (HICON) IntPtr.Zero,
lpszClassName = lpszClassName
ushort atom = RegisterClassEx(wndClassEx);
throw new InvalidOperationException($"Failed to register window class. Error: {Marshal.GetLastWin32Error()}");
var hWnd = CreateWindowEx
ShowWindow(hWnd, SW_NORMAL);
Windows.Win32.UI.WindowsAndMessaging.MSG msg;
if (GetMessage(out msg, hWnd, 0, 0) != false)
_ = TranslateMessage(&msg);
_ = DispatchMessage(&msg);
if (msg.message is WM_QUIT or WM_CLOSE or 0)
GetClientRect(hWnd, &windowRect);
clientSize = new SizeI(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
var dxgiFactory2 = DXGI.DXGI.CreateDXGIFactory1<DXGI.IDXGIFactory2>();
var hardwareAdapter = GetHardwareAdapter(dxgiFactory2)
// 这里 ToList 只是想列出所有的 IDXGIAdapter1 方便调试而已。在实际代码里,大部分都是获取第一个
.ToList().FirstOrDefault();
if (hardwareAdapter == null)
throw new InvalidOperationException("Cannot detect D3D11 adapter");
// [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)
D3D.FeatureLevel[] featureLevels = new[]
D3D.FeatureLevel.Level_11_1,
D3D.FeatureLevel.Level_11_0,
D3D.FeatureLevel.Level_10_1,
D3D.FeatureLevel.Level_10_0,
D3D.FeatureLevel.Level_9_3,
D3D.FeatureLevel.Level_9_2,
D3D.FeatureLevel.Level_9_1,
DXGI.IDXGIAdapter1 adapter = hardwareAdapter;
D3D11.DeviceCreationFlags creationFlags = D3D11.DeviceCreationFlags.BgraSupport;
var result = D3D11.D3D11.D3D11CreateDevice
out D3D11.ID3D11Device d3D11Device, out D3D.FeatureLevel featureLevel,
out D3D11.ID3D11DeviceContext d3D11DeviceContext
// 如果失败了,那就不指定显卡,走 WARP 的方式
// http://go.microsoft.com/fwlink/?LinkId=286690
result = D3D11.D3D11.D3D11CreateDevice(
out d3D11Device, out featureLevel, out d3D11DeviceContext);
// 大部分情况下,用的是 ID3D11Device1 和 ID3D11DeviceContext1 类型
// 从 ID3D11Device 转换为 ID3D11Device1 类型
var d3D11Device1 = d3D11Device.QueryInterface<D3D11.ID3D11Device1>();
var d3D11DeviceContext1 = d3D11DeviceContext.QueryInterface<D3D11.ID3D11DeviceContext1>();
// 转换完成,可以减少对 ID3D11Device1 的引用计数
// 调用 Dispose 不会释放掉刚才申请的 D3D 资源,只是减少引用计数
d3D11DeviceContext.Dispose();
DXGI.Format colorFormat = DXGI.Format.B8G8R8A8_UNorm;
const int FrameCount = 2;
DXGI.SwapChainDescription1 swapChainDescription = new()
Width = clientSize.Width,
Height = clientSize.Height,
BufferCount = FrameCount,
BufferUsage = DXGI.Usage.RenderTargetOutput,
SampleDescription = DXGI.SampleDescription.Default,
Scaling = DXGI.Scaling.Stretch,
SwapEffect = DXGI.SwapEffect.FlipDiscard,
AlphaMode = AlphaMode.Ignore,
DXGI.SwapChainFullscreenDescription fullscreenDescription = new DXGI.SwapChainFullscreenDescription
DXGI.IDXGISwapChain1 swapChain =
dxgiFactory2.CreateSwapChainForHwnd(d3D11Device1, hWnd, swapChainDescription, fullscreenDescription);
dxgiFactory2.MakeWindowAssociation(hWnd, DXGI.WindowAssociationFlags.IgnoreAltEnter);
D3D11.ID3D11Texture2D backBufferTexture = swapChain.GetBuffer<D3D11.ID3D11Texture2D>(0);
// 获取到 dxgi 的平面,这个平面就约等于窗口渲染内容
DXGI.IDXGISurface dxgiSurface = backBufferTexture.QueryInterface<DXGI.IDXGISurface>();
D2D.ID2D1Factory1 d2DFactory = D2D.D2D1.D2D1CreateFactory<D2D.ID2D1Factory1>();
//var renderTargetProperties = new D2D.RenderTargetProperties(PixelFormat.Premultiplied);
//// 在窗口的 dxgi 的平面上创建 D2D 的画布,如此即可让 D2D 绘制到窗口上
//D2D.ID2D1RenderTarget d2D1RenderTarget =
// d2DFactory.CreateDxgiSurfaceRenderTarget(dxgiSurface, renderTargetProperties);
//var renderTarget = d2D1RenderTarget;
// 创建 D2D 设备,通过设置 ID2D1DeviceContext 的 Target 输出为 dxgiSurface 从而让 ID2D1DeviceContext 渲染内容渲染到窗口上
// 如 https://learn.microsoft.com/en-us/windows/win32/direct2d/images/devicecontextdiagram.png 图
// 获取 DXGI 设备,用来创建 D2D 设备
DXGI.IDXGIDevice dxgiDevice = d3D11Device1.QueryInterface<DXGI.IDXGIDevice>();
D2D.ID2D1Device d2dDevice = d2DFactory.CreateDevice(dxgiDevice);
D2D.ID2D1DeviceContext d2dDeviceContext = d2dDevice.CreateDeviceContext();
D2D.ID2D1Bitmap1 d2dBitmap = d2dDeviceContext.CreateBitmapFromDxgiSurface(dxgiSurface);
d2dDeviceContext.Target = d2dBitmap;
var renderTarget = d2dDeviceContext;
var stopwatch = Stopwatch.StartNew();
Task.Factory.StartNew(() =>
var ellipseInfoList = new List<DrawingInfo>();
for (int i = 0; i < 100; i++)
var color = new Color4((byte) Random.Shared.Next(255), (byte) Random.Shared.Next(255), (byte) Random.Shared.Next(255));
D2D.ID2D1SolidColorBrush brush = renderTarget.CreateSolidColorBrush(color);
ellipseInfoList.Add(new DrawingInfo(new System.Numerics.Vector2(Random.Shared.Next(clientSize.Width), Random.Shared.Next(clientSize.Height)), new Size(Random.Shared.Next(10, 100)), brush));
renderTarget.BeginDraw();
renderTarget.Clear(new Color4(0xFF, 0xFF, 0xFF));
for (var i = 0; i < ellipseInfoList.Count; i++)
var drawingInfo = ellipseInfoList[i];
var vector2 = drawingInfo.Offset;
vector2.X += Random.Shared.Next(200) - 100;
vector2.Y += Random.Shared.Next(200) - 100;
while (vector2.X < 100 || vector2.X > clientSize.Width - 100)
vector2.X = Random.Shared.Next(clientSize.Width);
while (vector2.Y < 100 || vector2.Y > clientSize.Height - 100)
vector2.Y = Random.Shared.Next(clientSize.Height);
ellipseInfoList[i] = drawingInfo with { Offset = vector2 };
renderTarget.FillEllipse(new D2D.Ellipse(vector2, drawingInfo.Size.Width, drawingInfo.Size.Height), drawingInfo.Brush);
swapChain.Present(1, DXGI.PresentFlags.None);
d3D11DeviceContext1.Flush();
if (stopwatch.Elapsed >= TimeSpan.FromSeconds(1))
Console.WriteLine($"FPS: {count / stopwatch.Elapsed.TotalSeconds}");
}, TaskCreationOptions.LongRunning);
static IEnumerable<DXGI.IDXGIAdapter1> GetHardwareAdapter(DXGI.IDXGIFactory2 factory)
DXGI.IDXGIFactory6? factory6 = factory.QueryInterfaceOrNull<DXGI.IDXGIFactory6>();
for (int adapterIndex = 0;
factory6.EnumAdapterByGpuPreference(adapterIndex, DXGI.GpuPreference.HighPerformance,
out DXGI.IDXGIAdapter1? adapter).Success;
DXGI.AdapterDescription1 desc = adapter.Description1;
if ((desc.Flags & DXGI.AdapterFlags.Software) != DXGI.AdapterFlags.None)
// Don't select the Basic Render Driver adapter.
Console.WriteLine($"枚举到 {adapter.Description1.Description} 显卡");
for (int adapterIndex = 0;
factory.EnumAdapters1(adapterIndex, out DXGI.IDXGIAdapter1? adapter).Success;
DXGI.AdapterDescription1 desc = adapter.Description1;
if ((desc.Flags & DXGI.AdapterFlags.Software) != DXGI.AdapterFlags.None)
// Don't select the Basic Render Driver adapter.
Console.WriteLine($"枚举到 {adapter.Description1.Description} 显卡");
static LRESULT WndProc(HWND hWnd, uint message, WPARAM wParam, LPARAM lParam)
return DefWindowProc(hWnd, message, wParam, lParam);
readonly record struct DrawingInfo(System.Numerics.Vector2 Offset, Size Size, D2D.ID2D1SolidColorBrush Brush);