using System;
using System.Collections.Generic;
namespace UnityEngine.Rendering
{
///
/// Represents timing data captured from a single frame.
///
internal struct FrameTimeSample
{
internal float FramesPerSecond;
internal float FullFrameTime;
internal float MainThreadCPUFrameTime;
internal float MainThreadCPUPresentWaitTime;
internal float RenderThreadCPUFrameTime;
internal float GPUFrameTime;
internal FrameTimeSample(float initValue)
{
FramesPerSecond = initValue;
FullFrameTime = initValue;
MainThreadCPUFrameTime = initValue;
MainThreadCPUPresentWaitTime = initValue;
RenderThreadCPUFrameTime = initValue;
GPUFrameTime = initValue;
}
};
///
/// Container class for sample history with helpers to calculate min, max and average in one pass.
///
class FrameTimeSampleHistory
{
public FrameTimeSampleHistory(int initialCapacity)
{
m_Samples.Capacity = initialCapacity;
}
List m_Samples = new();
internal FrameTimeSample SampleAverage;
internal FrameTimeSample SampleMin;
internal FrameTimeSample SampleMax;
internal void Add(FrameTimeSample sample)
{
m_Samples.Add(sample);
}
// Helper functions
static Func s_SampleValueAdd = (float value, float other) =>
{
return value + other;
};
static Func s_SampleValueMin = (float value, float other) =>
{
return other > 0 ? Mathf.Min(value, other) : value;
};
static Func s_SampleValueMax = (float value, float other) =>
{
return Mathf.Max(value, other);
};
static Func s_SampleValueCountValid = (float value, float other) =>
{
return other > 0 ? value + 1 : value;
};
static Func s_SampleValueEnsureValid = (float value, float other) =>
{
return other > 0 ? value : 0;
};
static Func s_SampleValueDivide = (float value, float other) =>
{
return other > 0 ? value / other : 0;
};
internal void ComputeAggregateValues()
{
void ForEachSampleMember(ref FrameTimeSample aggregate, FrameTimeSample sample, Func func)
{
aggregate.FramesPerSecond = func(aggregate.FramesPerSecond, sample.FramesPerSecond);
aggregate.FullFrameTime = func(aggregate.FullFrameTime, sample.FullFrameTime);
aggregate.MainThreadCPUFrameTime = func(aggregate.MainThreadCPUFrameTime, sample.MainThreadCPUFrameTime);
aggregate.MainThreadCPUPresentWaitTime = func(aggregate.MainThreadCPUPresentWaitTime, sample.MainThreadCPUPresentWaitTime);
aggregate.RenderThreadCPUFrameTime = func(aggregate.RenderThreadCPUFrameTime, sample.RenderThreadCPUFrameTime);
aggregate.GPUFrameTime = func(aggregate.GPUFrameTime, sample.GPUFrameTime);
};
FrameTimeSample average = new();
FrameTimeSample min = new(float.MaxValue);
FrameTimeSample max = new(float.MinValue);
FrameTimeSample numValidSamples = new(); // Using the struct to record how many valid samples each field has
for (int i = 0; i < m_Samples.Count; i++)
{
var s = m_Samples[i];
ForEachSampleMember(ref min, s, s_SampleValueMin);
ForEachSampleMember(ref max, s, s_SampleValueMax);
ForEachSampleMember(ref average, s, s_SampleValueAdd);
ForEachSampleMember(ref numValidSamples, s, s_SampleValueCountValid);
}
ForEachSampleMember(ref min, numValidSamples, s_SampleValueEnsureValid);
ForEachSampleMember(ref max, numValidSamples, s_SampleValueEnsureValid);
ForEachSampleMember(ref average, numValidSamples, s_SampleValueDivide);
SampleAverage = average;
SampleMin = min;
SampleMax = max;
}
internal void DiscardOldSamples(int sampleHistorySize)
{
Debug.Assert(sampleHistorySize > 0, "Invalid sampleHistorySize");
while (m_Samples.Count >= sampleHistorySize)
m_Samples.RemoveAt(0);
m_Samples.Capacity = sampleHistorySize;
}
internal void Clear()
{
m_Samples.Clear();
}
}
}