むい空間
むいむい(´ω`*)
Entries
同じ画像を50枚、画面内をくるくる跳ね回らせる処理を、OpenGL ESとUIViewでそれぞれ書いて実行してみた(SDK for iPhone OS 2.2)。
まずはOpenGL ES。
なお、以下の記述は全てSDK for iPhone OS 2.2のプロジェクトテンプレートを使ったものであり、それ以外のバージョンでも同じように書けるかどうかは不明である。
- プロジェクトテンプレート「OpenGL ES Application」を選んで新規プロジェクトを作成
- iPhone OS 2.1 Library(2.0でも良い/2.2には無い)のサンプルコード「CrashLanding」から「Texture2D.*」と「Ship.png」をコピーしてプロジェクトに加える
- EAGLViewに以下のインスタンス変数を追加
Texture2D* texture; NSArray* array;
- 各画像(スプライト)の速度や位置を保持・計算するクラス・Fooを追加
@interface Foo : NSObject { CGPoint velocity; NSInteger angularVelocity; CGPoint coord; NSInteger angle; } - (void)step; - (void)stepIn:(CGSize)size; @property CGPoint velocity; @property NSInteger angularVelocity; @property (readonly) CGPoint coord; @property (readonly) NSInteger angle; @end @implementation Foo @synthesize velocity, angularVelocity, coord, angle; - (id)init { if(self = [super init]) { velocity = CGPointMake(rand() % 11 - 5, rand() % 11 - 5); angularVelocity = rand() % 21 - 10; } return self; } - (void)step { NSInteger newAngle = angle + angularVelocity; while(newAngle >= 360) newAngle -= 360; while(newAngle < 0) newAngle += 360; angle = newAngle; coord = CGPointMake(coord.x + velocity.x, coord.y + velocity.y); } - (void)stepIn:(CGSize)size { [self step]; if(fabs(coord.x) > size.width) velocity.x = -velocity.x; if(fabs(coord.y) > size.height) velocity.y = -velocity.y; } @end EAGLView::initWithCorder:の後ろの方にテクスチャ関係の初期化コードを追加glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); texture = [[Texture2D alloc] initWithImage:[UIImage imageNamed:@"Ship.png"]]; const NSUInteger objectCount = 50; NSMutableArray* mutableArray = [[NSMutableArray alloc] initWithCapacity:objectCount]; for(int i = 0; i < objectCount; i++) { Foo* foo = [[Foo alloc] init]; [mutableArray addObject:foo]; [foo release]; } array = mutableArray;EAGLView::createFramebufferの後ろの方に視点関係の初期化コードを追加glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(-backingWidth / 2, backingWidth / 2, -backingHeight / 2, backingHeight / 2, -1, 1); glMatrixMode(GL_MODELVIEW); glViewport(0, 0, backingWidth, backingHeight); glScissor(0, 0, backingWidth, backingHeight);
EAGLView::drawViewを書き換える[EAGLContext setCurrentContext:context]; glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); CGSize size = { (backingWidth - texture.contentSize.width) / 2.0f, (backingHeight - texture.contentSize.height) / 2.0f }; for(Foo* foo in array) { [foo stepIn:size]; glPushMatrix(); glTranslatef(foo.coord.x, foo.coord.y, 0.0f); glRotatef(foo.angle, 0.0f, 0.0f, 1.0f); [texture drawAtPoint:CGPointZero]; glPopMatrix(); } glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES];EAGLView::deallocに後始末コードを追加し、ビルドが通るように#importやらフレームワークやらを追加する
OpenGL ESを使ったのはこれが初めてなので、本当にこの書き方が効率いいのか分からなかったりするが、ともかく、これを第1世代iPod touchで実行した結果は24fps(Instrumentsで測定)だった。
続いてUIView。
- プロジェクトテンプレート「Window-Based Application」を選んで新規プロジェクトを作成
- サンプルコード「CrashLanding」から「Ship.png」をコピーしてプロジェクトに加える
- OpenGL ESの方で作成したクラスFooと、そのインスタンスとUIViewを関連づけるクラス・Barを追加
@interface Bar : NSObject { Foo* foo; UIView* view; } @property (retain) Foo* foo; @property (retain) UIView* view; @end @implementation Bar @synthesize foo, view; - (void)dealloc { [view release]; [foo release]; [super dealloc]; } @end - 新規ファイルテンプレート「Objective-C NSView subclas」でクラス・MyViewを追加し、以下のように書き換える
@interface MyView : UIView { NSTimer *animationTimer; CGSize imageSize; NSArray* array; } @end @implementation MyView - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.backgroundColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0]; self.layer.sublayerTransform = CATransform3DMakeTranslation(CGRectGetMidX(frame), CGRectGetMidY(frame), 0.0f); UIImage* image = [UIImage imageNamed:@"Ship.png"]; imageSize = image.size; const NSUInteger objectCount = 50; NSMutableArray* mutableArray = [[NSMutableArray alloc] initWithCapacity:objectCount]; for(int i = 0; i < objectCount; i++) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; Bar* bar = [[[Bar alloc] init] autorelease]; bar.foo = [[[Foo alloc] init] autorelease]; UIView* view = [[[UIImageView alloc] initWithImage:image] autorelease]; view.layer.anchorPoint = CGPointMake(0.5f, 0.5f); view.center = CGPointZero; [self addSubview:view]; bar.view = view; [mutableArray addObject:bar]; [pool release]; } array = mutableArray; } return self; } - (void)drawRect:(CGRect)rect { [animationTimer release]; animationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:@selector(drawView) userInfo:nil repeats:YES]; } - (void)drawView { CGSize size = { (CGRectGetWidth(self.bounds) - imageSize.width) / 2.0f, (CGRectGetHeight(self.bounds) - imageSize.height) / 2.0f }; for(Bar* bar in array) { [bar.foo stepIn:size]; bar.view.center = bar.foo.coord; bar.view.transform = CGAffineTransformMakeRotation(bar.foo.angle * M_PI / 180); } } - (void)dealloc { [array release]; [image release]; [animationTimer release]; [super dealloc]; } applicationDidFinishLaunching:でMyViewのインスタンスをウィンドウに追加- (void)applicationDidFinishLaunching:(UIApplication *)application { UIView* view = [[MyView alloc] initWithFrame:[window bounds]]; [window addSubview:view]; [window makeKeyAndVisible]; }- ビルドが通るように#importやらフレームワークやらを追加する
このコードの実行結果は7fps。
結構、遅い。
ところで、実行してみると分かるのだが、この2つのコードは挙動に決定的な違いがある。
それは、OpenGL ESを使ったコードが60fpsに満たない分は「処理落ち」しているのに対し、UIViewの方は「フレームスキップ」している、ということである(まあ処理落ちもしているが)。
実際、1秒間にdrawViewが呼ばれる回数を数えてみたところ、OpenGL ESの方がfpsと同じ24であったのに対し、UIViewの方は40回も呼ばれていた。
setAnimationDidStopSelector: を使って描画の完了を待てば、OpenGL ESと同じ挙動を示すようにもできるのだが、フレームレートはむしろ下がる。
コメント
コメントの投稿
トラックバック
[T12] 描画命令呼び出し回数の最小化、とか
TN2230『Optimizing OpenGL ES for iPhone OS』の最終項「Minimizing the Number of Draw Calls」に、次のような記述がある。
16種256匹の蝶を描こうというのであれば、...
- 2009-01-17 19:37
- 無為空間
- トラックバック URL
- http://idlysphere.blog66.fc2.com/tb.php/164-ab842adb
- この記事にトラックバックする(FC2ブログユーザー)