XNA 2.0でのGraphicsDevice仮想化
消えたデバイスロスト
XNA1.0で作ったソースコードをXNA2.0に移植していて最初に気づくのは以下の警告メッセージだと思います。
warning CS0672: Member 'MyGame.Game1.LoadGraphicsContent(bool)' overrides obsolete member 'Microsoft.Xna.Framework.Game.LoadGraphicsContent(bool)'.
結論から書くと、XNA2.0ではグラフィクスリソースの扱いが簡単になり、Windows上で発生するデバイスロスト等の問題を気にする必要が殆ど無なり、新しく追加されたGame.LoadContent内でグラフィクスリソースを簡単に生成できるようになりました。LoadGraphicsContentメソッドもXNA 1.0との互換性を保つために一度だけ呼ばれます。
Texture2D、VertexBuffer、RenderTargetなどの全てのグラフィクスリソースはXNA2.0上では一度生成したら、そのインスタンスは常に有効な状態になります。もちろん、デバイスロストやデバイスリセットが発生するケース、例えばウィンドウのサイズ変更、フルスクリーンへの切り替え、マルチモニタ環境でウィンドウをモニタ間で移動したときでも最初に作ったインスタンスは問題なく使えます。
ただしDirectX 9のドライバモデルの制限上、RenderTargetやDynamicVertexBufferといった動的に内容を書き換えるグラフィクスリソースの中身はデバイスロスト時に破棄されてしまいます。通常、動的に生成されるグラフィクスリソースの多くは毎フレーム毎に生成されるので、その内容が破棄されても次のフレームで生成されるので特別な処理をする必要がありません。フレーム間でその内容を保持しなければいけないグラフィクスリソース、例えばHDRレンダリングで使われるトーンマッピング用の明度テクスチャ、前のフレームの内容を重ねて描くことによるモーションブラーなどの手法を使うときにのみデバイスロスト時に正しい初期値にする必要があります。
まとめると:
- グラフィクスリソースはGame.LoadContent内で生成する
- Game.LoadContentが呼び出されるのはプログラム開始時のみ
- DrawableGameComponent.LoadContentはコンポーネントの初期化時のみに呼ばれる
- グラフィクスリソースのインスタンスは作り直す必要がなくなった
- フレーム間でその内容を保持するグラフィクスリソースはデバイスロスト時にその内容が破棄される
- インスタンス自体はデバイスロスト時でも有効なまま
- GraphicsDevice.DeviceResetイベント時にSetData<T>等を使って初期値に変更するのみ
と、いった感じになります。
XNA 1.0での問題点
XNA1.0ではGameクラスやDrawableComponentクラスにはLoadGraphicsContentとUnloadGraphicsContentメソッドがあり、デバイスリセット時にはUnloadGraphicsContent(flase)、LoadGraphicsContent(false)の順に呼ばれ、デバイスの再構築時にはUnloadGraphicsContent(true)、LoadGraphicsContent(true)の順に呼び出され、その関数の中でグラフィクスリソースの開放と生成をしなければいけませんでした。
ここでの問題はTexture2DやVertexBufferといったインスタンスを生成する必要があるので、ゲーム側のでこれらのオブジェクトを参照している場合、それらの参照を更新する必要がありました。これではゲームが複雑になるに比例してLoadGraphicsContentメソッド内のコードも複雑になるし、予期せぬエラーの原因になってしまいます。
XNA 2.0では、この問題を解決することでグラフィクスリソースの管理が格段に簡単になりました。
どうやってるの?
Texture2DやVertexBufferといったマネージクラスは内部でネイティブのリソースを管理しています。XNA 1.0ではデバイスロスト時にマネージクラス自体を破棄する必要がありましたが、XNA 2.0ではマネージクラスは内部でネイティブリソースに対して処理をするようになっています。マネージクラスはテクスチャのサイズやフォーマットといったネイティブリソースの生成情報を保持しているので、ネイティブリソースの再構築が必要なときでも自動的にネイティブリソースを生成しなおすことができます。
また、デバイスの再構築が必要なとき、例えばウィンドウを他のモニタへ移動させた場合、マネージクラスはD3DPOOL_MANAGEDでつくられたリソースの内容を一旦メインメモリに保持してから、ネイティブリソースの再構築、リソースの内容の復元を行うようになっています。
Xbox 360では?
Xbox 360上ではデバイスロスト自体発生しないので、上記のようなGraphicsDeviceの仮想化処理は最初からする必要がありません。逆に言えば、XNA 2.0ではXbox 360のようにデバイスロストのない開発しやすい環境をWindos上に持ってきたとも言えます。
参考URL:
Comments
- Anonymous
January 17, 2008
以前から、XNAを.NetのControl内で使いたいという要望を何度か聞くことがありました。本当はXNA 2.0で導入される予定でしたがテスト期間が間に合わなかったために見送りとなりました。ですが 以前書いたように