4

I've asked a more general question about how CADisplayLink and drawRect behave here, but as that question doesn't have any answers yet, I thought I'd ask a more specific one: has anyone managed to get an app to run at 60fps with a drawRect method that takes ~10ms to execute?

In my app, I configure a CADisplayLink like so:

displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

and the update method simply calls setNeedsDisplay on my custom UIView (as well as checking for skipped frames):

- (void)update
{
    static CFTimeInterval timestamp = 0;

    // check for skipped frame
    CFTimeInterval newTimestamp = displayLink.timestamp;
    if ((newTimestamp - timestamp) > (displayLink.duration * (displayLink.frameInterval + 0.5)))
        NSLog(@"Frame skipped!");
    timestamp = newTimestamp;

    [myView setNeedsDisplay];
}

This begins to skip frames as soon as drawRect takes more than 4 or 5 milliseconds. For my purposes, it will need up to around 10-11ms, but I thought that given 1/60fps = 16.7ms this should be no problem.

Has anyone achieved this? What am I doing wrong?

Community
  • 1
  • 1
deltacrux
  • 1,186
  • 11
  • 18
  • From your description it sounds you're doing everything right. 60 fps is nice under ideal circumstances, but even native Apple UI elements drop down to 40-50 fps, for example when scrolling a table view quickly. – Aaron Brager Dec 15 '14 at 22:53
  • Also, I think you're probably already doing this, but in `drawRect:` make sure you're using the delta in `displayLink.timestamp` (rather than some hardcoded delta, like 5 points) to compute your movement. – Aaron Brager Dec 15 '14 at 22:55
  • The problem is that when the update method takes even slightly too long, the display link misses its next firing and the frame rate drops right down to 30fps. – deltacrux Dec 15 '14 at 22:57
  • Yeah, I am using deltas. It still looks bad when the framerate jumps between 60 and 30fps. I just don't understand why I can't use the full 16ms between frames. – deltacrux Dec 15 '14 at 22:58

1 Answers1

4

I just don't understand why I can't use the full 16ms between frames

Other stuff gets drawn when the screen refreshes besides the one view you're working on. (For example, the status bar clock.) Also, anything in drawRect: is not hardware accelerated. So some of that computational power is doing other stuff (background downloads, iCloud sync, etc.)

You'll never reliably get 60fps using drawRect:. You can try to improve performance using caching and some of Apple's other drawing tips. You might also get better performance using Core Animation or SceneKit, both of which are highly optimized.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
  • Thanks for the tips. Actually, I do reliably get 60fps with `drawRect:`, as long as it takes less than 4ms or so. What I don't get is why the `drawRect:` isn't called until 10ms after the display link fires (see my linked question). Are you saying that all that time is taken up drawing things like the status bar? That seems unlikely to me... – deltacrux Dec 16 '14 at 01:05
  • @deltacrux Well, it's not just drawing. Since there's no hardware acceleration for `drawRect:`, it's taking ~10ms for all other operations, not just drawing operations. – Aaron Brager Dec 16 '14 at 21:01
  • I'm certain that none of my code is running during that time. If those 10ms are being used for other operations (what would they be?), is there some way of using threads to allow my drawing code to be processed in parallel? I know that this can sometimes make a big difference to UI responsiveness (e.g. http://stackoverflow.com/questions/7551411/uiscrollview-blocks-run-loop), but I don't know much about how to implement multi-threading... – deltacrux Dec 16 '14 at 23:42
  • Possible other operations that may be happening in the background: reconnecting to a cell tower, downloading email or app updates, playing music, reindexing Spotlight, etc. etc. You can't do your drawing code on another thread because the graphics context is only valid until `drawRect:` returns. – Aaron Brager Dec 17 '14 at 00:00