SCNView frame rate

There’s no API to get any kind of performance information from an SCNView, even as basic as a rolling frame rate.  But it’s not hard to add – you can be notified when each frame is drawn by overriding the -draw method.  For example:

NSDate *nextFrameCounterReset;
NSUInteger frameCount;

- (void)draw {
  NSDate *now = [NSDate date];

  if (nextFrameCounterReset) {
    if (NSOrderedDescending == [now compare:nextFrameCounterReset]) {
      [frameRateView setLabel:frameCount];
      frameCount = 0;
      nextFrameCounterReset = [now dateByAddingTimeInterval:1.0];
    }
  } else {
    nextFrameCounterReset = [now dateByAddingTimeInterval:1.0];
  }

  ++frameCount;

  [super draw];
}

Update:
However, if you’re layer-backed the “draw” method isn’t used. Instead you can override:

- (void)drawInBackingLayerWithCGLContext:(CGLContextObj)arg1;

Like so:

NSDate *nextFrameCounterReset;
NSUInteger frameCount;

- (void)drawInBackingLayerWithCGLContext:(CGLContextObj)arg1 {
  NSDate *now = [NSDate date];

  if (nextFrameCounterReset) {
    if (NSOrderedDescending == [now compare:nextFrameCounterReset]) {
      [frameRateView setLabel:frameCount];
      frameCount = 0;
      nextFrameCounterReset = [now dateByAddingTimeInterval:1.0];
    }
  } else {
    nextFrameCounterReset = [now dateByAddingTimeInterval:1.0];
  }

  ++frameCount;

  [super draw];
}

But don’t. Because in actual fact you can get this information via supported API – the SCNSceneRendererDelegate protocol.  You can register your view as its own delegate, or put your frame rate calculation in another object.  Either way, your code should look something like:

NSDate *nextFrameCounterReset;
NSUInteger frameCount;

- (void)renderer:(id<SCNSceneRenderer>)aRenderer didRenderScene:(SCNScene*)scene atTime:(NSTimeInterval)time {
  NSDate *now = [NSDate date];

  if (nextFrameCounterReset) {
    if (NSOrderedDescending == [now compare:nextFrameCounterReset]) {
      [frameRateView setLabel:frameCount];
      frameCount = 0;
      nextFrameCounterReset = [now dateByAddingTimeInterval:1.0];
    }
  } else {
    nextFrameCounterReset = [now dateByAddingTimeInterval:1.0];
  }

  ++frameCount;

  [super draw];
}

Leave a Reply