You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinForm下基于SharpDX搭建Direct2D环境及d2dTarget异常求助

WinForms 搭建 Direct2D 绘图环境步骤及问题修复

代码核心错误原因

  • 重复创建D3D设备:先初始化了一次D3D设备并基于它创建了D2D上下文,后续调用CreateWithSwapChain时又生成了新的D3D设备覆盖了原有变量,导致D2D上下文和后续的交换链不属于同一个设备,创建Bitmap1时触发跨设备资源访问异常
  • 像素格式不匹配:交换链后台缓冲配置的是R8G8B8A8_UNorm,创建D2D位图时用的是B8G8R8A8_UNorm,格式不兼容导致初始化失败

完整实现步骤

  1. 初始化带BGRA支持的D3D11设备,同时创建绑定到目标控件句柄的交换链
  2. 从D3D11设备查询获取DXGI设备,基于DXGI设备创建D2D设备和D2D设备上下文
  3. 从交换链获取后台缓冲表面,配置和后台缓冲格式一致的D2D位图属性,创建D2D渲染目标位图
  4. 绑定D2D渲染目标到上下文,实现绘图逻辑,每次绘制完成后调用交换链的Present方法呈现画面
  5. 处理窗口大小变更事件,重建交换链和对应渲染资源避免拉伸失真

修正后的可运行代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 19:27:01