自分のためのプログラミング雑記

C/C++とかゲームプログラミングに関して、自分用のメモとして書く場所

DXライブラリの読み込むテクスチャフォーマットの話

久々の更新です。
DXライブラリの画像読み込み用関数LoadGraphは複数のフォーマットに対応しているが、どれがどれくらいかかるのか気になったの調べることにした。
開発環境は、
最初の雑記 - 自分のためのプログラミング雑記

コードは、
DXライブラリの描画の話 - 自分のためのプログラミング雑記

の読み込みのソースコードを使用した。(描画に関しては特にフォーマットで変化することは無かった)

画像サイズは2048×2048、計測結果はプログラムを10回行った平均、検証したフォーマットは、"bmp"、"jpg"、"png"、"tga"、"tga(RLE圧縮)"、"dds"である。

読み込み処理[msec]

画像種類 2048^2
bmp 617
jpg 3420
png 4435
tga 576
tga(RLE) 928
dds 81

ddsが圧倒的に高速で読み込めることが分かった。さすがにDirectX用のフォーマットというだけあって、DXライブラリのようなDirectXを使用している環境では最適化されているのかもしれない。
前に計測したbmppngの結果が少し変化しているが、これは変換するソフトを前回の時と変更したためである。

ちなみに、各フォーマット毎のデータサイズは、

データサイズ[MB]

画像種類 2048^2
bmp 12.0
jpg 1.15
png 1.84
tga 12.0
tga(RLE) 6.49
dds 2.66

となっている。読み込み時間と、容量のどちらを取るかによって選ぶフォーマットが変えるようにするのがいいと思う。(jpgに関して、ゲームで使うのはあまり聞かないが)

以上

DXライブラリの描画の話2

前回の記事、DXライブラリの描画の話 - 自分のためのプログラミング雑記の高速化部分についての続き。

今回は、2枚の違う画像を順に描画するのと、1枚の大きな画像か一部分を抜き出して作成した新しい画像2枚を順に描画するのでの速度の違いを検証します。

なお、開発環境は最初の雑記 - 自分のためのプログラミング雑記を参照。また、前回と同じ部分は省きます。

以下ソースコード

2枚の画像を順に描画

        int gr = LoadGraph( _T( "test256.bmp" ) );//画像1の読み込み
        int gr2 = LoadGraph( _T( "test256_2.bmp" ) );//画像2の読み込み

	for(int i = 0; i < 10; i++ )
	{
		//計測開始
		auto start = std::chrono::system_clock::now();

		for( int j = 0; j < 1000; j++)
		{
			DrawGraph( j, j, gr, TRUE);
			DrawGraph( j, j, gr2, TRUE);
		}

		//計測終了
		auto end = std::chrono::system_clock::now();

		//計測結果の出力
		std::cout << "time = "
			<< std::chrono::duration_cast<std::chrono::milliseconds>( end - start ).count()
			<< "msec."
			<< std::endl;
	}


1枚の大きな画像から一部分を抜き出して順に描画

	int graphHandle = LoadGraph( _T( "testGraph/test2048.bmp" ) );//元になる画像の読み込み

	int gr = DerivationGraph( 0, 0, 256, 256, graphHandle );//256サイズで画像を作成
	int gr2 = DerivationGraph( 256, 256, 256, 256, graphHandle );256サイズで画像を作成

	for(int i = 0; i < 10; i++ )
	{
		//計測開始
		auto start = std::chrono::system_clock::now();

		for( int j = 0; j < 1000; j++)
		{
			DrawGraph( j, j, gr, TRUE);
			DrawGraph( j, j, gr2, TRUE);
		}

		//計測終了
		auto end = std::chrono::system_clock::now();

		//計測結果の出力
		std::cout << "time = "
			<< std::chrono::duration_cast<std::chrono::milliseconds>( end - start ).count()
			<< "msec."
			<< std::endl;
	}

2枚の画像を描画するほうは、縦横ともに256サイズのbmp画像を使用。
1枚の画像から抜き出すほうは、縦横ともに2048サイズのbmp画像から256サイズになるように抜き出して使用。
それぞれ1000個ずつの描画が終わるまでの計測を5回おこなった場合の時間を調べた。

以下結果[msec]

ループ回数 別の2枚 1枚から抜き出し
1 42 67
2 43 52
3 44 3
4 39 3
5 47 2

表から分かるように、別の2枚から描画するよりも1枚から抜き出して描画した場合のほうが複数回呼び出された時に高速化されることが分かった。
DXライブラリは同じ画像を連続で描画すると高速化するということが検証できた。

ただし、たとえ1枚から抜き出した場合でも間に関係の無い画像を描画すれば高速化はリセットされるため、高速化を意識して書く場合は注意が必要である。


以上

DXライブラリの描画の話

さっそく1つ検証してみた。
昔、どこかのサイトでDXライブラリは2のn乗のサイズの画像の方が処理が高速になるようなことが書かれていたのを見た気がしたので、それを検証しました。

私の開発環境を知りたい方は最初の雑記 - 自分のためのプログラミング雑記の方をどうぞ

以下ソースコード

読み込みの計測

#include <iostream>	//標準出力のため
#include <chrono>	//時間計測のため

#include "DxLib.h"

int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR nCmdLine, int nCmdShow )
{
	//コンソール画面の呼び出し
	AllocConsole();
	FILE* fp;
	freopen_s(&fp, "CONOUT$", "w", stdout);
	freopen_s(&fp, "CONIN$", "r", stdin);

	int graphHandle[10] = {0};//グラフィックハンドル

	//ウィンドウで起動
	ChangeWindowMode( TRUE );
	if( DxLib_Init() == -1 )
	{
		return -1;
	}

	//計測開始
	auto start = std::chrono::system_clock::now();

	for( auto& i : graphHandle )
	{
		i = LoadGraph( _T( "test.png" ) );//画像の読み込み
	}

	//計測終了
	auto end = std::chrono::system_clock::now();

	//計測結果の出力
	std::cout << "time = "
		<< std::chrono::duration_cast<std::chrono::milliseconds>( end - start ).count()
		<< "msec."
		<< std::endl;

	WaitKey();//キー入力があるまで待機

	DxLib_End();//終了処理

	FreeConsole();//コンソールの解放

	return 0;
}


描画の計測

#include <iostream>	//標準出力のため
#include <chrono>	//時間計測のため

#include "DxLib.h"

int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR nCmdLine, int nCmdShow )
{
	//コンソール画面の呼び出し
	AllocConsole();
	FILE* fp;
	freopen_s(&fp, "CONOUT$", "w", stdout);
	freopen_s(&fp, "CONIN$", "r", stdin);

	//ウィンドウで起動
	ChangeWindowMode( TRUE );
	if( DxLib_Init() == -1 )
	{
		return -1;
	}

	int graphHandle = LoadGraph( _T( "test.png" ) );//画像の読み込み

	//計測開始
	auto start = std::chrono::system_clock::now();

	for( int i = 0; i < 1000; i++)
	{
		DrawGraph( i, i, graphHandle, TRUE);
	}

	//計測終了
	auto end = std::chrono::system_clock::now();

	//計測結果の出力
	std::cout << "time = "
		<< std::chrono::duration_cast<std::chrono::milliseconds>( end - start ).count()
		<< "msec."
		<< std::endl;

	WaitKey();//キー入力があるまで待機

	DxLib_End();//終了処理

	FreeConsole();//コンソールの解放

	return 0;
}


それぞれ、縦横ともに2047、2048、2049のサイズのbmpとpngを用意して行った。
(実際1pixelだけ変えるのが正解なのかは知りません)

読み込みは、同じ画像を10個読み込むのにかかった時間。
描画は、同じ画像を1000個描画するのにかかった時間。

上記のプログラムをReleaseモードで10回行った平均が下の表。

読み込み処理[msec]

画像種類 2047^2 2048^2 2049^2
bmp 1222 1247 1561
png 5065 5130 5255


描画処理[msec]

画像種類 2047^2 2048^2 2049^2
bmp 12 10 52
png 10 12 50


まず、読み込みに関しては2のn乗を超えたサイズになると少しだけ時間がかかることが分かりました。ただ、大きい画像を10枚読み込んでようやく100~300msec程度の差なのであまり気にする必要はないように感じます。

次に、描画に関しては同じように2のn乗を超えたサイズになると時間がかかるようです。差に関しては、こちらはもっと少なく40msec程度なのでこれも気にしなくてよさそうです。

1つ気になったのが、記事には載せてませんが読み込みと描画ともに、実行の初回のみ時間がかかることが分かりました。
調べてみると、どうやらDXライブラリは同じグラフィックを連続で処理すると高速化するみたいです。そのため、初回のみ高速化されてない状態なので時間がかかるということみたいです。

以上のことから、2Dゲームを作るぐらいなら特に画像のサイズを気にする必要はあまりないようです。ただ、今回の検証が拡張子が2種類だけだったり、もっと大きいテクスチャでは検証していないので実際にどうかは分かりません。


最後に今回の検証とは関係ないのですが、ソースコード内のコンソールを使用する処理を使うとコンソール画面で出力が出来るため、デバッグ作業が非常に楽になります。
時間計測に使用したchronoライブラリはC++11で登場した新機能で、かなりスマートに時間の計測が出来るためオススメです。


以上

最初の雑記

自分がプログラミングをしていて覚えたことを忘れないようにメモをする用に作りました。なので、あまり他人に対して有益な情報がある可能性はありません。

とりあえず、検証等を行っていく環境は、

OS:Windows7 32bit
CPU:Intel Corei3 1.33GHz
Memory:4GB
Language:C/C++
IDE:VisualStudio Express 2013 for Windows Desktop

になっています。
コレ以外にライブラリとしてDXライブラリ、boostを使用してこうと考えてます。

とりあえずはこんな感じでやってこうと思います。

追記:2014-02-26

IDEをVisualStudio Express 2012 for Windows Desktop から 、VisualStudio Express 2013 for Windows Desktop に変更しました。


以上