walterlv / BlogComments

3 stars 0 forks source link

post/map-directx-surface-to-bitmap #72

Open utterances-bot opened 3 years ago

utterances-bot commented 3 years ago

将 Direct3D11 在 GPU 中的纹理(Texture2D)导出到内存(Map)或导出成图片文件 - walterlv

将 Direct3D11 在 GPU 中的纹理(Texture2D)导出到内存(Map)或导出成图片文件

https://blog.walterlv.com/post/map-directx-surface-to-bitmap.html

h82258652 commented 3 years ago

这几天在玩这块,碰上保存 Texture2D 到硬盘的问题,Google 搜了下刚好搜到吕大的 blog。 也看到了 SharpDX 的例子代码 整合了一下,用了吕大的思路,但是用了 SharpDX 里的 CopyMemory,这样应该会快一些。

public static void MapTexture2DToFile(SharpDX.Direct3D11.Texture2D texture, string filename)
{
    var device = texture.Device;
    var originDesc = texture.Description;

    var desc = new Texture2DDescription
    {
        CpuAccessFlags = CpuAccessFlags.Read,
        BindFlags = BindFlags.None,
        Usage = ResourceUsage.Staging,
        Width = originDesc.Width,
        Height = originDesc.Height,
        Format = originDesc.Format,
        MipLevels = 1,
        ArraySize = 1,
        SampleDescription =
        {
            Count = 1,
            Quality = 0
        },
        OptionFlags = ResourceOptionFlags.Shared
    };

    var texture2D = new Texture2D(device, desc);

    device.ImmediateContext.CopyResource(texture, texture2D);

    var mapSource = device.ImmediateContext.MapSubresource(texture2D, 0, MapMode.Read, MapFlags.None);

    var bitmap = new System.Drawing.Bitmap(desc.Width, desc.Height);
    var boundsRect = new System.Drawing.Rectangle(0, 0, desc.Width, desc.Height);

    var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
    var sourcePtr = mapSource.DataPointer;
    var destPtr = mapDest.Scan0;

    for (var y = 0; y < desc.Height; y++)
    {
        Utilities.CopyMemory(destPtr, sourcePtr, desc.Width * 4);

        sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
        destPtr = IntPtr.Add(destPtr, mapDest.Stride);
    }

    bitmap.UnlockBits(mapDest);
    device.ImmediateContext.UnmapSubresource(texture, 0);

    bitmap.Save(filename);
}

说起来,话说有没有办法将 Texture2D 转为 WPF 的 ImageSource,现在我是 Texture2D -> Gdi Bitmap -> MemoryStream -> WPF BitmapImage,不知道有没有更好的做法。

walterlv commented 3 years ago

@h82258652 Texture2D -> byte[] -> MemoryStream -> WPF BitmapImage,这可以比你说的省两份内存空间(你的占 3 份,我的占 1 份)。

因为 Texture2D 到你没写的字节数组有一次拷贝,Gdi Bitmap 到 MemoryStream 有一次拷贝。 而我提到的 byte[] 到 Memory Stream 没有。

sylarfan commented 3 years ago

拷贝到bitmap可以使用lock+unlock得到backbuffer,然后直接memcpy就行吧