Entries

スポンサーサイト

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

描画命令呼び出し回数の最小化、とか

タグ: iPhone Objective-C OpenGL

TN2230『Optimizing OpenGL ES for iPhone OS』の最終項「Minimizing the Number of Draw Calls」に、次のような記述がある。

16種256匹の蝶を描こうというのであれば、まずは描画命令を呼出す回数を、「1匹1回計256回」から「1種1回計16回」に削減するよう努めよ。

というわけで、以前作成した「1種50機の宇宙船を描くサンプル」における「1機1回計50回」の glDrawArrays 呼び出しを、「1種1回計1回」の glDrawElements 呼び出しに変えてみる。
なお、座標計算(とそのコストの把握)が面倒だったので、今回 glRotatef によるモデルビューの回転は排除した。

// drawView のfor文をまるっと書き換え
const GLfloat textureCoordinates[] = {
	0, 1,
	1, 1,
	0, 0,
	1, 0,
};

const GLfloat halfPixelsWide = texture.pixelsWide / 2.f;
const GLfloat halfPixelsHigh = texture.pixelsHigh / 2.f;

const NSUInteger objectCount = [array count];
const NSUInteger vertexCount = objectCount * 4;
const NSUInteger indexCount = objectCount * 6 - 2;

GLfloat vertices[vertexCount * 3];
GLfloat coordinates[vertexCount * 2];
GLushort indices[indexCount];

GLfloat* pv = vertices;
GLfloat* pc = coordinates;
GLushort* pi = indices;
GLushort index = 0;
for(Foo* foo in array) {
	[foo stepIn:size];
	
	const GLfloat modelVertices[] = {
		-halfPixelsWide + foo.coord.x, -halfPixelsHigh + foo.coord.y, 0.f,
		 halfPixelsWide + foo.coord.x, -halfPixelsHigh + foo.coord.y, 0.f,
		-halfPixelsWide + foo.coord.x,  halfPixelsHigh + foo.coord.y, 0.f,
		 halfPixelsWide + foo.coord.x,  halfPixelsHigh + foo.coord.y, 0.f,
	};
	
	memcpy(pv, modelVertices, sizeof(modelVertices));
	pv += sizeof(modelVertices) / sizeof(*modelVertices);
	
	memcpy(pc, textureCoordinates, sizeof(textureCoordinates));
	pc += sizeof(textureCoordinates) / sizeof(*textureCoordinates);
	
	if(index > 0) {
		*(pi++) = index - 1;
		*(pi++) = index;
	}
	for(int i = 0; i < 4; i++) {
		*(pi++) = index++;
	}
}

glBindTexture(GL_TEXTURE_2D, texture.name);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_SHORT, indices);
glDrawArrays 50回26fps
glDrawElements 1回26fps

まるで変わっていない……!(^o^)

多分、アルファブレンディングが遅過ぎて、変更の影響が見えないのだろう。
TN2230にもアルファブレンディングやアルファテスティングの使用は必要最小限に留めよと記されている。
なのでとりあえず initWithCoder:glEnable(GL_BLEND); をコメントアウト。するとフレームレートがどちらも60fpsになってので、 objectCount を50から500に増やして比較してみた。

glDrawArrays 500回17fps
glDrawElements 1回8fps

1回にまとめた方がむしろ遅くなってる……!/(^o^)\

これは、なんだろう。画像が大きくて、重なりあう部分が多いからだろうか。
ならばあまり重ならないよう画像のサイズを小さく(32x32に)し、 velocity の計算を

velocity = CGPointMake(rand() % 1001 / 100.f - 5, rand() % 1001 / 100.f - 5);

このように変更したらどうか。

glDrawArrays 500回24fps
glDrawElements 1回16fps(開始1秒のばらけきっていない時分は33fps)

重なってると速いのは、むしろ1回にまとめた方だった!\(^o^)/

そんなわけで、残念ながら呼び出しオーバーヘッドの削減によるパフォーマンスの向上は実践できず。
素直に1機ずつ描いた方が、コード的にも速度的にも良いという結果になった。

ちなみに。
glDrawArrays を500回呼んでいる方は、同時に glBindTexture も500回呼んでいる。
これを、TN2230の冗長な状態変更を避けよという言葉に従い、ループ前に1度だけ呼ぶようにすると、フレームレートが24fpsから30fpsに向上する上、 glEnable(GL_BLEND); を有効にしても(少なくともこの32x32画像では)パフォーマンスにさほど影響が出ない。
この結果を見ると、同じくTN2230にある一つの大きなテクスチャに複数のテクスチャをまとめ、描画命令の呼び出し回数を削減せよという指示は、なるほど、たしかに効果が期待できそうに思える。

スポンサーサイト

コメント

[C12]

初めまして。
試したわけではないのですが、ソースコードを見ると、むしろmemcpyがボドルネックになってやしないかと思いました。
計算結果を、直接verticesに書き込んでいったらどうでしょう?
  • 2009-02-20 13:11
  • URL
  • 編集

[C13]

ご意見ありがとうございます。
「コールオーバーヘッドを減らそう」というのに「memcpy()」は如何なものか、というのはごもっともです。
というわけで、 modelVertices を作って memcpy() している部分を
*pv++ = -halfPixelsWide + foo.coord.x;
*pv++ = -halfPixelsHigh + foo.coord.y;
*pv++ = 0.f;
...
このような形に変えて実行してみました。
結果、フレームレートの方に目立った変化は見られませんでした。
速度の向上はフレームあたり0.05ms程ありましたが、このサンプルでは目に見えるほどの違いは出ないようです。
  • 2009-02-20 22:16
  • むい
  • URL
  • 編集

コメントの投稿

コメントの投稿
管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://idlysphere.blog66.fc2.com/tb.php/168-3dddc58c
この記事にトラックバックする(FC2ブログユーザー)

Appendix

タグ

Blog内検索

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