はかせのラボ

私の頭の中を書いていく雑記ブログです

DirectX バグ取り ~キャッシュミス~

あいさつ

どうも、はかせです。
今回は前回解決できていなかったメモリ増加バグの解決編です。

前回までのおさらい

前回犯人はローカル変数が解放されていないことだと判断し
捜査をしていました。

ですが、それは真犯人が仕組んだ巧妙な罠だったのです。

罠の概要

まずはコードを見てみます。

std::wstringstream hp;//←前回犯人だと思っていた変数
hp.precision(6);
hp << value;
auto hpt = hp.str();
auto hpptr = hpt.c_str();
mHPText->UpdateText(hpptr);
hp.clear();

前回は確保したstd::wstringstreamがスコープ抜けたときに解放されていない
という判断をしました。

それはこの一連の処理をコメントアウトしたらメモリ増加が止まったからです。
動的にメモリ確保しているのはstd::wstringstreamだけだと思っていました。
なのでstd::wstirngstreamstd::to_wstringに変えて実行してみました。
(ToString的なものを発見できました)

これでローカル変数確保がなくなりメモリ増加も止まるだろうとそう思っていました。
ですが結果はむしろ逆で、メモリ増加はより増えました。

真犯人

std::wstringstream hp;//←前回犯人だと思っていた変数
hp.precision(6);
hp << value;
auto hpt = hp.str();
auto hpptr = hpt.c_str();
mHPText->UpdateText(hpptr);//←真犯人
hp.clear();

真犯人はキャッシュミスでした。
キャッシュはテクスチャ描画の時に使用しています。
今回のキャッシュミスは数値の描画の時に発生していました。

キャッシュの概要

テキスト描画では描画するときに既に文字が作成済みだった場合は
キャッシュから文字のテクスチャを持ってきて描画、
なければ作成してキャッシュします。

キャッシュに関してはこちらをどうぞ。
こちらの記事では画像のテクスチャのキャッシュを行っていますが
文字のテクスチャも所詮同じテクスチャなので同様の機構でキャッシュしていました。

キャッシュのキーとなる値は画像の場合はその画像のパスを、
文字の場合は1文字ずつ切り分けてその文字で設定しキャッシュしていました。
(GAMEなら"G","A","M","E"というように分けてキャッシュを行っていました)
実際ちゃんとその値でキャッシュはされていましたし、
表示もされていました。

ただキャッシュ検索が問題でした。
私は1文字ずつ切り分ける時よくあるchar型のポインタを++して実体を取り出すやり方で切り分けをしました。

ですが、キャッシュ検索するときには
実態を取り出す前のポインタの状態で比較をしていました。
なので"GAME"で実体は”G"であっても”GAME”で比較されてしまい、キャッシュミスを引き起こしていました。

//textは"G"を示しているがポインタ比較のため"GAME"で比較されてしまった
if (text == tex.get()->fileName)
{
	pReturn = tex.get();
	break;
}

真犯人逮捕

今度こそ真犯人をしっかりみつけました。
罪状もばっちりです。
あとは逮捕するのみ。
ということで逮捕します。

問題なのはキャッシュ検索の際に実体ではなく
ポインタを使っていたことです。
なので実体で比較するように直します。
ただstd::wstringwchar_tは直接比較できないようなので
std::wstringに直してみます。
wchr_tstd::wstringへの変換は直代入でオッケーみたいです。

TEXTURE_DATA * DXRenderDataPool::FindTextureData(wchar_t text)
{
        //実体のポインタなので特定の1文字を示してくれる
	std::wstring str = &text;
	TEXTURE_DATA* pReturn = nullptr;
	//テクスチャ情報のキャッシュがあるか
	for (auto &tex : mTextureList)
	{
		if (str == tex.get()->fileName)
		{
			pReturn = tex.get();
			break;
		}
	}
	return pReturn;
}

これで実行してみたところ最初はキャッシュが無いため
メモリ増加が起こりましたが、
少しするとあとは何回やってもメモリ増加は発生しなくなりました。

あとがき

今回は前回に続きバグ取りでした。
バグは犯人が見つかればあとは楽なのですが、
犯人捜しがとにかくむずかしいです。
今回のように犯人は捜査をかく乱するための罠を張り巡らしています。
(まぁ罠というか自爆というか・・・orz)

今後は罠を最初から見抜いて真犯人に一気に詰め寄れるように精進します。
それでは今回はこの辺でノシ

今回作ったものはgithubに上げました。
github.com

ところで・・・

前回発生していた問題のメモリ増加バグは解決できました。
ですが、解決の途中でぼやいていたデストラクタ呼ばれない問題は解決できていません。
こっちはネットで調べても手元で色々やってもうんともすんとも言いません。
直接的な問題発生にはならないと思いますが、
解決できていないのはなんか気持ち悪いです。
コードを載せますのでわかる方がいらっしゃいましたら教えてくださいm(__)m

struct BULLET_SETTING_DATA
{
	//位置情報
	TRANSFORM* pTransform;
	TRANSFORM transform;
	//弾に設定するタグ
	Tag tag;
	//x方向にかける力
	float xVectol;
	//y方向にかける力
	float yVectol;
	//読み込む画像のパス
	std::wstring texturePath;
	void ScaleRatio(float ratio)
	{
		scaleXRatio = ratio;
		scaleYRatio = ratio;
		scaleZRatio = ratio;
	}
	//自身のスケールを1とした時の弾の大きさの割合
	float scaleXRatio = 1.0f;
	float scaleYRatio = 1.0f;
	float scaleZRatio = 1.0f;
	bool isXFixed = false;
	bool isYFixed = false;
	BULLET_SETTING_DATA()
	{
		scaleXRatio = 1.0f;
		scaleYRatio = 1.0f;
		scaleZRatio = 1.0f;
	}
	~BULLET_SETTING_DATA()
	{
                //何も書かないとブレークポイントで確かめられなかった
		scaleXRatio = 0.0f;
		pTransform = nullptr;
	}
};