なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

DirectX のテクスチャーを Bitmap として取り出したい

いつもは Unity みたいな高レイヤーですが、今回は比較的低レイヤーのお話です。
基本的に、最近の場合は DirectX を直に触るケースは少なくなってて、例えば Unity や Unreal Engine 等のような、ゲームエンジンを触ることがほとんどだと思います。
ただ、細かい処理をしようとしたり、 Windows の内部をごにょごにょしたい場合は、やはり DirectX の知識が必要になります。

ということで、今回は DirectX の Texture2D を Bitmap として取り出す方法です。

まず、 Texture2D を作成します。
作成する Texture2D には、必ず CPUAccessFlags には Read を、 Usage には Staging を指定する必要があります。

var description = new Texture2DDescription
{
    ArraySize = 1,
    BindFlags = BindFlags.None,
    CPUAccessFlags = CpuAccessFlags.Read, // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_cpu_access_flag
    Format = Format.B8G8R8A8_UNorm,
    Height = 1080,
    MipLevels = 1,
    SampleDescription = new SampleDescription(1, 0),
    Usage = ResourceUsage.Staging, // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_usage
    Width = 1920,
};

var texture = device.CreateTexture2D(description);

作成したら、適当なテクスチャーをコピーしてきます。

device.ImmediateContext.CopyResource(texture, surface);

コピーしたら、 GPU 上にあるデータを、 CPU 上にコピーします。

var box = device.ImmediateContext.Map(texture, 0);

最後に、コピーしてきたデータを Bitmap に投げつけるだけです。

using var bitmap = new Bitmap(description.Width, description.Height, PixelFormat.Format32bppArgb);
var dest = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
var copyBytes = bitmap.Width * 4;

for (var y = 0; y < bitmap.Height; y++)
    MemoryHelpers.CopyMemory(dest.Scan0 + y * dest.Stride, box.DataPointer + y * box.RowPitch, copyBytes);

bitmap.UnlockBits(dest);
device.ImmediateContext.Unmap(texture, 0);

あとはこの bitmap を煮るなり焼くなりすれば良いでしょう。
ということで、メモでした。