スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

"DXライブラリ"で気になった事

現在の自作ゲームの制作状況を報告します。
とりあえず、リソース管理の部分は出来ました。

具体的に言うと、
-------------------------------------
【1】リソースを読み込む
【2】リソースのハンドルを割り当てる (そして返す)
【3】リソースを最後に開放する

-------------------------------------
この3つです。

実装方法としては、まずSTLのmapを2つ用意します。
---------------------------------------
std::map<int, T*>::iterator it; //ハンドルで、リソースを検索
std::map<std::string, int>::iterator it2; //名前で、ハンドルを検索
---------------------------------------

で、LoadGraph("aiueo.bmp");と言う風に呼び出した際は、
-----------------------------------------------------
【1】2番目のmapで、既に同じ名前のモノが無いか調べる。
【2】あれば、そのハンドルを返す。 (it->second)

【2】なければ、新しく生成する。
【3】ハンドルを割り当て、1番目のmapに追加。
【4】割り当てられたハンドルと、名前のセットを2番目のmapに追加。

-----------------------------------------------------

そして、DrawGraph(handle);と言う風に呼び出した際は、
-----------------------------------------------------
【1】1番目のmapで、ハンドルと対になるリソースを調べる
【2】リソースがあれば、使用する。
【2】なければ、関数呼び出しは無効。

-----------------------------------------------------

まぁ、大体こんな感じにしてます。
もっと良さそうな方法があるような気がするので、色々と調べてみる予定ですが、
ともかく、実装自体は する事が出来ました。


で、上記を見ても分かるように既に同じ名前のファイルが読み込まれていた場合は、
既に生成した同じハンドルを割り当てるようにしているのですが、

DXライブラリ は、どうも
同じハンドルは返さない仕様になっているっぽいですね。

要するに、
----------------------------------
int x = LoadGraph("Map.bmp");
int y = LoadGraph("Map.bmp");
int z = LoadGraph("Map.bmp");

----------------------------------
こういう風にしたら、 メモリ確保は3回行う 、みたいな。
(同じ画像でも、毎回 別の画像扱いで ハンドルを割り当てる)

個人的に、同じ画像とかだったら何度もメモリ確保せずに既に生成したものを返す
という形式にした方が良いような気がする。

結構、 掲示板とかで
ループ中にメモリ確保をする関数を入れてるバグの報告とか見ますし。
(「次第に速度が遅くなるので困ってます」、みたいな)

まぁ、同じハンドルを返す形式だと、
致命的なミスに気付かずにゲーム制作を進めてしまうかもしれませんし、
バグが見つかって原因を知る機会が出来る、というのは利点かもしれません。


※自分のDXライブラリは2008年6月にダウンロードしたものです。
もしかすると、現在は仕様が変わってしまっているかも?
スポンサーサイト

テーマ : ゲーム製作 関連 - ジャンル : ゲーム

コメント

No title

 私も似たような仕組みを作ったことがあります。
 キャッシュシステムと呼んでいましたが、リソースの用途毎に「箱」を用意して、読み込みシステムにそのキャッシュの箱を渡した場合は、その箱の中で同じ名前を持ったリソースがあれば同じオブジェクトを返すようにしていました。
(キャッシュの箱を渡さなければそのまま読み込む)


>STLのmapを2つ用意
 双方向 mapですね。他のところでも再利用できるよう template化してみてはどうでしょう?


>既に同じ名前のファイルが読み込まれていた場合は、
>既に生成した同じハンドルを割り当てるようにしている
 ちょっとした疑問なのですが、そのアルゴリズムは "./a.bmp"と "a.BMP"と"a.bmp"と "dummy/../a.bmp"、絶対パスの "a.bmp"を同一と見なしますか?


>1番目のmapで、ハンドルと対になるリソースを調べる
 ということはこのハンドルはDXライブラリのものではなく、独自のものということでしょうか?


>何度もメモリ確保せずに既に生成したものを返す
 気持ちはわからなく無いですが、それをすると途中で画像が差し替った時に問題になります。

 ファイル名(パス)だけでは1回目に読み込んだ Map.bmpと2回目に読み込もうとしたMap.bmpが同じである保証がありません。
 途中で自他のアプリからファイルが書き換えられるかもしれませんし、読み込みにいったドライブ毎入れ替わってしまうかもしれません。

 或いは1つ目を読み込んだ後画像をメモリ上で動的に加工・編集したとします。2回目を読み込んだとき同じファイルだったという理由で前のハンドルを返してしまうと、加工後の画像を指してしまうのでこれも問題になります。

 他にも解放周りとかいろいろありそうですが、ライブラリレベルで無条件で同じハンドルを返してしまうと問題になるので、ライブラリ製作者はそういう手段を執らなかっただと思います。


>現在は仕様が変わってしまっているかも
 今も変わっていないはずですよ。

No title

なるほど、同じような仕組みを既に作られた経験があるのですねv-293
自分の作ったのは、まだ変なバグとか有るかもしれませんので、色々と修正・チェック中です。
(既に何回かメモリリークが発生。 今は修正済みです)

双方向マップはテンプレート化してみる事にします。
まぁ、双方向マップに関わらず、
再利用できないクラスとか作ると、無駄な重複したコードが増えて
汚くなるのが目に見えてますので、
再利用可能に出来そうなものは、どんどん再利用可能にしようと思います。

名前の件についてですが、本当に単純に「名前が全く同じもの」を同じリソースとして判別します。

なので、
・別の階層にあったら読み込み失敗
(全ての階層を調べるような事はしない)
・階層を入れた名前でも別物だと判別
(実質、同じリソースを示している場合であっても)

まぁ、そんな貧相な仕組みになってます。
DXUTの方はDXUTFindDXSDKMediaFileCchとか言う
階層が異なっていても、ちゃんと調べてくれる便利な関数があるっぽいですが、
そこまでの機能はついておりませんv-292


ハンドルに関しては、
自分で被らないハンドルを返すCCreateHandlleクラスをつくり、
それを用いてハンドルを生成・管理してます。

↓大まかな仕様です。
-------------------------------------------------
・通常は内部の変数をインクリメントして、それを返す。(1、2、3、4、・・・・)
・DeleteGraph()などの削除関数が呼ばれた場合、
そのリソースのハンドルをフリーハンドルリストに追加。
・フリーハンドルリストの要素数が0でない場合は、そのリストの先頭の値を返し、返した値はリストから削除。

-------------------------------------------------


確かに、中身が変更されたのに「同じ画像である」と判断し、
勝手に「新しく読み込まずに、既存のものを返す」とか
された場合は困りますね。
(利用者にとっては、バグでしかない)

利用者からは、「別のリソースと判別する」か、
「既に同じ名前があったらそれを返す」という風にするか、
自分で指示 出来るようにしておけば混乱しないかもしれません。

No title

>再利用可能に出来そうなものは、どんどん再利用可能にしようと思います
 そういう再利用性の高いモジュール集がドキュメント付で提出物の中に用意できると少しポイントは高くなるかもしれませんね。


>単純に「名前が全く同じもの」を同じリソースとして判別します
 なるほど。
 回答ありがとうございます。


>そんな貧相な仕組みになってます
 いや、それでいいと思いますよ。
 複雑にしてもそれを作るコスト、実行時に余計な処理時間がかかるというコスト、問題が見つかった時にかかる調査時間のコスト等を考えるとシンプルな方がいいですから。

 個人的にはいっそのこと同じかどうかの判定にはパスのディレクトリのところを全部無視して、大小区別無しのファイル名だけで見てしまってもいいかな、と思いました。

 普通に汎用的な観点から考えると同じファイル名でパスが異なる別ファイルだった時に困るわけですが、ゲーム用途であることを考えるとそこは(キャッシュ不使用版も用意した上で)運用で逃げらるかな、と。
(デザイナも素材の管理上特別な理由がない限り同じファイル名で異なる素材は作らないでしょうし)


>大まかな仕様です
 んーと、ということは、一度解放されたハンドル値はすぐに再利用されることがある、ということでしょうか。

 ほんとに些細なことで申し訳ないんですが、あるテクスチャAを指すハンドルがあるとしてそのテクスチャAが今現在解放されているか・使用されているかどうか調べたくなることってないでしょうか?

No title

とりあえずは、C++の機能を活かしながら
現状の仕様で進めて行こうと思いますv-290
(想定外の問題が発生したら、適宜 対処しようかなと)

で、ハンドルについてですが、 ヤバイですね・・・コレは。
(仕様として)
------------------------------------------------
int h = LoadGraph("aiueo.bmp");  //ハンドル1を割り当て
int h2 = LoadGraph("aiueo.bmp");  //ハンドル1を割り当て
DeleteGraph(h);//ハンドル1削除

int h3 = LoadGraph("sample.bmp");  //ハンドル1割り当て

DrawGraph(100,150,h2);  //sample.bmpが描画される
------------------------------------------------
上記のようにすると不具合が発生します。
ハンドルが消えてしまったのか、既に別モノに変わってしまったのかを
知る事が出来ないと、面倒なことになりますね。

んー・・・、素直に参照数計測ポインタでカウントしたほうがいいのかも。
でも、 出来るだけint型でしたい んですよね。
(単純だから、という理由だけなのですが)

ちょっと何回LoadGraph関数が呼ばれたかをカウントしておく変数でも作って対応してみようと思います。
-------------------------------------------------
【1】変数countを作る。 (privateメンバ)
【2】LoadGraph関数を呼ぶ。 (++count)
【3】LoadGraph関数を呼ぶ。 (++count)
【4】DeleteGraph関数を呼ぶ。 (--count)
【4】DeleteGraph関数を呼ぶ。 (--count)
//ハンドル1のリソースが消える
-------------------------------------------------
まぁ、この仕様でも
LoadGraph関数を呼んだ分だけ、DeleteGraph関数を呼んでくれなかった場合、
プログラム終了までリソースが消えてくれない状態になる可能性があるんですよね。

ハンドル(int型の変数)がローカルだったら、
変数が消滅してDeleteGraph関数すら呼べなくなりますからね。
(ハンドルの値が分からなくなる)

これは、もうちょっと考えてみますv-292

No title

>出来るだけint型でしたい
 なら、同じファイル名を渡したとき LoadGraphの戻り値は毎回異なるけど、中身の画像は同じもの指している、内部で参照カウンタで管理する、というのはどうですか?


>変数が消滅してDeleteGraph関数すら呼べなくなりますからね
 そこまでくると int型では辛いですね。
 もうハンドルクラスにして、デストラクタで DeleteGraphしてしまうのがいいのかも……。

No title

あれこれ考えてみましたが、
-----------------------------------------------------
・ハンドルは異なるが、同じリソースを指している(内部で参照数も管理)
・名前を引数にとるDeleteGraph関数も作る。
・全てのリソース、同種のリソース(画像だけ、音楽だけ等)を開放する関数を作る

-----------------------------------------------------
という風にしてみようと思います。
※アドバイスを参考にさせていただきました
 ありがとうございます


mapは下記の2つがありますので、
--------------------------------------------
std::map<int, T*>::iterator it; 
std::map<std::string, int>::iterator it2;
--------------------------------------------
【1】名前で、後者のmapを検索
【2】ハンドルを得る(it->second)
【3】ハンドルで、前者のmapを検索
【4】割り当てられたリソースを削除

DeleteGraph("aiueo.bmp");という風にしても、
一応 こんな手順を踏んで削除する事は出来ます。


ともかく、これで
--------------------------------------------------------
・得たハンドルが指すリソースが、いつの間にか変わっている
・ハンドルが消滅して、指定のリソースが削除できない
・一部のリソースの削除忘れ

--------------------------------------------------------
には対応出来るかと思います(`・ω・´)
コメントの投稿
管理者にだけ表示を許可する



上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。