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

如何共享WriteableBitmap后台缓冲区,配合Direct2D直接绘制减少拷贝?

问题

我在WPF窗口中使用WriteableBitmap作为快速渲染并显示内容的方式,后续还会复用同一渲染管线将内容导出为媒体。我可以绘制图像、基础图形,还能执行调整大小、裁剪、翻转、旋转、调整透明度等常见图像字节操作(此部分与当前问题无关)。

我的渲染管线会获取WriteableBitmap的后台缓冲区(nint/IntPtr)及其部分参数(尺寸、缩放比例、偏移量等),并通过SharpDX绘制文本,代码如下:

public override void RenderAt(nint canvas, int canvasStride, int canvasWidth, int canvasHeight, 
    double horizontalScale, double verticalScale, int leftOffset, int topOffset, 
    int rightOffset, int bottomOffset) 
{
    using (var factory = new SharpDX.Direct2D1.Factory(FactoryType.MultiThreaded))
    using (var writeFactory = new SharpDX.DirectWrite.Factory(SharpDX.DirectWrite.FactoryType.Shared))
    using (var textFormat = new TextFormat(writeFactory, "Segoe UI", 20))
    using (var bitmap = new SharpDX.WIC.Bitmap(new ImagingFactory(), canvasWidth, canvasHeight, SharpDX.WIC.PixelFormat.Format32bppPBGRA, SharpDX.WIC.BitmapCreateCacheOption.CacheOnLoad))
    using (var renderTarget = new WicRenderTarget(factory, bitmap, new RenderTargetProperties()))
    {
        //Create a SharpDX Bitmap as the backbuffer.
        var backBufferBitmap = new SharpDX.Direct2D1.Bitmap(renderTarget, new Size2(canvasWidth, canvasHeight), new SharpDX.Direct2D1.BitmapProperties(renderTarget.PixelFormat));
        backBufferBitmap.CopyFromMemory(canvas, canvasStride);

        renderTarget.BeginDraw();
        renderTarget.Transform = Matrix3x2.Scaling((float)horizontalScale, (float)verticalScale) * Matrix3x2.Translation(leftOffset, topOffset);
        
        //Get the current canvas content, to draw the text later.
        renderTarget.DrawBitmap(backBufferBitmap, 1f, BitmapInterpolationMode.Linear);

        var text = "Example";

        //Backdrop of the text.
        using (var brush = new SharpDX.Direct2D1.SolidColorBrush(renderTarget, new RawColor4(0,0,0,1)))
            renderTarget.FillRectangle(new RawRectangleF(0, 0, canvasWidth, canvasHeight / 4), brush);

        //Text.
        using (var brush = new SharpDX.Direct2D1.SolidColorBrush(renderTarget, new RawColor4(1,1,1,1)))
            renderTarget.DrawText(text, textFormat, new RectangleF(0, 0, Width, Height), brush);

        renderTarget.EndDraw();

        //Copy back the rendered result.
        bitmap.CopyPixels(new RawBox(0, 0, canvasWidth, canvasHeight), canvasStride, new DataPointer(canvas, canvasStride * canvasHeight));
    }
}

目前该流程需要执行两次拷贝操作,我想了解是否可以直接在此后台缓冲区中进行绘制,从而减少拷贝次数。

解决方案

完全可以直接在WriteableBitmap的后台缓冲区上绘制,彻底消除两次拷贝的开销。核心是跳过WIC Bitmap中间层,直接基于原始内存缓冲区创建Direct2D的渲染目标,让绘制操作直接作用在目标内存上。

核心优化点

  • 直接关联原始缓冲区:利用SharpDX.Direct2D1.Bitmap的构造函数,传入WriteableBitmap的后台缓冲区指针、 stride、尺寸和匹配的像素格式,创建与原始内存绑定的Direct2D Bitmap。
  • 以该Bitmap为渲染目标:创建WicRenderTarget时直接使用这个Bitmap,所有绘制操作会直接写入原始缓冲区,无需后续拷贝。
  • 省略冗余拷贝步骤:去掉原代码中CopyFromMemory和CopyPixels的操作,因为渲染目标本身就指向WriteableBitmap的后台内存。

优化后的代码示例

public override void RenderAt(nint canvas, int canvasStride, int canvasWidth, int canvasHeight, 
    double horizontalScale, double verticalScale, int leftOffset, int topOffset, 
    int rightOffset, int bottomOffset) 
{
    using var factory = new SharpDX.Direct2D1.Factory(FactoryType.MultiThreaded);
    using var writeFactory = new SharpDX.DirectWrite.Factory(SharpDX.DirectWrite.FactoryType.Shared);
    using var textFormat = new TextFormat(writeFactory, "Segoe UI", 20);

    // 匹配WriteableBitmap的像素格式:32bppPBGRA对应DXGI.B8G8R8A8_UNorm,预乘alpha
    var pixelFormat = new SharpDX.Direct2D1.PixelFormat(
        SharpDX.DXGI.Format.B8G8R8A8_UNorm, 
        SharpDX.Direct2D1.AlphaMode.Premultiplied);

    // 直接从WriteableBitmap后台缓冲区创建Direct2D Bitmap
    using var d2dBitmap = new SharpDX.Direct2D1.Bitmap(
        factory,
        new Size2(canvasWidth, canvasHeight),
        new DataPointer(canvas, canvasStride * canvasHeight),
        canvasStride,
        new SharpDX.Direct2D1.BitmapProperties(pixelFormat));

    // 创建以该Bitmap为渲染目标的RenderTarget
    using var renderTarget = new SharpDX.Direct2D1.WicRenderTarget(
        factory,
        d2dBitmap,
        new RenderTargetProperties(pixelFormat));

    renderTarget.BeginDraw();
    // 设置变换矩阵
    renderTarget.Transform = Matrix3x2.Scaling((float)horizontalScale, (float)verticalScale) 
        * Matrix3x2.Translation(leftOffset, topOffset);

    // 绘制文本背景
    using var bgBrush = new SharpDX.Direct2D1.SolidColorBrush(renderTarget, new RawColor4(0,0,0,1));
    renderTarget.FillRectangle(new RawRectangleF(0, 0, canvasWidth, canvasHeight / 4), bgBrush);

    // 绘制文本
    using var textBrush = new SharpDX.Direct2D1.SolidColorBrush(renderTarget, new RawColor4(1,1,1,1));
    renderTarget.DrawText("Example", textFormat, new RectangleF(10, 10, canvasWidth - 20, canvasHeight / 4 - 20), textBrush);

    renderTarget.EndDraw();

    // 无需拷贝:所有绘制操作已直接写入WriteableBitmap的后台缓冲区
}

关键注意事项

  • 像素格式必须匹配:WriteableBitmap默认使用Format32bppPBGRA格式,对应Direct2D的DXGI.Format.B8G8R8A8_UNorm像素格式,且需设置AlphaMode.Premultiplied,否则会出现颜色异常或绘制失败。
  • 复用全局资源:如果渲染管线会被频繁调用,建议把Direct2D.Factory、DirectWrite.Factory等对象提升到类级别复用,避免每次创建销毁带来的性能损耗。
  • 保留原有内容:如果需要保留WriteableBitmap上已有的内容,不需要额外绘制,因为创建的Direct2D Bitmap已经包含了原始缓冲区的数据,直接在上面叠加绘制新内容即可。

内容的提问来源于stack exchange,提问作者Nicke Manarin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:17:06