如何共享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
相关产品推荐
相关产品推荐

