How do I get what the current frame index is/will be in the Profiler?

Hello, I would like to test performance of a couple hundred prefabs in my project, and need to gather CPU and GPU time during runtime which I then use to calculate the averages per prefab…

I plan to do this using ProfilerDriver.GetRawFrameDataView(frameIndex, threadIndex), and save the frames where I start a specific prefab test and end it, to determine which frames should be included to calculate the average. But how do I get what frame the current one corresponds to in the Profiler?

For example, if I instantiate a certain prefab this frame I’d like to save the index this frame has in the Profiler in order to match it with the recorded data later, but using an int counter that increments by one every frame doesn’t seem to give the correct results.

I’m not sure I understand what you’re looking for exactly, but it seems to me that you are looking for the number of frames since startup?
https://docs.unity3d.com/ScriptReference/Time-frameCount.html

The problem is that when running the minimized code example below, even though the Special Frame is explicitly recorded on frame 5, it sometimes shows on frame 4 when double-checking the value in the Profiler window, and sometimes on frame 5. This is identified with the BeginSample-function:

Then, as we get the values of the previous frame and print them, we get the data for this frame on frame 6 in this case, although it also seems to vary for some reason:

7137893--853610--upload_2021-5-13_20-13-53.png

Hence, it seems like I’m not getting what the actual frame corresponds to in the Profiler.

using UnityEngine;
using UnityEditor.Profiling;
using UnityEditorInternal;
using UnityEngine.Profiling;


public class ExampleScript : MonoBehaviour
{
    private int FrameIndex = -1;

    void Start()
    {

    }

    void Update()
    {

        FrameIndex = ProfilerDriver.GetPreviousFrameIndex(Time.frameCount);
        RawFrameDataView frameData = ProfilerDriver.GetRawFrameDataView(FrameIndex, 0);

        if (frameData.valid)
        {
            UnityEngine.Debug.Log("Frame: " + (Time.frameCount - 1) + " CPU: " + frameData.frameTimeMs + " GPU: " + frameData.frameGpuTimeMs);
        }

        if (Time.frameCount == 5)
        {
            Profiler.BeginSample("TestSample");

            UnityEngine.Debug.Log("Special Frame: " + Time.frameCount);

            Profiler.EndSample();
        }

    }
}

Hi @MissFlip . Hoping I can offer some clarifications and advice on this for you.

When entering play-mode in the Editor, there are two Update ticks during the first Profiler frame. This could partly explain the behaviour that you describe and why it’s sometimes different (in a build, for example).

Here you appear to be printing the frame index as Time.frameCount - 1, yet you are asking the Profiler for data for the frame index GetPreviousFrameIndex(Time.frameCount). The Profiler data stream deserialization is an asynchronous process and as such there is no guarantee that GetPreviousFrameIndex(Time.frameCount) will be equal to Time.frameCount - 1. If the frame you asked for has not been processed yet, GetPreviousFrameIndex will return the index of the last processed frame. This might explain the behaviour that you describe.

Rather than trying to correlate frame indices, a more robust approach could be to use Profiler frame metadata. This allows you to write some metadata associated with the current frame to the Profiler data stream. For example, you could emit some flag on the frame at which you wish to start the measurement. Then when processing the data, you can scan through each frame and retrieve its metadata to find the flagged frame at which to begin summing the timing samples. You could extend this approach by adding further metadata if you need it, such as the end frame of the test, or the type of test the start flag applies to if you have many running in parallel. To emit frame metadata you use Profiler.EmitFrameMetaData and to retrieve it once processed you use FrameDataView.GetFrameMetaData.

It’s perhaps also worth mentioning the existence of ProfilerRecorder in Unity 2020+. The benefit of ProfilerRecorder is that you can check the values directly in your Player code without waiting for the data stream to be processed, as well as being able to capture this data in Release builds without the Profiler attached at all (as long as the counter/marker is present in release builds). The linked API documentation demonstrates using the “Main Thread” marker to retrieve timings directly in Player code, which you could use. There is also a “PlayerLoop” marker (using ProfilerRecorderHandle.GetAvailable to check the list) that would give you time spent in the Player loop specifically. However, right now I believe you’ll be limited to CPU on this; there has been considerable work in the 2021 cycle on the GPU profiling side so this may be a solution to investigate in a future Editor version (2021.2+) if you need GPU timings.

1 Like