WinForm下基于SharpDX搭建Direct2D环境及d2dTarget异常求助
WinForms 搭建 Direct2D 绘图环境步骤及问题修复
代码核心错误原因
- 重复创建D3D设备:先初始化了一次D3D设备并基于它创建了D2D上下文,后续调用
CreateWithSwapChain时又生成了新的D3D设备覆盖了原有变量,导致D2D上下文和后续的交换链不属于同一个设备,创建Bitmap1时触发跨设备资源访问异常 - 像素格式不匹配:交换链后台缓冲配置的是
R8G8B8A8_UNorm,创建D2D位图时用的是B8G8R8A8_UNorm,格式不兼容导致初始化失败
完整实现步骤
- 初始化带BGRA支持的D3D11设备,同时创建绑定到目标控件句柄的交换链
- 从D3D11设备查询获取DXGI设备,基于DXGI设备创建D2D设备和D2D设备上下文
- 从交换链获取后台缓冲表面,配置和后台缓冲格式一致的D2D位图属性,创建D2D渲染目标位图
- 绑定D2D渲染目标到上下文,实现绘图逻辑,每次绘制完成后调用交换链的Present方法呈现画面
- 处理窗口大小变更事件,重建交换链和对应渲染资源避免拉伸失真
修正后的可运行代码
using System.Windows.Forms; using SharpDX; using SharpDX.DXGI; using SharpDX.Direct3D; using SharpDX.Direct3D11; using SharpDX.Direct2D1; using Device = SharpDX.Direct3D11.Device; using Factory2 = SharpDX.DXGI.Factory2; namespace SharpDX_Tester { public partial class Form1 : Form { Device d3dDevice; DeviceContext1 d3dContext; SharpDX.Direct2D1.Device d2dDevice; SharpDX.Direct2D1.DeviceContext d2dContext; SwapChain swapChain; Bitmap1 d2dTarget; public Form1() { InitializeComponent(); // 禁止窗体双缓冲避免和DirectX渲染冲突 DoubleBuffered = false; #region 步骤1:创建D3D设备和交换链 var scd = new SwapChainDescription() { BufferCount = 2, // 双缓冲避免画面撕裂 Flags = SwapChainFlags.None, IsWindowed = true, ModeDescription = new ModeDescription(panel1.ClientSize.Width, panel1.ClientSize.Height, new Rational(60, 1), Format.B8G8R8A8_UNorm), // 统一用BGRA格式 OutputHandle = panel1.Handle, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput | Usage.Shared }; // 一次性创建带BGRA支持的设备和交换链,不要重复创建设备 Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.BgraSupport | DeviceCreationFlags.Debug, scd, out d3dDevice, out swapChain); // 禁用DXGI对窗体消息的接管,避免窗口操作时异常 using (var factory = swapChain.GetParent<Factory2>()) { factory.MakeWindowAssociation(panel1.Handle, WindowAssociationFlags.IgnoreAll); } #endregion #region 步骤2:创建D2D设备和上下文 d3dContext = d3dDevice.ImmediateContext.QueryInterface<DeviceContext1>(); var dxgiDevice = d3dDevice.QueryInterface<SharpDX.DXGI.Device2>(); d2dDevice = new SharpDX.Direct2D1.Device(dxgiDevice); d2dContext = new SharpDX.Direct2D1.DeviceContext(d2dDevice, DeviceContextOptions.None); #endregion #region 步骤3:创建D2D渲染目标 var backBuffer = swapChain.GetBackBuffer<Surface>(0); var bitmapProps = new BitmapProperties1( new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied), 96, 96, // 标准DPI,可根据系统DPI调整 BitmapOptions.Target | BitmapOptions.CannotDraw); d2dTarget = new Bitmap1(d2dContext, backBuffer, bitmapProps); d2dContext.Target = d2dTarget; #endregion // 测试绘图 Render(); } private void Render() { d2dContext.BeginDraw(); d2dContext.Clear(Color.CornflowerBlue); // 绘制一个红色矩形测试 using(var brush = new SolidColorBrush(d2dContext, Color.Red)) { d2dContext.DrawRectangle(new RectangleF(10, 10, 200, 100), brush, 5); } d2dContext.EndDraw(); // 呈现画面,垂直同步 swapChain.Present(1, PresentFlags.None); } // 重写Dispose方法释放所有SharpDX资源避免内存泄漏 protected override void Dispose(bool disposing) { if (disposing) { d2dTarget?.Dispose(); d2dContext?.Dispose(); d2dDevice?.Dispose(); swapChain?.Dispose(); d3dContext?.Dispose(); d3dDevice?.Dispose(); } base.Dispose(disposing); } } }
注意事项
- 调试时如果出现Debug层提示,请先检查资源是否正确释放,不需要的临时资源要及时Dispose
- 窗口大小变化时需要先释放当前的d2dTarget、交换链,再用新的窗口尺寸重建交换链和对应的渲染目标
- WPF实现逻辑类似,不过需要使用D3DImage作为DirectX渲染内容和WPF渲染管线的桥梁,不需要绑定窗口句柄,改为绑定到D3DImage的后台缓冲即可
内容的提问来源于stack exchange,提问作者Kuumba
相关产品推荐
相关产品推荐

