This repository has been archived by the owner on Oct 27, 2022. It is now read-only.
/
RenderTiming.cs
147 lines (126 loc) · 4.51 KB
/
RenderTiming.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using UnityEngine.Rendering;
using System.Collections;
using System.Runtime.InteropServices;
using System;
/// Attach me to an active scene object to measure GPU render time
///
/// GPU time is measured from LateUpdate to WaitForEndOfFrame. Late app rendering
/// done in WaitForEndOfFrame may not be accounted for, depending on script order.
///
/// TODO: support multiple & nested timings per frame
/// TODO: have plugin work with GLES2
/// TODO: support more platforms (iOS, Vulkan, desktop, etc.)
public class RenderTiming : MonoBehaviour {
public static RenderTiming instance;
/// True if rendering timing supported in this platform
/// TODO: test whether GL extention is available
public bool isSupported {
get { return GetStartTimingFunc() != IntPtr.Zero; }
}
/// Render period in seconds if available, else NaN. Reflects the value
/// from several frames prior, depending on the level of GPU buffering.
/// TODO: smoothDeltaTime
public float deltaTime { get { return GetTiming(); } }
/// True to periodically log timing to debug console. Honored only at init.
public bool logTiming = true;
#if UNITY_ANDROID && !UNITY_EDITOR
[DllImport ("RenderTimingPlugin")]
private static extern void SetDebugFunction(IntPtr ftp);
[DllImport ("RenderTimingPlugin")]
private static extern IntPtr GetStartTimingFunc();
[DllImport ("RenderTimingPlugin")]
private static extern IntPtr GetEndTimingFunc();
[DllImport ("RenderTimingPlugin")]
private static extern float GetTiming();
#else
private void SetDebugFunction(IntPtr ftp) {}
private IntPtr GetStartTimingFunc() { return IntPtr.Zero; }
private IntPtr GetEndTimingFunc() { return IntPtr.Zero; }
private float GetTiming() { return float.NaN; }
#endif
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void MyDelegate(string str);
private bool isInitialized;
private CommandBuffer commandBufferStart;
private CommandBuffer commandBufferEnd;
private Coroutine loggingCoroutine;
[AOT.MonoPInvokeCallback(typeof(MyDelegate))]
static void DebugCallback(string str) {
Debug.LogWarning("RenderTimingPlugin: " + str);
}
void Awake() {
Debug.Assert(instance == null, "only one instance allowed");
instance = this;
// Note: intentionally not accessing any plugin code here so that leaving the component
// disabled will avoid loading the plugin.
}
// called from first OnEnable()
void Init() {
MyDelegate callback_delegate = new MyDelegate(DebugCallback);
IntPtr intptr_delegate = Marshal.GetFunctionPointerForDelegate(callback_delegate);
SetDebugFunction(intptr_delegate);
// prepare timer start & stop commands-- see OnEnable()
commandBufferStart = new CommandBuffer();
commandBufferStart.name = "StartGpuTiming";
commandBufferStart.IssuePluginEvent(GetStartTimingFunc(), 0 /*unused*/);
commandBufferEnd = new CommandBuffer();
commandBufferEnd.name = "EndGpuTiming";
commandBufferEnd.IssuePluginEvent(GetEndTimingFunc(), 0 /*unused*/);
isInitialized = true;
}
void LateUpdate() {
GL.IssuePluginEvent(GetStartTimingFunc(), 0 /*unused*/);
}
IEnumerator FrameEnd() {
while (true) {
yield return new WaitForEndOfFrame();
if (enabled) {
GL.IssuePluginEvent(GetEndTimingFunc(), 0 /*unused*/);
} else {
break;
}
}
}
void OnEnable() {
if (!isSupported) {
enabled = false;
return;
}
if (!isInitialized) {
Init();
}
StartCoroutine(FrameEnd());
if (logTiming) {
loggingCoroutine = StartCoroutine(ConsoleDisplay());
}
}
void OnDisable() {
if (!isSupported) {
return;
}
if (loggingCoroutine != null) {
StopCoroutine(loggingCoroutine);
loggingCoroutine = null;
}
}
private IEnumerator ConsoleDisplay() {
while (true) {
yield return new WaitForSeconds(1);
Debug.LogFormat("Render time: {0:F3} ms", deltaTime * 1000);
}
}
}