View difference between Paste ID: VWcPUba1 and pq1BfY49
SHOW: | | - or go back to the newest paste.
1
using UnityEditor;
2
using UnityEngine;
3
using UnityEngine.Profiling;
4
using System.Collections.Generic;
5
using System.Linq;
6
using UnityEngine.Rendering;
7
8
public class GpuTimingWindow : EditorWindow
9
{
10
    private const int SampleCount = 1;
11
    private FrameTiming[] frameTimings = new FrameTiming[SampleCount];
12
    private double gpuFrameTime;
13
    private double cpuFrameTime;
14
15
    private const int HistorySize = 50;
16
    private Queue<float> cpuHistory = new Queue<float>(HistorySize);
17
    private Queue<float> gpuHistory = new Queue<float>(HistorySize);
18
19
    private Shader shaderToProfile;
20
    private CustomSampler shaderSampler;
21
22
    [MenuItem("Window/Analysis/GPU Timing Viewer")]
23
    public static void ShowWindow()
24
    {
25
        var window = GetWindow<GpuTimingWindow>("GPU Timing");
26
        window.Show();
27
    }
28
29
    private void OnEnable()
30
    {
31
        EditorApplication.update += UpdateTimings;
32
    }
33
34
    private void OnDisable()
35
    {
36
        EditorApplication.update -= UpdateTimings;
37
    }
38
39
    private void UpdateTimings()
40
    {
41
        // Capture frame timings from the last frame
42
        FrameTimingManager.CaptureFrameTimings();
43
        uint count = FrameTimingManager.GetLatestTimings((uint)frameTimings.Length, frameTimings);
44
45
        if (count > 0)
46
        {
47
            var timing = frameTimings[0];
48
            gpuFrameTime = timing.gpuFrameTime;
49
            cpuFrameTime = timing.cpuFrameTime;
50
51
            AddToHistory(cpuHistory, (float)cpuFrameTime);
52
            AddToHistory(gpuHistory, (float)gpuFrameTime);
53
54
            Repaint();
55
        }
56
    }
57
58
    private void AddToHistory(Queue<float> queue, float value)
59
    {
60
        if (queue.Count >= HistorySize)
61
            queue.Dequeue();
62
        queue.Enqueue(value);
63
    }
64
65
    private float GetAverage(Queue<float> queue)
66
    {
67
        if (queue.Count == 0) return 0f;
68
        return queue.Average();
69
    }
70
71
    private void OnGUI()
72
    {
73
        GUILayout.Label("GPU & CPU Frame Timing", EditorStyles.boldLabel);
74
75
        EditorGUILayout.LabelField("CPU Frame Time (ms):", cpuFrameTime.ToString("F2"));
76
        EditorGUILayout.LabelField("GPU Frame Time (ms):", gpuFrameTime.ToString("F2"));
77
78
        float avgCpu = GetAverage(cpuHistory);
79
        float avgGpu = GetAverage(gpuHistory);
80
81
        EditorGUILayout.Space();
82
        EditorGUILayout.LabelField("CPU Avg (200 frames, ms):", avgCpu.ToString("F2"));
83
        EditorGUILayout.LabelField("GPU Avg (200 frames, ms):", avgGpu.ToString("F2"));
84
85
        float targetFrameRate = 1000f / Mathf.Max(0.0001f, avgGpu);
86
        EditorGUILayout.LabelField("Approx. Avg GPU FPS:", targetFrameRate.ToString("F1"));
87
88
        GUILayout.Space(10);
89
        Rect rect = GUILayoutUtility.GetRect(100, 30);
90
        DrawTimingBar(rect, (float)cpuFrameTime, (float)gpuFrameTime);
91
92
        GUILayout.Space(20);
93
        GUILayout.Label("Frame Timing History (last 200 frames)", EditorStyles.boldLabel);
94
        Rect graphRect = GUILayoutUtility.GetRect(position.width - 20, 100);
95
        DrawGraph(graphRect, cpuHistory, gpuHistory);
96
    }
97
98
    private void DrawTimingBar(Rect rect, float cpuMs, float gpuMs)
99
    {
100
        float maxMs = Mathf.Max(cpuMs, gpuMs, 16.67f); // baseline at ~60 FPS
101
        float cpuWidth = Mathf.Clamp01(cpuMs / maxMs) * rect.width;
102
        float gpuWidth = Mathf.Clamp01(gpuMs / maxMs) * rect.width;
103
104
        EditorGUI.DrawRect(new Rect(rect.x, rect.y, cpuWidth, rect.height / 2), new Color(0.3f, 0.6f, 1f));
105
        EditorGUI.DrawRect(new Rect(rect.x, rect.y + rect.height / 2, gpuWidth, rect.height / 2), new Color(1f, 0.4f, 0.4f));
106
107
        EditorGUI.DropShadowLabel(rect, $"CPU {cpuMs:F2} ms | GPU {gpuMs:F2} ms");
108
    }
109
110
    private void DrawGraph(Rect rect, Queue<float> cpuData, Queue<float> gpuData)
111
    {
112
        Handles.BeginGUI();
113
        GUI.Box(rect, GUIContent.none);
114
115
        if (cpuData.Count > 1 && gpuData.Count > 1)
116
        {
117
            float[] cpuArray = cpuData.ToArray();
118
            float[] gpuArray = gpuData.ToArray();
119
            int count = Mathf.Min(cpuArray.Length, gpuArray.Length);
120
121
            float maxVal = 0f;
122
            for (int i = 0; i < count; i++)
123
                maxVal = Mathf.Max(maxVal, Mathf.Max(cpuArray[i], gpuArray[i]));
124
125
            if (maxVal < 16.67f)
126
                maxVal = 16.67f; // ensure ~60 fps baseline is visible
127
128
            Vector3 prevCpu = Vector3.zero;
129
            Vector3 prevGpu = Vector3.zero;
130
            for (int i = 0; i < count; i++)
131
            {
132
                float x = Mathf.Lerp(rect.x, rect.xMax, i / (float)(count - 1));
133
                float cpuY = Mathf.Lerp(rect.yMax, rect.y, cpuArray[i] / maxVal);
134
                float gpuY = Mathf.Lerp(rect.yMax, rect.y, gpuArray[i] / maxVal);
135
136
                Vector3 cpuPoint = new Vector3(x, cpuY, 0);
137
                Vector3 gpuPoint = new Vector3(x, gpuY, 0);
138
139
                if (i > 0)
140
                {
141
                    Handles.color = new Color(0.3f, 0.6f, 1f);
142
                    Handles.DrawLine(prevCpu, cpuPoint);
143
144
                    Handles.color = new Color(1f, 0.4f, 0.4f);
145
                    Handles.DrawLine(prevGpu, gpuPoint);
146
                }
147
148
                prevCpu = cpuPoint;
149
                prevGpu = gpuPoint;
150
            }
151
        }
152
153
        Handles.EndGUI();
154
    }
155
}
156