Game loop and performance

Member
Posts: 94
Joined: 2008.08
Post: #1
So, I've created a few opengl games and as I'm still fairly new to this, from reading around recently I think I'm not doing my game loop very efficiently.

I've also just made a post about mp3 music in my latest game severly affect performance, I wonder if the two might be related(?)

Here's my current situation:

Code:
//AppDelegate
[glView startAnimation];

//EAGLView
- (void)startAnimation {
    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
}

- (void)drawView {
    [EAGLContext setCurrentContext:context];
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    drawScene();

    updateGame();


    glFlush();
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

So after reading sacred softwares tutorial on how to handle time based animation etc I'm wondering how best to integrate it. Should I keep the NSTimer and in drawView put the code from the tutorial about seperating game updates the game updates/drawing? Or should I remove the NSTimer all together, and do something else?
Quote this message in a reply
Member
Posts: 249
Joined: 2008.10
Post: #2
I wonder the same questions.

BTW, do you queue events?
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #3
Frank and I both posted some sample code for it in this other thread a while back. I'm using Display Link now on iPhone, which offers a bit better performance, but the timing section is pretty much the same.

I haven't really had any problem rolling with the timer, but there are many who swear by their own loops. I can't recall seeing a single example of an actual implementation of it, only pseudocode and descriptions. The way I had tried it myself didn't really yield much, if any, better performance, so I wouldn't mind seeing a working example posted by someone else who feels they got it down.

Anyway, regardless of which technique you decide to go with, since you've already read ThemsAllTook's timing tutorial, be sure to also check out this other article as well to get an idea of how you might want to further improve it at some point in the future. Adding the extra little step to interpolate between updates requires some extra coding and state management, which isn't entirely necessary, but wow, it yields perfection in terms of smoothness! I haven't bothered adding the interpolation to any of my iPhone stuff, but I figure it's worth mentioning.
Quote this message in a reply
Apprentice
Posts: 9
Joined: 2009.02
Post: #4
This is what we've been doing in all of our games where we want to support older than 3.x and don't use CADisplayLink. It's definitely worked better than NSTimer for us:

Code:
- (void) startAnimation {
     NSThread *myGameLoop = [[NSThread alloc] initWithTarget:self selector:@selector(mainLoopTimer) object:nil];
     [self setAnimationThread:myGameLoop];
     [myGameLoop start];
     [myGameLoop release];
}

- (void) stopAnimation {
     [self setAnimationThread:nil];
}

- (void) setAnimationThread:(NSThread *)newThread {
     // animationThread is a NSThread* declared in the header file
     if (animationThread != nil) {
          [animationThread cancel];
          [animationThread release];
          animationThread = nil;
     }

     animationThread = newThread;
     [animationThread retain];
}

- (void) mainLoopTimer {
     while (![[NSThread currentThread] isCancelled]) {
          [self performSelectorOnMainThread:@selector(eventLoop) withObject:nil waitUntilDone:YES];
          usleep(1500);
     }
}

- (void) eventLoop {
     // Do all your loop stuff (handle events, draw everything, etc)
}

You use the start/stop animation to start and stop the mainEventLoop. It uses a thread to drive the event loop (but still running the mainEventLoop on the main thread. You can change the usleep amount to control how much time to give back to the OS for event handling etc. It seems to work really nicely.
Quote this message in a reply
⌘-R in Chief
Posts: 1,252
Joined: 2002.05
Post: #5
Yikes. kshep, that's a whole lot of... pointlessness. All you're doing is attempting to regularly fire off calls on the main run loop which is just what a timer does, except a timer is more efficient than what you're doing.

Why did you do go down that path?
Quote this message in a reply
Apprentice
Posts: 9
Joined: 2009.02
Post: #6
Yup, I was skeptical at first too, seems like it would be no different. I've tried and profiled this and the other methods in our games. CADisplayLink is more consistent but requires 3.x. This way gets us a much more consistent 60fps frame rate than NSTimer across 2.x and 3.x devices. NSTimer was very finicky for us and drop from 60 to 40fps or so fairly regularly.

A lot of other devs I know have switched to this method as well and it seems to be working for them as well.
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #7
Before we wind up stoking another heated discussion, I would like to ask that those who participate in this thread from here on provide an actual working example of their technique: either post an Xcode project, or code that can be pasted into an Xcode template and executed. Pseudocode just isn't enough. There are other threads arguing about the merits of various techniques alone without hard code to back them up. Let's see the goods here! Smile

[edit] Here's one example of an existing heated thread on this topic, which we'd prefer not to repeat.
Quote this message in a reply
Apprentice
Posts: 9
Joined: 2009.02
Post: #8
@AnotherJake The above code should be trivial to drop into the OpenGL ES template. Is there something that is missing?

@FreakSoftware I now recall the reasoning why this works better than NSTimer. It's actually very similar to trying to overdrive the frequency of the NSTimer updates. Lots of people get smoother frame rates using 240 updates per second or more. The problem with overdriving the NSTimer is that you end up flooding the event queue and start having terrible response times for touches or other system events. With this you are overdriving and getting the smoother framerate, but leaving just enough time to actually process the events well (through the usleep).
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #9
kshep Wrote:@AnotherJake The above code should be trivial to drop into the OpenGL ES template. Is there something that is missing?

Well, it's pretty sparse and doesn't draw or do anything as-is. I'd actually have to come up with an implementation, and while that is clearly trivial for you to do with your code and your way of thinking, it might not be worth the extra five minutes I have to spend on my end Wink Look at the link to the thread I mentioned above. I provide cut-and-paste code for an Xcode template which executes and does something.

If this were just one person's viewpoint I'd spend the effort, but there are several other guys who might bump in here, and a talk-fest without real examples is sure to ensue, so working examples are a must Wink
Quote this message in a reply
Member
Posts: 446
Joined: 2002.09
Post: #10
I've since ditched the timer and use a tight loop. I'd post some code but I don't want AnotherJake to yell at me for not posting something you can cut & paste Rasp
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #11
LOL!!!

If you're using a tight loop now, I'd really love to see a working example of it. Can I yell at you and beg at the same time? Rasp

... that doesn't sound right. That's something my girlfriend does. Sneaky
Quote this message in a reply
Member
Posts: 446
Joined: 2002.09
Post: #12
LOL OK, here's a brute-force copy paste...

Create a new project using the OpenGLES template (I'm using the 3.1.2 SDK) then crack open EAGLView.m and replace startAnimation and stopAnimation with this:

Code:
- (void) runAnimation
{
  while (animating) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, (1.0/250.0), FALSE) == kCFRunLoopRunHandledSource);
    [self drawView:nil];
    [pool release];
  }
}

- (void) startAnimation
{
  animating = TRUE;
  [self performSelectorOnMainThread:@selector(runAnimation) withObject:nil waitUntilDone:NO];
}

- (void)stopAnimation
{
  animating = FALSE;
}

In a real app drawView would be much smarter of course, using an approach like this: http://gafferongames.com/game-physics/fi...-timestep/

The (1.0/250.0) value is negotiable, but I found that to be a reasonable starting point.

I posted some code in that old thread that sorta explained how I handle timing and fixed steps but I think it's old and busted. I can post my current frame driver code if anyone thinks it'll help but it definitely won't be copy & paste!
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #13
Perfect! Well done! Grin
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  How do you manage events and game loop? riruilo 2 3,138 Nov 23, 2009 04:16 PM
Last Post: riruilo
  Where to put my game loop? davidB 2 2,279 Jan 11, 2009 08:04 AM
Last Post: davidB