- Home /
Real elapsed time since frame begin
Hello,
I have a number of things to do every frame. I don't have to do all of them in a single frame. However I have to do as many of them as possible while maintaining 60 FPS.
In order to do this I do the following :
float targetFps = 60f;
float maxFrameDuration = 1f / targetFps;
float beginTime = Time.realtimeSinceStartup;
while (there is work to be done)
{
float iterBeginTime = Time.realtimeSinceStartup;
// Perform one iteration of work.
float currentTime = Time.realtimeSinceStartup;
float totalDuration = currentTime - beginTime;
float iterDuration = iterBeginTime - currentTime;
if (totalDuration + iterDuration > maxFrameDuration)
{
break;
}
}
The problem with this is that it doesn't take into account the frame time that was already consumed by other scripts BEFORE the current update function was called. In other words, if this is all my game had to do, this code would allow maintaining the target FPS, but since the game is also doing other things that this code is not aware about, it results in a lower FPS than the targeted FPS.
So my question is : How do I know how much frame time has already been consumed for the current frame ?
Also as I'm writing these lines another problem comes to mind : even if i manage to know how much time has been consumed BEFORE the above code is called, I don't know if Unity isn't going to add more work AFTER it for the current frame, in which case it would be too late to suspend the work. Any thoughts on this ?
Thanks for your help ! :)
You can use system time with something like System.DateTime.Now, but even that will have a polling interval that might yield the same time over short spans.
As for your goal, if you have a long operation that needs to be done, then you are probably better off just using threads.
Thanks for your comment. I know how to measure time. There is also the Stopwatch to do it accurately. But what I would like to know is the time that was used by previously called scripts, so that I can compute the time I have left. I am already using threads. In fact i'm coding a $$anonymous$$inecraft-like voxel terrain. Unfortunately, Unity doesn't allow to do much beyond basic maths in threads. That's why I'm preparing the data of the meshes into threads and assigning it to the $$anonymous$$esh objects within the main thread. $$anonymous$$y code is just polling for data ready to be assigned into a $$anonymous$$esh object.
Answer by TreyH · May 13, 2018 at 12:14 PM
You can accomplish this pretty easily using script execution order and the System.Diagnostics.Stopwatch, but note that you'll have to set up a similar system to catch LateUpdate duration and I don't know if the Stopwatch works on mobile / web platforms. There isn't much to comment:
using System.Diagnostics;
using UnityEngine;
public class FrameTimer : MonoBehaviour
{
private Stopwatch stopwatch;
public long FrameDuration
{
get
{
if (this.stopwatch == null)
return 0;
else
return this.stopwatch.ElapsedMilliseconds;
}
}
void Awake()
{
this.stopwatch = new Stopwatch();
}
void Update()
{
// For whatever reason, .Restart() wasn't recognized.
this.stopwatch.Reset();
this.stopwatch.Start();
}
}
Set this to run before anything else in your project's Script Execution Order setting:
You can then test it with this:
using UnityEngine;
public class RandomLongOperation : MonoBehaviour
{
// Component listed above
public FrameTimer frameTimer;
// Test with the inspector slider
[Range(0, 1000)] public int durationLimit = 15;
void Update()
{
while (true)
{
if (this.frameTimer.FrameDuration >= this.durationLimit)
break;
}
Debug.LogFormat("Broke! Frame lasted ~{0} ms", this.frameTimer.FrameDuration);
}
}
The LateUpdate modification is pretty straightforward.
Also this sort of goes without saying, but any script where you intend to use that FrameDuration property should be at the end of your Script Execution Order.
Great thanks ! I didn't know about this script reorder thing.