Alternative to NSTimer

Nibbie
Posts: 2
Joined: 2009.04
Post: #16
When using a while loop to handle the main game loop, is your game able to handle multiple touches? I switched from NSTimer to a while loop similar to demonpants' but my app no longer handles multiple touch events. It won't recognize new touch events unless any previous touch event has ended.
Quote this message in a reply
Member
Posts: 61
Joined: 2009.01
Post: #17
Multiple touches works just fine for me. Did you make sure that setting didn't somehow get turned off?
Quote this message in a reply
Nibbie
Posts: 1
Joined: 2009.05
Post: #18
Where exactly do you place this mainLoop code? Is this called once from applicationDidFinishLaunching or are you wedging in someplace else?

I have set up my game to enter its main loop from applicationDidFinishLaunching
and never return, however I get no rending what-so-ever - so I suspect that I have a flaw in the setup.
Quote this message in a reply
Moderator
Posts: 133
Joined: 2008.05
Post: #19
Just read the previous posts...

When you create an instance of a NSRunLoop, it creates an autorelease pool. Any methods/functions/whatevers that get called by this NSRunLoop have an autorelease pool to back it up.

A CFRunLoop does not create an autorelease pool. If a CFRunLoop calls a method/function/whatever, it is not backed up by an autorelease pool. For example, when you create a CFStream with a callback for when bytes arrive, that callback function does not have an autorelease pool.

You might think you can avoid this problem by not using autorelease. This is ridiculous. Just because you don't explicitly send an autorelease message to one of the objects you own doesn't mean that a method you call doesn't send the message autorelease to one of the objects it owns.
Quote this message in a reply
Nibbie
Posts: 2
Joined: 2009.01
Post: #20
Well first of all thanks for all the useful information in this thread, I was having problems with NSTimer for quite some time. I now call the gameloop in my glView with

Code:
[self performSelectorOnMainThread:@selector(mainGameLoop) withObject:nil waitUntilDone:NO];


Is this the preferred way, or is there a more efficient alternative?
Quote this message in a reply
Member
Posts: 35
Joined: 2008.06
Post: #21
Thanks for the suggestion.
I gained a couple fps with this code rather than using a nstimer.


In my mainloop I added, in addition to the

while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004, FALSE) == kCFRunLoopRunHandledSource);


I added a safe sleep to keep the loop from polling too heavily.

Code:
safeSleep(150);


void safeSleep(unsigned short millisecs ) {
    struct timeval ts1;

    ts1.tv_sec  = millisecs * 0.001f;
    ts1.tv_usec = millisecs % 1000000000;
    // printf("total secs = %f", ts1.tv_sec + ts1.tv_usec * 0.000001f);
    select( 0, 0, 0, 0, &ts1 );    

} // safeSleep
Quote this message in a reply
Member
Posts: 241
Joined: 2008.07
Post: #22
My 2 cents... I'm working on a game that's become quite large in scope now. It's in Objective C as much as possible but since we had to port from Windows some of the engines, there are many areas that are Objective-C++. It's mixing 2D and 3D graphics in OpenGL ES and has audio, accelerometer, touch, tap, and the kitchen sink. I've been using NSTimer for the game loop at 1/60 ever since the beginning and mach timers for timing and it runs great.

From experience I recommend strongly that you keep NSTimer for your loop and use mach for timing. If you even get more than 60 FPS, your game is pretty simple and it shouldn't matter anyway. If it's less than 60 FPS NSTimer will fire as frequently as possible anyway. Trying to do all this other stuff is complicating things for you and might make your life miserable.

Apple is a very successful company and they have some of the most intelligent and innovative people in the world working for them. If you find yourself cussing them as you struggle to defy their design, maybe consider that you're doing something wrong, not they.
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #23
bmantzey Wrote:My 2 cents... I'm working on a game that's become quite large in scope now. It's in Objective C as much as possible but since we had to port from Windows some of the engines, there are many areas that are Objective-C++. It's mixing 2D and 3D graphics in OpenGL ES and has audio, accelerometer, touch, tap, and the kitchen sink. I've been using NSTimer for the game loop at 1/60 ever since the beginning and mach timers for timing and it runs great.

From experience I recommend strongly that you keep NSTimer for your loop and use mach for timing. If you even get more than 60 FPS, your game is pretty simple and it shouldn't matter anyway. If it's less than 60 FPS NSTimer will fire as frequently as possible anyway. Trying to do all this other stuff is complicating things for you and might make your life miserable.

Apple is a very successful company and they have some of the most intelligent and innovative people in the world working for them. If you find yourself cussing them as you struggle to defy their design, maybe consider that you're doing something wrong, not they.


Oh they are wrong often enough ... like for instance their borked implementation of OpenGL driver (copyVertexData anyone ?) ... implicitly disabling anti-aliasing by forcing OpenGL ES devs to render to a texture etc ..etc ..

NSTimer is kind of like WM_TIMER on Windows …. good enough for GUI apps but I don’t know anybody who uses it for coding time sensitive apps ( like games)
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #24
green_ghost Wrote:Thanks for the suggestion.
I gained a couple fps with this code rather than using a nstimer.


In my mainloop I added, in addition to the

while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004, FALSE) == kCFRunLoopRunHandledSource);


I added a safe sleep to keep the loop from polling too heavily.

Code:
safeSleep(150);


void safeSleep(unsigned short millisecs ) {
    struct timeval ts1;

    ts1.tv_sec  = millisecs * 0.001f;
    ts1.tv_usec = millisecs % 1000000000;
    // printf("total secs = %f", ts1.tv_sec + ts1.tv_usec * 0.000001f);
    select( 0, 0, 0, 0, &ts1 );    

} // safeSleep


Why don't you just run CFRunLoopRunInMode with whatever time period you want to sleep for instead of doing selects ?
Quote this message in a reply
Member
Posts: 35
Joined: 2008.06
Post: #25
Yeah, your right.

But I did find the most responsiveness and fps when I used two calls to pause in the loop.
YMMV Wink

Code:
mainloop {
    while ( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0027f, FALSE) == kCFRunLoopRunHandledSource);
   // update
   // render
   while ( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0017f, FALSE) == kCFRunLoopRunHandledSource);
}
Quote this message in a reply
Member
Posts: 25
Joined: 2009.05
Post: #26
Finally composed fixed time step loop:
while (!quit)
{
begin_time = GetTimeInMsSinceCPUStart();
Game::Step();
step_time = GetTimeInMsSinceCPUStart();
Game::Render();
[glView swapBuffers];
end_time = GetTimeInMsSinceCPUStart();

double sleep = STEP_DT - (end_time - begin_time);
if (sleep < 0.002) sleep = 0.002;
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, sleep, false) == kCFRunLoopRunHandledSource);

LOG("World step: %10.3f", step_time - begin_time);
LOG("Render step: %10.3f", end_time - step_time);

if (last_time != 0.0)
LOG("Frame DT = %10.3f, WorkTime = %10.3f", begin_time - last_time, end_time - begin_time);
last_time = begin_time;
}

If frame work time is below STEP_DT, then rest of the time smoking CFRunLoopRunInMode. If frame time is above STEP_DT, then CFRunLoopRunInMode should run at least 0.002 sec.
Quote this message in a reply
Member
Posts: 24
Joined: 2009.02
Post: #27
I'm going to disagree with the general sentiment in this thread and say using a thread is much better than using a timer. If the amount of time it takes to render your frame gets too close to your timer period (16.667ms for 60fps), then the incidence of misfires from the timer becomes unacceptable. I posted a blog about this earlier today (although I wrote this up a few months ago).

A timer overrun, no matter how small will mean a 'tick' is missed. The next tick will be at the next scheduled time. It's quite common to see ticks like this:

16.67ms, 16.67ms, 33.34ms, 16.67ms, etc.

That 33.34ms tick is generally going to cause a jitter. Sure, increasing the timer period as many here have suggested helps. But a misfire still means waiting for the next scheduled tick. A thread will never have this problem.

In fact, if your thread churns out frames as fast as possible, you'll find if your rendering takes less than 16.67ms your framerate will be exactly 60fps as one of the calls (I suspect flipping the frame buffer) blocks.

So in my book, a thread is the only option unless you can render your frame in something significantly less than 16.67ms (for 60fps).
Quote this message in a reply
Member
Posts: 25
Joined: 2009.05
Post: #28
mpatric

mpatric Wrote:I'm going to disagree with the general sentiment in this thread, and say using a thread is much better than using a timer.

Generally this thread dedicated exactly to unacceptable behavior of NSTimer. I have found this thread when i have got suddenly doubled render time. i was trying to setup fixed time step game loop with dt = 0.030s, and found that actually it takes 0.060s.
As you can see my post above there is no NSTimer atall, and no mess with threads. Only odd thing is I have very unstable render time that jumps from 42ms to 55ms from frame to frame, and i cannot find reason of it.

PS I can only confirm that render in thread could increase render performance in some cases.
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #29
mpatric Wrote:I'm going to disagree with the general sentiment in this thread and say using a thread is much better than using a timer. If the amount of time it takes to render your frame gets too close to your timer period (16.667ms for 60fps), then the incidence of misfires from the timer becomes unacceptable. I posted a blog about this earlier today (although I wrote this up a few months ago).

A timer overrun, no matter how small will mean a 'tick' is missed. The next tick will be at the next scheduled time. It's quite common to see ticks like this:

16.67ms, 16.67ms, 33.34ms, 16.67ms, etc.

That 33.34ms tick is generally going to cause a jitter. Sure, increasing the timer period as many here have suggested helps. But a misfire still means waiting for the next scheduled tick. A thread will never have this problem.

In fact, if your thread churns out frames as fast as possible, you'll find if your rendering takes less than 16.67ms your framerate will be exactly 60fps as one of the calls (I suspect flipping the frame buffer) blocks.

So in my book, a thread is the only option unless you can render your frame in something significantly less than 16.67ms (for 60fps).


Yeah, it makes sense.

On other hand I am kind of puzzled why would you need a separate thread to handle timing issues.
Imo it is much simpler to run your own loop and simply enter the default message loop if you have some time to spare or just for a set bare minimum if your game starts to chase its own tail.

Am I missing anything here ?
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #30
I think people are misunderstanding NSTimer. It's not a hard-realtime timing solution. You're supposed to set the timer to fire faster than your intended framerate, and check the time in your callback. If it's running too fast, you'd just do nothing, and wait for the next callback. If the timer runs slower than intended, it's because device is busy doing something else.

The custom runloop doesn't do much else, only in a roundabout fashion. A separate thread only helps if the main thread is blocked on IO or other transactions which leave the CPU idle.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  NSTimer fine, CADisplayLink having some issues monteboyd 5 9,235 Aug 31, 2010 07:05 PM
Last Post: Skorche
  NSTimer hiccups/choppy/jerky jeonghyunhan 2 2,779 Sep 24, 2009 07:35 PM
Last Post: jeonghyunhan
  Using an NSTimer to progressively draw a view StevenD 4 4,227 May 14, 2009 07:57 AM
Last Post: StevenD
  OpenGL render loop - NSTimer vs rendering thread smallstepforman 27 22,681 Feb 2, 2009 10:22 AM
Last Post: ThemsAllTook
  Alternative Input/Control Ideas chrisco 8 4,272 Aug 6, 2008 03:21 PM
Last Post: bruss14