Starling2.0のDevice contextをロストした際の挙動を理解する


Starling2.0からはDeviceContextをロストした(それまでにGPU描画した内容を失い、描画処理を続けられなくなった)後に自動的に復帰処理が働くのがデフォルトの動作になりました。(Starling.handleLostContextプロパティはもはや存在しません。)余計な手間が減って便利になりましたが、それでも手動で復帰処理を書かなくてはいけないケースも存在しますので、この周辺でどんな処理がなされているのか、手動で復帰処理を書かなくてはいけない場合はいつでどのようにすればいいのか、といった事をまとめてみます。

Contextをロストした際に失われるもの

Contextをロストした際に失われるのは、GPUにアップロードしたテクスチャ情報です。他の描画に必要なパラメータなどは基本的に毎フレーム画面全体を描画し直す際に、再度GPUに送信されるので、特に気にせずとも良いのです。テクスチャはいろいろな方法で作ることができますが、Bitmap(画像ディスプレイオブジェクト)もしくはBitmapData(画像データ)から作ることを基本に考えます。

sample.as
var texture:Texture = Texture.fromBitmapData(bitmapData);

Bitmap系以外には、Embedされた(画像関連の)クラスなどからテクスチャを作ることができますが、それぞれが結局は画像に変換されてテクスチャが作成されます。

参考:リファレンスマニュアル Textureクラス
http://doc.starling-framework.org/current/starling/textures/Texture.html

復帰処理の詳細

Device Contextをロストして、再度Contextを得た際に各textureインスタンスのtexture.root.onRestore関数が呼びだされます。このonRestore関数はユーザ側で設定が可能ですが、Texture.fromBitmapDataなどでTextureクラスの静的メソッドでテクスチャを生成する際はデフォルトですでに設定されており、テクスチャの元となった画像を再度GPUにアップロードする処理が書かれています。ContextをロストしてもネイティブのBitmapDataは消失しません。

sample.as
texture.root.onRestore = function():void
{
    texture.root.uploadBitmapData(bitmapData);
};

注意しなくてはいけないのは、このBitmapDataをTexture生成後に外部で破棄してしまっていると、再アップロード処理が行えなくなってしまうことです。メモリに無駄が出てしまいますがこのデフォルトの挙動を有効とするために、(かなり大きなテクスチャを扱っているような特殊な場合をのぞいて)BitmapDataは破棄せず保持しておくと良いでしょう。Embedされた画像クラスを元にテクスチャを作る場合は、画像クラスを再度インスタンス化すれば良いです。

Videoテクスチャについて

カメラ、ビデオ、ストリーム映像など動画系のテクスチャ(Videoテクスチャ)を扱う場合も、自動で再アップロード処理が働きます。そもそもテクスチャ内容が刻々と変わっていくのがVideoテクスチャなので、再アップロードというよりもビデをテクスチャに再接続する、という感じですね。特にStarling利用ユーザ側でonRestore処理を書き換える必要はなささそうです。なお、Videoテクスチャの場合は、texture.root.onRestoreではなくtexture.onRestoreに復帰処理が書かれています。

Renderテクスチャについて

このテクスチャは描画内容の自動復帰処理が行われません。動的に内容を描画していくテクスチャで全体の元となるBitmapDataは存在しないため当然なのですが、お絵かきソフトなどを作っている場合は描画内容を復活させたい場合もあるかもしれません。その場合は、ユーザのお絵かき手順を記録しておき、Contextロスト時にはその記録を元に描画しなおしてやるか、Stage.drawToBitmapDataを使って、適時BitmapData側にデータをバックアップしておくと良いかもしれません。

外部から画像を再ロードする

BitmapDataを破棄してしまっている場合はどうすれば良いでしょうか。その画像データが外部から動的に読み込まれている場合であればonRestore関数の中で、Loaderクラスなどを使ってその画像を読み込み直し、texture.root.uploadBitmapDataしてやればOKです。

AssetManagerに任せる

後者の方法(テクスチャ生成時にBitmapDataは破棄してしまい、Contextロスト時には外部から画像を読み込みしてテクスチャを再生成する事を自動で行ってくれるのがAssetManagerです。AssetManagerに任せておけば通常Contextロスト時の事をケアする必要はありませんが、AssetManagerの内部処理はなかなかにトリッキーです。状況によってよくわからない挙動を取る事もありますので、場合によっては自身でContextロスト時の処理を書く事も必要になるかと思います。

Contextロスト時の処理を考慮する必要があるのはいつか

雑に箇条書きしてみます。他にもあるかと思いますが、参考としてください。

  • Texture生成に使ったBitmapDataを変更または破棄したとき
  • AssetManagerを通さず外部から読み込んだ画像を元にTextureを生成したとき
  • RenderTextureを使っているとき

なお、DeviceContextをロストする可能性があるのはWindowsとAndroidだけはなく、その他どの環境であってもまれに再現するようです。

関連イベントなど

Contextロスト時はStarlingインスタンスからEvent.CONTEXT3D_CREATEが発行されます。(Starlingの初期化時も同じイベントが発行されるので注意。)また、AssetManagerからは全てのテクスチャの再生成が終わった時点で、Event.TEXTURES_RESTOREDイベントが発行されます。これらのイベントを監視する事で、テクスチャの再生成待ちの間はゲームをPauseするなどの対応が取れそうです。

おわりに

記事にするにあたって詳細を調べなおしたら細かいところの認識間違いがそこそこありました。きちんと発表する事で自分の理解も高まるので良い事です。ではでは。