ゆとりーなの日記

日記的な事を書いて行くと思はれる

月例の時間と場所誰か教えて!

KDVの月例です。明日だということしか分かっていません。分からなかったら家でひきこもってます。まあメーリス登録してないので分からないのは残当なんですが。

ゲームライブラリでの画像のキャッシュってどんな形がいいのだろうか

流石に上の内容だけなのは少ない気がするので、今考えていることをちょっと書いておきますか。内容は小見出しの通りです。用語の使い方が間違っているかもしれませんが適宜脳内で変換してくれるとありがたいです。

なんでキャッシュしたいのか

これは私が勝手に思ったことです。例えば同じ画像の敵を大量に生産するとき、以下の様なクラスが大量生成したくなるわけです。

class Enemy {
public:
  Enemy() : texture_(nullptr) {
    D3DXCreateTextureFromFile(device, "enemy.png", &texture_);
  }
private:
  IDirect3DTexture9 *texture_;
};

VC++2010使うようになったので早速nullptr使ってます。それは置いといて。コンストラクタ内でテクスチャを読み込むわけです。このままだと敵を生成するために同じ画像のテクスチャが量産されてしまいます。これではメモリの無駄です。まあこうすれば解決すると言えばするんですが。

class Enemy {
public:
  Enemy() {}
  static void createTexture() {
    D3DXCreateTextureFromFile(device, "enemy.png", &texture_);
  }
private:
  static IDirect3DTexture9 *texture_;
};

ゲームの初期化時にcreateTexture()を呼ぶわけです。こうすれば同じ画像のテクスチャは一枚だけです。しかしこの方法だと、画像の初期化をクラス外で責任持たなくてはいけなくなりますし、継承したクラス毎に画像を分けたいという時等に不便です。やっぱり最初の形式の方が直観的だし色々融通が効くと思うんです。

キャッシュ自体はそんなに難しくない(註:STLに頼ればの話)

画像キャッシュの有効性が分かった所で次に行きます。上の方式で同じ画像のテクスチャが量産されない方法はまあ簡単です。連想配列を使えばいいんです。以前この日記でも適当に作ったのを載せたと思いますが、簡単に説明を。先ず画像ファイル名をキーとして、テクスチャのポインタを登録していきます。そしてD3DXCreateTextureFromFileの代わりに連想配列からファイル名をキーとしたテクスチャが見つかればそれを返し、見つからなければD3DXCreateTextureFromFileで新しく作るという関数を呼べばいいのです。

解放タイミングはいつがいいのか

で、ここからが今日の本題。テクスチャの解放タイミングをいつにするかということです。先ず最初に思いついたのが参照カウント方式。参照カウントが0になった時にテクスチャを解放するというものです。実装としては、連想配列にはweek_ptrをいれて、クラスに渡すのはshared_ptrにする。そうすればshared_ptrを持ったクラスが全滅したら参照カウンタが0になるのでデストラクタ内で連想配列から自身を引っこ抜くという方法を考えました。スマートポインタにいれる型はテクスチャのポインタと連想配列の自信を指すイテレータを持たせたものとするわけです。最初はこれでいいなと思っていたんです。メモリ効率も良いですし。
しかしふと思いました。ゲーム中に敵が全滅してからすぐに新しい同じ敵が出ることはよくあるぞと。そのたびにまたテクスチャを生成するのか。その間の解放と生成が無駄になっていないかと。少しぐらい解放を待ってた方が効率いいのではないかと。マークアンドスィープっぽいことをしてあげた方がいいのではないかと。ここで言うマークアンドスィープっぽいことというのは、参照カウントを止めるということではなく、GCが走るタイミングを真似るということです。詰まりは、メモリが足りなそうな時、ゲームが暇してそうな時、ゲーム製作者がGC命令を出してきた時に参照カウンタが0の子を解放するのがいいのではないかということです。しかし正直私には、メモリが足りなそうな時、ゲームが暇してそうな時が分かりません。唯一分かるのはゲーム製作者がGC命令を出してきた時だけです。NowLoadingとか画面の切り替え時等に明示的に解放命令呼び出してもらう形式にしようかと只今思案中です。