Entries

スポンサーサイト

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

kCAFilteNearest

タグ: Quartz Mac iPhone Objective-C

CALayerのmagnificationFilterやminificationFilterにkCAFilteNearestを設定すると、レイヤの拡大(縮小)表示時に適用するフィルタが「線形補間」から「最近傍補間」に切り替わる。
この設定(拡大用・縮小用どちらか一方)は、レイヤを等倍表示する時にも適用される。
そして最近傍補間フィルタがどのピクセルを拾うかは、実行環境によってだいぶ変わってくる。

void addLayers(CALayer* superlayer)
{
#if TARGET_OS_IPHONE
	CGImageRef image =
		[UIImage imageNamed:@"rock.gif"].CGImage;
#else
	NSString* path =
		[[NSBundle mainBundle] pathForImageResource:@"rock.gif"];
	CGImageRef image =
		[[NSBitmapImageRep imageRepWithContentsOfFile:path] CGImage];
#endif
	CGRect imageRect = CGRectMake(0, 0, 32, 32);
	
	CALayer* layer;
	
	layer = [CALayer layer];
	layer.frame = CGRectOffset(imageRect, 1, 1);
	layer.contents = (id)image;
	[superlayer addSublayer:layer];
	
	layer = [CALayer layer];
	layer.frame = CGRectOffset(imageRect, 33.5, 1.5);
	layer.contents = (id)image;
	[superlayer addSublayer:layer];
	
	layer = [CALayer layer];
	layer.frame = CGRectOffset(imageRect, 65.5, 1.5);
	layer.contents = (id)image;
	layer.magnificationFilter = kCAFilterNearest;
	[superlayer addSublayer:layer];
	
	layer = [CALayer layer];
	layer.frame = CGRectOffset(imageRect, 97.5, 1.5);
	layer.contents = (id)image;
	layer.minificationFilter = kCAFilterNearest;
	[superlayer addSublayer:layer];
}

「rock.gif」は、[ロックマン9]ロックマンポーズ集から切り抜いた32x32の画像。
このコードを、まずはiPhoneで実行してみよう。
なお、細部を見やすくするため、キャプチャ画像は2倍に拡大している。

rock_iphone.png

一番左のレイヤはコンテンツとデバイスのピクセルが完全に一致しているため補間の必要がなく、元の画像がそのまま表示される。
それ以外のレイヤはピクセルが半分ずつずれており、左から「常に線形補間」「拡大時は最近傍補間」「縮小時は最近傍補間」されて表示される。
この結果を見る限り、iPhoneでは「等倍用フィルタ=縮小用フィルタ」であり、等倍の最近傍補間は、もとの画像をわりと忠実に再現してくれるようだ。

次はiPhone Simulator。

rock_simulator.png

Simulatorでは「等倍用フィルタ=拡大用フィルタ」となる。
足元がぼやけているが、これはレイヤ境界アンチエイリアスの効果だろう。

次、MacBook Pro(late 2008)。
(ちなみにOSは以下全てMac OS X 10.5.6)

rock_mbp2008late.png

「等倍用フィルタ=縮小用フィルタ」。
足の辺りが縦に1ピクセル重複している。

次、MacBook(early 2008)。

rock_mb2008early.png

なんかパチモンくさくなった。

最後、MacBook Pro(early 2006)。

rock_mbp2006early.png

自主規制補間。

以上、今回の例ではこのような結果になった。
実際のところ、iPhoneもピクセルのずれ具合によっては崩れるし、MacBookも常にパチモンを生み出しているわけではない(むしろ生み出さないことの方が多い)。
ピクセルがずれてもドット絵を綺麗に(ぼやけさせずに)表示してくれることを期待して最近傍補間を試してみたのだが、どうやらそういう用途に使えるものではなさそうだ。

no_x11

タグ: Mac MacPorts

久しぶりに

$ sudo port upgrade outdated

を実行したら、見知らぬ「xorg-なんちゃら」がどんとインストールされた。
どうも3週間ほど前に「xorg-libs」というメタパッケージが追加され、Xを使うportsはそれに依存するようになったらしい。
しかし、Xを使うようなものを入れた覚えは、私にはない。
ImageMagickは入っているが、variantsに+no_x11を指定している。

とりあえず

$ port installed variant:no_x11

してみると

  • cairo
  • graphviz
  • pango

が+no_x11の指定無しでインストールされていた。

なるほど、これらはdoxygenをインストールしたときに入ってきたものだ。
doxygen自体にはno_x11のvariantsがないから、これらのportsも+no_x11の指定なしでインストールされてしまっていたのだろう。

また、ffmpegの方では、新たにlibsdlという、これまた「+no_x11を指定しなければXに依存するライブラリ」への依存関係が発生していた。
このままupdateすると、「ffmpegをアップデートするためにlibsdlをインストールするためにxorg-libsをインストール」してしまうことになる。

以上のことから、「xorg-libsをインストールしないために個別にno_x11を指定する」のはナンセンスであることがわかる。
そういうことは「variants.conf」を使って行うべきだ。
そうすれば、no_x11のvariantsを持つportsは全て+no_x11付きでインストールされる。

しかし、そこまでして全てのportsに+no_x11を指定しても、MacPorts 1.7.0は「variantsによるdepends_lib-delete(依存関係除外指示)をupdate時は無視する」ため、結局xorg-libsは入ってくる。
もっとも、これは1.8.0では修正されるらしいので、xorg-libsを入れたくなく、特に急いでupdateする必要もないのなら、1.8.0までupdateを待つのも手だ。

続きを読む

プロトコルメソッドへのリンク

タグ: Doxygen Objective-C
/**
 * @brief プロトコル Foo のメソッドへのリンクは……
 *
 * - Foo::bar では発生しない
 * - Foo-p::bar でも発生しない
 * - @ref Foo-p::bar で発生する
 */
@protocol Foo
/**
 * @brief このメソッドにリンクしたい
 */
- (void)bar;
@end
protocol_ref.png

以上、Doxygen 1.5.7.1のお話。
「-p」というのは、プロトコル名とクラス名が衝突しないよう、Doxygenがプロトコルの識別子に付ける接尾辞。
それを直接利用することで、半ば無理矢理リンクを生成させている。
もちろん、正規の書き方ではない(と思う)。今後もこう書けるという保証もない。
ちなみに、同じFooという名前のクラスが存在する場合、プロトコルFooへのリンクになっていた Foo はクラスFooへのリンクとなり、 Foo::bar はクラスFooのメソッドbarへのリンクとなる。

で。
ここでふと、疑問に思ったのだが……
Objective-Cのクラスとプロトコルは、これで名前の衝突が回避できる。
では、Objective-CとC++で同名のクラスがあった場合、果たしてどうなるのだろう。

/**
 * @brief C++クラス
 *
 * この Foo::bar はどこにリンクする?
 */
class Foo
{
    /**
     * @brief C++クラス Foo のメンバ変数
     */
    int bar;
};

/**
 * @brief Objective-Cクラス
 *
 * この Foo::bar はどこにリンクする?
 */
@interface Foo
/**
 * @brief Objective-Cクラス Foo のインスタンスメソッド
 */
- (void)bar;
@end
cpp_objc_conflict1.png

む。C++のクラスしかない。
C++の方が優位なのか?

cpp_objc_conflict2.png

(;゚Д゚)混ざってる!

……て。
よく考えたら、クラス名はC++とObjective-Cで衝突するから、分けられないのは当たり前だった。
一晩寝て気づいたよ……('A`)

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

タグ: 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にある一つの大きなテクスチャに複数のテクスチャをまとめ、描画命令の呼び出し回数を削減せよという指示は、なるほど、たしかに効果が期待できそうに思える。

01月09日のココロ日記(BlogPet)

話の背比べ……

*このエントリは、ブログペットのココロが書いてます♪

Appendix

タグ

Blog内検索

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