画面キャプチャ

DirectXで画面の状態を画像ファイルに落とすのは結構簡単です。

CComPtr< IDirect3DSurface9 > pBackBuffer;
pDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer );
D3DXSaveSurfaceToFile("screen.bmp",D3DXIFF_BMP, pBackBuffer,NULL,NULL);

同じ要領で、テクスチャに落とせないかと思ったら思った以上に苦戦しました。
まず最初に気が付いたこと、理由はよくわからんがバックバッファってロックできないのね。ロックしようとしたらぬるぽが返って来ました。多分VRAM上にあるからハード的理由で出来ないんじゃないかな? まあ確かにバックバッファを直接いじられたら内部的にあまり嬉しくないかも。
というわけで、しょうがないからまずはDirectXの関数を使ってサーフェースをコピーします。

pDevice->GetRenderTargetData( pBackBuffer, pCopy );

これで新しく作ったバックバッファのコピーはちゃんとロックしていじることが出来ます。というわけで、あとは新しくテクスチャを作って両方同時にロックしてビットをぺたぺた貼り付ければ終〜了〜・・・と思ったんですけど、予想通り、欲しい形のテクスチャにはなってませんでした。
まず、テクスチャはサーフェースと違って2^nの長さの辺しかとれません。画面サイズの場合、640×480ですが、これを取り込もうと思うと1024×512のサイズのテクスチャが必要になります。これにサーフェースと同じようにビットをぺたぺた貼り付けたら640×480の部分に入ってのこりは空っぽになります。もちろんそれでも使えることは使えるのでので悪くは無いのですがやっぱり使いにくいんです。テクスチャはテクスチャらしく、のっぺりと全面に張り付いていて欲しいんです。つまり、640×480のピクセルデータを1024×512に引き伸ばした状態で入っていて欲しいんです。しかしこれを自前でやるのはクソ大変なんですよね。各ピクセルに関して被っているピクセルの割合重み付きの平均を取っていくか、若しくは、フーリエ変換してモードの情報に変えて、それを系の大きさを変えて逆変換するか・・・・・。まあ、とにかくクソ面倒くさいというわけでどうにかならんかな?と思ったらどうにかなる方法を一応見つけました。そういう面倒なことはDirectXにやってもらおうということで、サーフェースからビットマップをメモリ上に作りました。サーフェースの構造なんて殆どビットマップそのまんまなので、ヘッダだけくっつければいいだけなので比較的容易に出来ます。ビットマップならD3DXCreateTextureFromFileInMemoryExに投げればあとはDirectXの方でクソ面倒な計算をやってくれます。
で、これを実行してみたら、なんと上下反転してました。・・・・やられた、サーフェースのビット情報って上から入っているのね。ちなみにビットマップは下から入っています。というわけで、メモリ上にビットマップを作る際に上下逆にぺたぺたすることによりようやく完成。んで実行したらちゃんと思うようなテクスチャが完成していました♪しかし、予想以上に予想通り、クソ重いです。そりゃそうだ。全画面分のピクセルデータ、何回コピーしていると思っているんだ。バックバッファ→サーフェース→システムメモリ→テクスチャ。むしろ処理落ちしない環境の方が恐ろしいですね。でもまあ、実用上、画面キャプチャのテクスチャが必要になる場面なんて画面切り替えとかあまりリアルタイム性が必要な場所じゃないからいいかなぁと。

ちなみに、バックバッファからテクスチャをつくる一番簡単な方法は以下の通り。

CComPtr< IDirect3DSurface9 > pBackBuffer;
pDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer );
D3DXSaveSurfaceToFile("screen.bmp",D3DXIFF_BMP, pBackBuffer,NULL,NULL);

CComPtr< IDirect3DTexture9 > pTexture;
D3DXCreateTextureFromFile( pDevice, "screen.bmp", &pTexture );

ものすごいことをしていますが、たった5行で済みます。ただし、ほぼ間違いなく激烈に重いです。それにゲーム終了後に変なファイルが出来ています。それでもよいなら。