Skip to content

Instantly share code, notes, and snippets.

@apkd
Created October 20, 2019 19:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apkd/b0c311ad42ac76f9e75355fa6918eafc to your computer and use it in GitHub Desktop.
Save apkd/b0c311ad42ac76f9e75355fa6918eafc to your computer and use it in GitHub Desktop.
using System.Diagnostics.CodeAnalysis;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
namespace UnityEngine.Experimental.Rendering
{
public sealed class BatchingInfo : MonoBehaviour
{
public bool Enable = true;
const float AverageStatDuration = 1.0f; // stats refresh each second
int frameCount;
float accDeltaTime;
string statsLabel;
GUIStyle style;
bool oldBatcherEnable;
sealed class RecorderEntry
{
public string name;
public string oldName;
public int callCount;
public float accTime;
public Recorder recorder;
};
enum SRPBMarkers
{
kStdRenderDraw,
kStdShadowDraw,
kSRPBRenderDraw,
kSRPBShadowDraw,
kRenderThreadIdle,
kStdRenderApplyShader,
kStdShadowApplyShader,
kSRPBRenderApplyShader,
kSRPBShadowApplyShader,
kPrepareBatchRendererGroupNodes,
};
readonly RecorderEntry[] recordersList =
{
// Warning: Keep that list in the exact same order than SRPBMarkers enum
new RecorderEntry() {name = "RenderLoop.Draw"},
new RecorderEntry() {name = "Shadows.Draw"},
new RecorderEntry() {name = "SRPBatcher.Draw", oldName = "RenderLoopNewBatcher.Draw"},
new RecorderEntry() {name = "SRPBatcherShadow.Draw", oldName = "ShadowLoopNewBatcher.Draw"},
new RecorderEntry() {name = "RenderLoopDevice.Idle"},
new RecorderEntry() {name = "StdRender.ApplyShader"},
new RecorderEntry() {name = "StdShadow.ApplyShader"},
new RecorderEntry() {name = "SRPBRender.ApplyShader"},
new RecorderEntry() {name = "SRPBShadow.ApplyShader"},
new RecorderEntry() {name = "PrepareBatchRendererGroupNodes"},
};
void Awake()
{
foreach (var recorder in recordersList)
{
var sampler = Sampler.Get(recorder.name);
if (sampler.isValid)
{
recorder.recorder = sampler.GetRecorder();
}
else if (recorder.oldName != null)
{
sampler = Sampler.Get(recorder.oldName);
if (sampler.isValid)
recorder.recorder = sampler.GetRecorder();
}
}
style = new GUIStyle {fontSize = 15, normal = {textColor = Color.white}};
oldBatcherEnable = Enable;
ResetStats();
}
void RazCounters()
{
accDeltaTime = 0.0f;
frameCount = 0;
foreach (var recorder in recordersList)
{
recorder.accTime = 0.0f;
recorder.callCount = 0;
}
}
void ResetStats()
{
statsLabel = "Gathering data...";
RazCounters();
}
void ToggleStats()
{
Enable = !Enable;
ResetStats();
}
[SuppressMessage("ReSharper", "HeapView.BoxingAllocation")]
void Update()
{
if (Input.GetKeyDown(KeyCode.F9))
GraphicsSettings.useScriptableRenderPipelineBatching = !GraphicsSettings.useScriptableRenderPipelineBatching;
if (GraphicsSettings.useScriptableRenderPipelineBatching != oldBatcherEnable)
{
ResetStats();
oldBatcherEnable = GraphicsSettings.useScriptableRenderPipelineBatching;
}
if (Input.GetKeyDown(KeyCode.F8))
ToggleStats();
if (!Enable)
return;
bool SRPBatcher = GraphicsSettings.useScriptableRenderPipelineBatching;
accDeltaTime += Time.unscaledDeltaTime;
frameCount++;
// get timing & update average accumulators
foreach (var recorder in recordersList)
{
if (recorder.recorder == null)
continue;
recorder.accTime += recorder.recorder.elapsedNanoseconds / 1000000.0f; // acc time in ms
recorder.callCount += recorder.recorder.sampleBlockCount;
}
if (accDeltaTime < AverageStatDuration)
return;
float ooFrameCount = 1.0f / frameCount;
float avgStdRender = recordersList[(int)SRPBMarkers.kStdRenderDraw].accTime * ooFrameCount;
float avgStdShadow = recordersList[(int)SRPBMarkers.kStdShadowDraw].accTime * ooFrameCount;
float avgSRPBRender = recordersList[(int)SRPBMarkers.kSRPBRenderDraw].accTime * ooFrameCount;
float avgSRPBShadow = recordersList[(int)SRPBMarkers.kSRPBShadowDraw].accTime * ooFrameCount;
float RTIdleTime = recordersList[(int)SRPBMarkers.kRenderThreadIdle].accTime * ooFrameCount;
float avgPIRPrepareGroupNodes = recordersList[(int)SRPBMarkers.kPrepareBatchRendererGroupNodes].accTime * ooFrameCount;
statsLabel = $"Accumulated time for RenderLoop.Draw and ShadowLoop.Draw (all threads)\n{avgStdRender + avgStdShadow + avgSRPBRender + avgSRPBShadow + avgPIRPrepareGroupNodes:F2}ms CPU Rendering time ( incl {RTIdleTime:F2}ms RT idle )\n";
if (SRPBatcher)
{
statsLabel += $" {avgSRPBRender + avgSRPBShadow:F2}ms SRP Batcher code path\n";
statsLabel += $" {avgSRPBRender:F2}ms All objects ( {recordersList[(int)SRPBMarkers.kSRPBRenderApplyShader].callCount / frameCount} ApplyShader calls )\n";
statsLabel += $" {avgSRPBShadow:F2}ms Shadows ( {recordersList[(int)SRPBMarkers.kSRPBShadowApplyShader].callCount / frameCount} ApplyShader calls )\n";
}
statsLabel += $" {avgStdRender + avgStdShadow:F2}ms Standard code path\n";
statsLabel += $" {avgStdRender:F2}ms All objects ( {recordersList[(int)SRPBMarkers.kStdRenderApplyShader].callCount / frameCount} ApplyShader calls )\n";
statsLabel += $" {avgStdShadow:F2}ms Shadows ( {recordersList[(int)SRPBMarkers.kStdShadowApplyShader].callCount / frameCount} ApplyShader calls )\n";
statsLabel += $" {avgPIRPrepareGroupNodes:F2}ms PIR Prepare Group Nodes ( {recordersList[(int)SRPBMarkers.kPrepareBatchRendererGroupNodes].callCount / frameCount} calls )\n";
statsLabel += $"Global Main Loop: {accDeltaTime * 1000.0f * ooFrameCount:F2}ms ({(int)(((float)frameCount) / accDeltaTime)} FPS)\n";
RazCounters();
}
void OnGUI()
{
if (!Enable)
return;
bool SRPBatcher = GraphicsSettings.useScriptableRenderPipelineBatching;
GUI.color = new Color(1, 1, 1, 1);
GUILayout.BeginArea(new Rect(32, 50, 700, 256), SRPBatcher ? "SRP batcher ON (F9)" : "SRP batcher OFF (F9)", GUI.skin.window);
GUILayout.Label(statsLabel, style);
GUILayout.EndArea();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment