Search code examples
timemetaltimedeltatimestepmtkview

How to calculate the frame timestep/deltatime in an MTKView?


I want to get the time since the last frame (known as the timestep or deltatime) in the draw method of MTKView. I expect to see values of approximately 0.017 assuming 60 frames per second consistently. The value should rise to about 0.03 if unexpected slowdowns are causing the view to render at only 30 frames per second. I tried several ways.

1/view.preferredFramesPerSecond:

This method is undesirable because it assumes that all frames will execute in the preferred time. If there are slowdowns, the timestep will be inaccurate.

C clock() function:

I noticed that the deltatime is off by about a factor of 10 if I attempt to use clock() in the draw method in an MTKView.

Minimal Reproducible Example:

Create a new 'Metal' game template in Xcode. The one that should render a spinning multicolored cube. Then, in the draw method for per frame updates, add the line:

// Objective-C
printf("%f\n", (double)clock()/CLOCKS_PER_SEC);
// Swift
print(Double(clock())/Double(CLOCKS_PER_SEC))

Build and observe the printed numbers and the console.

The numbers increase at an irregular and slow rate (about 0.1 per second) instead of by a steady 1 per second.

I also tried to store the previous result in a variable then subtract from the current result and divide by CLOCKS_PER_SEC. I see incorrect deltatime values of around 0.001.

Apparently this is because clock() gets the CPU time used and not the real time. The clock() function is not suitable for this.

buffer.gpuEndTime-buffer.gpuStartTime:

This approach has similar issues as the clock() approach but worse. I see the deltatime drop to as low as about 1.0e-5 (100 microseconds) when no geometry is being rendered. This is several orders of magnitude off.

I also tried buffer.kernelStartTime instead of buffer.gpuStartTime to no avail.

This approach also requires MacOS 10.15 or newer. This is undesirable as the code should be as backwards compatible as possible.

Research:

I could find no relevant results about the use of clock() together with a Metal game, or about how to get any timestep or deltatime in Metal or an MTKView.


How to simply get the deltatime of a frame in Metal (e.g., for accurate physics simulation)?


Solution

  • I want to get the time since the last frame (known as the timestep or deltatime) in the draw method of MTKView. I expect to see values of approximately 0.017 assuming 60 frames per second consistently. The value should rise to about 0.03 if unexpected slowdowns are causing the view to render at only 30 frames per second. I tried several ways.

    In your class implementation declare two CFAbsoluteTime type variables:

    @implementation Renderer {
    
        /*.....*/
        CFAbsoluteTime _deltaTime;
        CFAbsoluteTime _currentFrameTime;
    }
    

    Then update your MTKView draw method like so:

    - (void)drawInMTKView:(MTKView *)view {
        CFAbsoluteTime currentTime = CACurrentMediaTime();
        _deltaTime          = currentTime - _currentFrameTime;
        _currentFrameTime   = currentTime;
        NSLog(@"%f", _deltaTime);
    
        /*.....*/
    }