Skip to content

Instantly share code, notes, and snippets.

@MerlinVR
Last active November 20, 2022 11:08
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MerlinVR/2da80b29361588ddb556fd8d3f3f47b5 to your computer and use it in GitHub Desktop.
Save MerlinVR/2da80b29361588ddb556fd8d3f3f47b5 to your computer and use it in GitHub Desktop.
Basic global profiling scripts for Udon. Throw one of each script on a game object that has a TMP UI text somewhere in its children.
// MIT License
// Copyright (c) 2021 Merlin
using UdonSharp;
using UnityEngine;
[DefaultExecutionOrder(1000000000)]
public class GlobalProfileHandler : UdonSharpBehaviour
{
TMPro.TextMeshProUGUI timeText;
GlobalProfileKickoff kickoff;
private void Start()
{
kickoff = GetComponent<GlobalProfileKickoff>();
timeText = GetComponentInChildren<TMPro.TextMeshProUGUI>();
}
int currentFrame = -1;
float elapsedTime = 0f;
private void FixedUpdate()
{
if (currentFrame != Time.frameCount)
{
elapsedTime = 0f;
currentFrame = Time.frameCount;
}
if (kickoff)
elapsedTime += (float)kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
}
private void Update()
{
if (currentFrame != Time.frameCount) // FixedUpdate didn't run this frame, so reset the time
elapsedTime = 0f;
elapsedTime += (float)kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
}
private void LateUpdate()
{
elapsedTime += (float)kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
timeText.text = $"Update time:\n{elapsedTime:F4}ms";
}
}
// MIT License
// Copyright (c) 2021 Merlin
using UdonSharp;
using UnityEngine;
[DefaultExecutionOrder(-1000000000)]
public class GlobalProfileKickoff : UdonSharpBehaviour
{
[System.NonSerialized]
public System.Diagnostics.Stopwatch stopwatch;
private void Start()
{
stopwatch = new System.Diagnostics.Stopwatch();
}
private void FixedUpdate()
{
stopwatch.Restart();
}
private void Update()
{
stopwatch.Restart();
}
private void LateUpdate()
{
stopwatch.Restart();
}
}
@Reimajo
Copy link

Reimajo commented Feb 22, 2021

Here is my version of GlobalProfileHandler.cs that averages the output, so that the frametime is more readable - updating a text field 90 times a second isn't easy to read after all :) Also I switched to Unity.UI.Text since I couldn't get TMP to work, maybe a prefab could be added to this here. You can simply switch to direct ouput on demand by uncommenting the first line.

#define AVERAGE_OUTPUT
using UdonSharp;
using UnityEngine;

[DefaultExecutionOrder(1000000000)]
public class GlobalProfileHandler : UdonSharpBehaviour
{
    public UnityEngine.UI.Text _timeText;
    private GlobalProfileKickoff _kickoff;

    private void Start()
    {
        _kickoff = GetComponent<GlobalProfileKickoff>();
    }

    private int _currentFrame = -1;
    private float _elapsedTime = 0f;
#if AVERAGE_OUTPUT
    private float _measuredTimeTotal = 0f;
    private int _measuredTimeFrameCount = 0;
    private const int MEASURE_FRAME_AMOUNT = 45;
#endif

    private void FixedUpdate()
    {
        if (_currentFrame != Time.frameCount)
        {
            _elapsedTime = 0f;
            _currentFrame = Time.frameCount;
        }

        if (_kickoff)
            _elapsedTime += (float)_kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
    }

    private void Update()
    {
        if (_currentFrame != Time.frameCount) // FixedUpdate didn't run this frame, so reset the time
            _elapsedTime = 0f;

        _elapsedTime += (float)_kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
    }

    private void LateUpdate()
    {
        _elapsedTime += (float)_kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
#if AVERAGE_OUTPUT
        if (_measuredTimeFrameCount >= MEASURE_FRAME_AMOUNT)
        {
            _timeText.text = $"{(_measuredTimeTotal / _measuredTimeFrameCount):F4}ms";
            _measuredTimeTotal = 0f;
            _measuredTimeFrameCount = 0;
        }
        _measuredTimeTotal += _elapsedTime;
        _measuredTimeFrameCount += 1;
#else
        _timeText.text = $"Update time:\n{_elapsedTime:F4}ms";
#endif
    }
}

@bdunderscore
Copy link

Here's an update with PostLateUpdate support as well:

// GlobalProfileHandler.cs
#define AVERAGE_OUTPUT

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using UdonSharp;
using UnityEngine;

[DefaultExecutionOrder(1000000000)]
public class GlobalProfileHandler : UdonSharpBehaviour
{
    public TMPro.TextMeshPro _timeText;
    private GlobalProfileKickoff _kickoff;

    private void Start()
    {
        _kickoff = GetComponent<GlobalProfileKickoff>();
    }

    private int _currentFrame = -1;
    private float _elapsedTime = 0f;
#if AVERAGE_OUTPUT
    private float _measuredFrametimeTotal = 0f;
    private float _measuredTimeTotal = 0f;
    private int _measuredTimeFrameCount = 0;
    private const int MEASURE_FRAME_AMOUNT = 45;
#endif

    private void FixedUpdate()
    {
        if (_currentFrame != Time.frameCount)
        {
            _elapsedTime = 0f;
            _currentFrame = Time.frameCount;
        }

        if (_kickoff)
            _elapsedTime += (float)_kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
    }

    private void Update()
    {
        if (_currentFrame != Time.frameCount) // FixedUpdate didn't run this frame, so reset the time
            _elapsedTime = 0f;

        _elapsedTime += (float)_kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
    }

    private void LateUpdate()
    {
        _elapsedTime += (float)_kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
    }

    float lastFrame;

    public override void PostLateUpdate()
    {
        _elapsedTime += (float)_kickoff.stopwatch.Elapsed.TotalSeconds * 1000f;
        float now = Time.timeSinceLevelLoad;
        _measuredFrametimeTotal += (now - lastFrame) * 1000f;
        lastFrame = now;

#if AVERAGE_OUTPUT
        if (_measuredTimeFrameCount >= MEASURE_FRAME_AMOUNT)
        {
            _timeText.text = $"Udon: {(_measuredTimeTotal / _measuredTimeFrameCount):F4}ms\nFrametime: {(_measuredFrametimeTotal/_measuredTimeFrameCount):F4}ms";
            _measuredTimeTotal = 0f;
            _measuredFrametimeTotal = 0f;
            _measuredTimeFrameCount = 0;
        }
        _measuredTimeTotal += _elapsedTime;
        _measuredTimeFrameCount += 1;
#else
        _timeText.text = $"Update time:\n{_elapsedTime:F4}ms";
#endif
    }
}

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

// MIT License
// Copyright (c) 2021 Merlin

using UdonSharp;
using UnityEngine;

[DefaultExecutionOrder(-1000000000)]
public class GlobalProfileKickoff : UdonSharpBehaviour
{
    [System.NonSerialized]
    public System.Diagnostics.Stopwatch stopwatch;

    private void Start()
    {
        stopwatch = new System.Diagnostics.Stopwatch();
    }

    private void FixedUpdate()
    {
        stopwatch.Restart();
    }

    private void Update()
    {
        stopwatch.Restart();
    }

    private void LateUpdate()
    {
        stopwatch.Restart();
    }

    public override void PostLateUpdate()
    {
        stopwatch.Restart();
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment