/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2021. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
* between Ultraleap and you, your company or other organization. *
******************************************************************************/
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.Profiling;
namespace Leap.Unity
{
///
/// Utility class used by the LeapServiceProvider for profiling the LeapCSharp dll
///
public static class LeapProfiling
{
//Maps a block name to the sampler for it
private static Dictionary _samplers = new Dictionary();
//Represents a queue of samplers that need to be created.
//Samplers can only be created on the main thread, so we need some way for the info given
//on alternate threads to be passed to the main thread.
private static Queue _samplersToCreate = new Queue();
//We keep track of the size of the queue in a member variable
private static int _samplersToCreateCount = 0;
public static void Update()
{
//Read of _samplersToCreateCount is atomic
if (_samplersToCreateCount > 0)
{
//Only if the count is nonzero do we do an expensive lock
lock (_samplersToCreate)
{
//First duplicate the existing dictionary
var newDictionary = new Dictionary(_samplers);
//Then construct all of the new samplers and add them to the new dictionary
while (_samplersToCreate.Count > 0)
{
string blockName = _samplersToCreate.Dequeue();
newDictionary[blockName] = CustomSampler.Create(blockName);
}
//Reset samplers to create to zero
_samplersToCreateCount = 0;
//Reference assignments are atomic in C#
//All new callbacks will now reference the updated dictionary
//Old dictionary will be collected by GC
_samplers = newDictionary;
}
}
}
public static void BeginProfilingForThread(BeginProfilingForThreadArgs eventData)
{
#if UNITY_2017_3_OR_NEWER
//Enable unity profiling for this thread
Profiler.BeginThreadProfiling("LeapCSharp", eventData.threadName);
//Assume that threads are not stopping and starting frequently
//so we can get away with less-than-optimal strategies when starting a thread.
//in this case we use a naive queue with a lock.
lock (_samplersToCreate)
{
foreach (var blockName in eventData.blockNames)
{
_samplersToCreate.Enqueue(blockName);
}
Interlocked.Add(ref _samplersToCreateCount, eventData.blockNames.Length);
}
#else
Debug.LogWarning("Thread Profiling is unavailable in versions of Unity below 2017.3");
#endif
}
public static void EndProfilingForThread(EndProfilingForThreadArgs eventData)
{
#if UNITY_2017_3_OR_NEWER
Profiler.EndThreadProfiling();
#else
Debug.LogWarning("Thread Profiling is unavailable in versions of Unity below 2017.3");
#endif
}
public static void BeginProfilingBlock(BeginProfilingBlockArgs eventData)
{
//Sampler might not have been created yet because samplers can only be created
//on the main thread. We will simply not be able to report all blocks until
//a sampler is available.
//Note that the Dictionary type is thread safe for read operations
//Dictionary is only used once and so there is no risk of the dictionary
//being swapped out from underneath us
CustomSampler sampler;
if (_samplers.TryGetValue(eventData.blockName, out sampler))
{
sampler.Begin();
}
}
public static void EndProfilingBlock(EndProfilingBlockArgs eventData)
{
CustomSampler sampler;
if (_samplers.TryGetValue(eventData.blockName, out sampler))
{
sampler.End();
}
}
}
}