Reputation: 4718
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.
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.
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)?
Upvotes: 1
Views: 485
Reputation: 4718
Another option is to use the POSIX clock_gettime()
function. POSIX functions are available for MacOS and by extension Metal games.
static struct timespec old;
// Init old once
struct timespec new;
if (clock_gettime(CLOCK_REALTIME, &new))
abort();
struct timespec delta = {new.tv_sec-old.tv_sec, new.tv_nsec-old.tv_nsec};
old = new;
Upvotes: 0
Reputation: 10137
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);
/*.....*/
}
Upvotes: 0
Reputation: 10137
From apple documentation:
You can use the command buffer’s GPUEndTime and GPUStartTime properties to calculate how much time the GPU spends running the command buffer:
__block dispatch_semaphore_t block_sema = _inFlightSemaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer)
{
CFTimeInterval start = buffer.GPUStartTime;
CFTimeInterval end = buffer.GPUEndTime;
CFTimeInterval gpuRuntimeDuration = end - start;
printf("%f \n", gpuRuntimeDuration);
dispatch_semaphore_signal(block_sema);
}];
Problem: The numbers increase at an irregular and slow rate (about 0.1 per second) instead of by a steady 1 per second.
Correct implementation:
clock_t c = clock();
/* .... */
printf("Time: %g sec.\n", (double)(clock() - (c)) / CLOCKS_PER_SEC);
Upvotes: 1