rev |
line source |
nuclear@0
|
1 /************************************************************************************
|
nuclear@0
|
2
|
nuclear@0
|
3 Filename : CAPI_LatencyStatistics.cpp
|
nuclear@0
|
4 Content : Record latency statistics during rendering
|
nuclear@0
|
5 Created : Sept 23, 2014
|
nuclear@0
|
6 Authors : Chris Taylor, Kevin Jenkins
|
nuclear@0
|
7
|
nuclear@0
|
8 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
|
nuclear@0
|
9
|
nuclear@0
|
10 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
|
nuclear@0
|
11 you may not use the Oculus VR Rift SDK except in compliance with the License,
|
nuclear@0
|
12 which is provided at the time of installation or download, or which
|
nuclear@0
|
13 otherwise accompanies this software in either electronic or hard copy form.
|
nuclear@0
|
14
|
nuclear@0
|
15 You may obtain a copy of the License at
|
nuclear@0
|
16
|
nuclear@0
|
17 http://www.oculusvr.com/licenses/LICENSE-3.2
|
nuclear@0
|
18
|
nuclear@0
|
19 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
|
nuclear@0
|
20 distributed under the License is distributed on an "AS IS" BASIS,
|
nuclear@0
|
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
nuclear@0
|
22 See the License for the specific language governing permissions and
|
nuclear@0
|
23 limitations under the License.
|
nuclear@0
|
24
|
nuclear@0
|
25 ************************************************************************************/
|
nuclear@0
|
26
|
nuclear@0
|
27 #include "CAPI_LatencyStatistics.h"
|
nuclear@0
|
28
|
nuclear@0
|
29 #include "../Kernel/OVR_Log.h"
|
nuclear@0
|
30 #include "../Kernel/OVR_Threads.h"
|
nuclear@0
|
31 #include "../Util/Util_SystemInfo.h"
|
nuclear@0
|
32
|
nuclear@0
|
33 namespace OVR { namespace CAPI {
|
nuclear@0
|
34
|
nuclear@0
|
35 //-----------------------------------------------------------------------------
|
nuclear@0
|
36 // ***** LatencyStatisticsObserver
|
nuclear@0
|
37
|
nuclear@0
|
38 LatencyStatisticsCSV::LatencyStatisticsCSV()
|
nuclear@0
|
39 {
|
nuclear@0
|
40 }
|
nuclear@0
|
41
|
nuclear@0
|
42 LatencyStatisticsCSV::~LatencyStatisticsCSV()
|
nuclear@0
|
43 {
|
nuclear@0
|
44 Stop();
|
nuclear@0
|
45 }
|
nuclear@0
|
46
|
nuclear@0
|
47 bool LatencyStatisticsCSV::Start(String fileName, String userData1)
|
nuclear@0
|
48 {
|
nuclear@0
|
49 if (_File.IsValid())
|
nuclear@0
|
50 {
|
nuclear@0
|
51 if (fileName == FileName)
|
nuclear@0
|
52 {
|
nuclear@0
|
53 UserData1 = userData1;
|
nuclear@0
|
54 return true;
|
nuclear@0
|
55 }
|
nuclear@0
|
56 else
|
nuclear@0
|
57 {
|
nuclear@0
|
58 Stop();
|
nuclear@0
|
59 }
|
nuclear@0
|
60 }
|
nuclear@0
|
61
|
nuclear@0
|
62 OVR::String basePath = OVR::GetBaseOVRPath(true);
|
nuclear@0
|
63 OVR::String path = basePath;
|
nuclear@0
|
64 path.AppendString("\\");
|
nuclear@0
|
65 path.AppendString(fileName);
|
nuclear@0
|
66 OS = OVR::Util::OSAsString();
|
nuclear@0
|
67 OSVersion = OVR::Util::OSVersionAsString();
|
nuclear@0
|
68 #if defined (OVR_OS_WIN64) || defined (OVR_OS_WIN32)
|
nuclear@0
|
69 ProcessInfo = OVR::Util::GetProcessInfo();
|
nuclear@0
|
70 DisplayDriverVersion = OVR::Util::GetDisplayDriverVersion();
|
nuclear@0
|
71 CameraDriverVersion = OVR::Util::GetCameraDriverVersion();
|
nuclear@0
|
72 OVR::Array< OVR::String > gpus;
|
nuclear@0
|
73 OVR::Util::GetGraphicsCardList(gpus);
|
nuclear@0
|
74 StringBuffer sb;
|
nuclear@0
|
75 size_t gpuIdx;
|
nuclear@0
|
76 for (gpuIdx = 0; gpuIdx < gpus.GetSize(); gpuIdx++)
|
nuclear@0
|
77 {
|
nuclear@0
|
78 sb.AppendString(gpus[gpuIdx]);
|
nuclear@0
|
79 if (gpuIdx + 1 < gpus.GetSize())
|
nuclear@0
|
80 sb.AppendString("; ");
|
nuclear@0
|
81 }
|
nuclear@0
|
82 GPUVersion = sb;
|
nuclear@0
|
83 #endif
|
nuclear@0
|
84 Guid = OVR::Util::GetGuidString();
|
nuclear@0
|
85
|
nuclear@0
|
86 if (!_File.Open(path, OVR::File::Open_Write, OVR::File::Mode_Write))
|
nuclear@0
|
87 {
|
nuclear@0
|
88 _File.Create(path, OVR::File::Mode_Write);
|
nuclear@0
|
89 WriteHeaderV1();
|
nuclear@0
|
90 }
|
nuclear@0
|
91 else
|
nuclear@0
|
92 {
|
nuclear@0
|
93 _File.Seek(0, FileConstants::Seek_End);
|
nuclear@0
|
94 }
|
nuclear@0
|
95
|
nuclear@0
|
96 if (_File.IsValid())
|
nuclear@0
|
97 {
|
nuclear@0
|
98 UserData1 = userData1;
|
nuclear@0
|
99 FileName = fileName;
|
nuclear@0
|
100 _Observer.SetHandler(LatencyStatisticsSlot::FromMember<LatencyStatisticsCSV, &LatencyStatisticsCSV::OnResults>(this));
|
nuclear@0
|
101
|
nuclear@0
|
102 return true;
|
nuclear@0
|
103 }
|
nuclear@0
|
104
|
nuclear@0
|
105 return false;
|
nuclear@0
|
106 }
|
nuclear@0
|
107 bool LatencyStatisticsCSV::Stop()
|
nuclear@0
|
108 {
|
nuclear@0
|
109 if (_File.IsValid())
|
nuclear@0
|
110 {
|
nuclear@0
|
111 _File.Flush();
|
nuclear@0
|
112 _File.Close();
|
nuclear@0
|
113 _Observer.ReleaseAll();
|
nuclear@0
|
114
|
nuclear@0
|
115 Guid.Clear();
|
nuclear@0
|
116 FileName.Clear();
|
nuclear@0
|
117 return true;
|
nuclear@0
|
118 }
|
nuclear@0
|
119 return false;
|
nuclear@0
|
120 }
|
nuclear@0
|
121 void LatencyStatisticsCSV::WriteHeaderV1()
|
nuclear@0
|
122 {
|
nuclear@0
|
123 if (_File.IsValid())
|
nuclear@0
|
124 {
|
nuclear@0
|
125 // Write header if creating the file
|
nuclear@0
|
126 const char *str = "GUID,OS,OSVersion,Process,DisplayDriver,CameraDriver,GPU,Time,Interval,FPS,EndFrameExecutionTime,LatencyRender,LatencyTimewarp,LatencyPostPresent,LatencyVisionProc,LatencyVisionFrame,UserData1\n";
|
nuclear@0
|
127 _File.Write((const uint8_t *) str, (int)OVR_strlen(str));
|
nuclear@0
|
128 }
|
nuclear@0
|
129 }
|
nuclear@0
|
130
|
nuclear@0
|
131 void LatencyStatisticsCSV::WriteResultsV1(LatencyStatisticsResults *results)
|
nuclear@0
|
132 {
|
nuclear@0
|
133 if (_File.IsValid())
|
nuclear@0
|
134 {
|
nuclear@0
|
135 char str[512];
|
nuclear@0
|
136 OVR_sprintf(str, sizeof(str),
|
nuclear@0
|
137 "%s,%s,%s,%s,%s,%s,%s,%f,%f,%f,%f,%f,%f,%f,%f,%f,%s\n",
|
nuclear@0
|
138 Guid.ToCStr(),
|
nuclear@0
|
139 OS.ToCStr(),
|
nuclear@0
|
140 OSVersion.ToCStr(),
|
nuclear@0
|
141 ProcessInfo.ToCStr(),
|
nuclear@0
|
142 DisplayDriverVersion.ToCStr(),
|
nuclear@0
|
143 CameraDriverVersion.ToCStr(),
|
nuclear@0
|
144 GPUVersion.ToCStr(),
|
nuclear@0
|
145 ovr_GetTimeInSeconds(),
|
nuclear@0
|
146 results->IntervalSeconds,
|
nuclear@0
|
147 results->FPS,
|
nuclear@0
|
148 results->EndFrameExecutionTime,
|
nuclear@0
|
149 results->LatencyRender,
|
nuclear@0
|
150 results->LatencyTimewarp,
|
nuclear@0
|
151 results->LatencyPostPresent,
|
nuclear@0
|
152 results->LatencyVisionProc,
|
nuclear@0
|
153 results->LatencyVisionFrame,
|
nuclear@0
|
154 UserData1.ToCStr());
|
nuclear@0
|
155 str[sizeof(str)-1] = 0;
|
nuclear@0
|
156 _File.Write((const uint8_t *)str, (int)OVR_strlen(str));
|
nuclear@0
|
157 }
|
nuclear@0
|
158 }
|
nuclear@0
|
159 void LatencyStatisticsCSV::OnResults(LatencyStatisticsResults *results)
|
nuclear@0
|
160 {
|
nuclear@0
|
161 WriteResultsV1(results);
|
nuclear@0
|
162 }
|
nuclear@0
|
163 //-------------------------------------------------------------------------------------
|
nuclear@0
|
164 // ***** LatencyStatisticsCalculator
|
nuclear@0
|
165
|
nuclear@0
|
166 LagStatsCalculator::LagStatsCalculator()
|
nuclear@0
|
167 {
|
nuclear@0
|
168 resetPerfStats();
|
nuclear@0
|
169 }
|
nuclear@0
|
170
|
nuclear@0
|
171 void LagStatsCalculator::resetPerfStats(double resetTime)
|
nuclear@0
|
172 {
|
nuclear@0
|
173 EndFrameStartTime = 0.;
|
nuclear@0
|
174 EndFrameEndTime = 0.;
|
nuclear@0
|
175
|
nuclear@0
|
176 LastCameraFrameCounter = ~(uint32_t)0;
|
nuclear@0
|
177
|
nuclear@0
|
178 EpochBegin = resetTime; // Set epoch start to now
|
nuclear@0
|
179
|
nuclear@0
|
180 FrameCount = 0;
|
nuclear@0
|
181 //EndFrameSum = 0.;
|
nuclear@0
|
182
|
nuclear@0
|
183 //VisionProcSum = 0.;
|
nuclear@0
|
184 //VisionLagSum = 0.;
|
nuclear@0
|
185 VisionFrames = 0;
|
nuclear@0
|
186
|
nuclear@0
|
187 //for (int i = 0; i < 3; ++i)
|
nuclear@0
|
188 //{
|
nuclear@0
|
189 // LatencyData[i] = 0.f;
|
nuclear@0
|
190 // LatencySum[i] = 0.;
|
nuclear@0
|
191 //}
|
nuclear@0
|
192
|
nuclear@0
|
193 latencyStatisticsData.EndFrameExecutionTime = 0;
|
nuclear@0
|
194 latencyStatisticsData.LatencyRender = 0;
|
nuclear@0
|
195 latencyStatisticsData.LatencyTimewarp = 0;
|
nuclear@0
|
196 latencyStatisticsData.LatencyPostPresent = 0;
|
nuclear@0
|
197 latencyStatisticsData.LatencyVisionProc = 0;
|
nuclear@0
|
198 latencyStatisticsData.LatencyVisionFrame = 0;
|
nuclear@0
|
199 }
|
nuclear@0
|
200
|
nuclear@0
|
201 void LagStatsCalculator::GetLatestResults(LatencyStatisticsResults* results)
|
nuclear@0
|
202 {
|
nuclear@0
|
203 *results = Results.GetState();
|
nuclear@0
|
204 }
|
nuclear@0
|
205 void LagStatsCalculator::AddResultsObserver(ObserverScope<LatencyStatisticsSlot> *calculateResultsObserver)
|
nuclear@0
|
206 {
|
nuclear@0
|
207 Lock::Locker locker(&calculateResultsLock);
|
nuclear@0
|
208 calculateResultsObserver->GetPtr()->Observe(calculateResultsSubject);
|
nuclear@0
|
209 }
|
nuclear@0
|
210 void LagStatsCalculator::InstrumentEndFrameStart(double timestamp)
|
nuclear@0
|
211 {
|
nuclear@0
|
212 EndFrameStartTime = timestamp;
|
nuclear@0
|
213 }
|
nuclear@0
|
214
|
nuclear@0
|
215 void LagStatsCalculator::InstrumentLatencyTimings(FrameTimeManager& ftm)
|
nuclear@0
|
216 {
|
nuclear@0
|
217 //latencies[0] = (float)RenderLatencySeconds;
|
nuclear@0
|
218 //latencies[1] = (float)TimewarpLatencySeconds;
|
nuclear@0
|
219 //latencies[2] = (float)FrameDeltas.GetMedianTimeDelta();
|
nuclear@0
|
220
|
nuclear@0
|
221 // Note: This assumes that it is called right before InstrumentEndFrameEnd()
|
nuclear@0
|
222 float latencyRender, latencyTimewarp, latencyPostPresent;
|
nuclear@0
|
223 ftm.GetLatencyTimings(latencyRender, latencyTimewarp, latencyPostPresent);
|
nuclear@0
|
224 latencyStatisticsData.LatencyRender += latencyRender;
|
nuclear@0
|
225 latencyStatisticsData.LatencyTimewarp += latencyTimewarp;
|
nuclear@0
|
226 latencyStatisticsData.LatencyPostPresent += latencyPostPresent;
|
nuclear@0
|
227 }
|
nuclear@0
|
228
|
nuclear@0
|
229 void LagStatsCalculator::InstrumentEndFrameEnd(double timestamp)
|
nuclear@0
|
230 {
|
nuclear@0
|
231 EndFrameEndTime = timestamp;
|
nuclear@0
|
232
|
nuclear@0
|
233 calculateResults();
|
nuclear@0
|
234 }
|
nuclear@0
|
235
|
nuclear@0
|
236 void LagStatsCalculator::InstrumentEyePose(const ovrTrackingState& state)
|
nuclear@0
|
237 {
|
nuclear@0
|
238 // If the camera frame counter has rolled,
|
nuclear@0
|
239 if (LastCameraFrameCounter != state.LastCameraFrameCounter)
|
nuclear@0
|
240 {
|
nuclear@0
|
241 // Accumulate eye pose data
|
nuclear@0
|
242 if (state.StatusFlags != 0)
|
nuclear@0
|
243 {
|
nuclear@0
|
244 latencyStatisticsData.LatencyVisionProc += state.LastVisionProcessingTime;
|
nuclear@0
|
245 latencyStatisticsData.LatencyVisionFrame += state.LastVisionFrameLatency;
|
nuclear@0
|
246 }
|
nuclear@0
|
247 ++VisionFrames;
|
nuclear@0
|
248
|
nuclear@0
|
249 LastCameraFrameCounter = state.LastCameraFrameCounter;
|
nuclear@0
|
250 }
|
nuclear@0
|
251 }
|
nuclear@0
|
252
|
nuclear@0
|
253 // Note: Currently assumes this is being called from OnEndFrameEnd() above.
|
nuclear@0
|
254 void LagStatsCalculator::calculateResults()
|
nuclear@0
|
255 {
|
nuclear@0
|
256 // Calculate time in the current epoch so far
|
nuclear@0
|
257 double intervalDuration = EndFrameEndTime - EpochBegin;
|
nuclear@0
|
258
|
nuclear@0
|
259 // If stats should be reset due to inactivity,
|
nuclear@0
|
260 if (intervalDuration >= OVR_LAG_STATS_RESET_LIMIT)
|
nuclear@0
|
261 {
|
nuclear@0
|
262 resetPerfStats(EndFrameEndTime);
|
nuclear@0
|
263 return;
|
nuclear@0
|
264 }
|
nuclear@0
|
265
|
nuclear@0
|
266 // Calculate EndFrame() duration
|
nuclear@0
|
267 double endFrameDuration = EndFrameEndTime - EndFrameStartTime;
|
nuclear@0
|
268
|
nuclear@0
|
269 // Incorporate EndFrame() duration into the running sum
|
nuclear@0
|
270 latencyStatisticsData.EndFrameExecutionTime += endFrameDuration;
|
nuclear@0
|
271
|
nuclear@0
|
272 //for (int i = 0; i < 3; ++i)
|
nuclear@0
|
273 //{
|
nuclear@0
|
274 // LatencySum[i] += LatencyData[i];
|
nuclear@0
|
275 //}
|
nuclear@0
|
276
|
nuclear@0
|
277 // Increment frame counter
|
nuclear@0
|
278 ++FrameCount;
|
nuclear@0
|
279
|
nuclear@0
|
280 // If enough time has passed,
|
nuclear@0
|
281 if (intervalDuration >= OVR_LAG_STATS_EPOCH)
|
nuclear@0
|
282 {
|
nuclear@0
|
283 LatencyStatisticsResults results;
|
nuclear@0
|
284
|
nuclear@0
|
285 float invFrameCount = 1.0f / (float) FrameCount;
|
nuclear@0
|
286
|
nuclear@0
|
287 // EndFrame() instrumentation
|
nuclear@0
|
288 results.IntervalSeconds = intervalDuration;
|
nuclear@0
|
289 results.FPS = FrameCount / intervalDuration;
|
nuclear@0
|
290 results.EndFrameExecutionTime = latencyStatisticsData.EndFrameExecutionTime * invFrameCount;
|
nuclear@0
|
291
|
nuclear@0
|
292 // Latency data
|
nuclear@0
|
293 results.LatencyRender = latencyStatisticsData.LatencyRender * invFrameCount;
|
nuclear@0
|
294 results.LatencyTimewarp = latencyStatisticsData.LatencyTimewarp * invFrameCount;
|
nuclear@0
|
295 results.LatencyPostPresent = latencyStatisticsData.LatencyPostPresent * invFrameCount;
|
nuclear@0
|
296
|
nuclear@0
|
297 double invVisionFrameCount = 1. / VisionFrames;
|
nuclear@0
|
298
|
nuclear@0
|
299 // Eye pose instrumentation
|
nuclear@0
|
300 results.LatencyVisionProc = latencyStatisticsData.LatencyVisionProc * invVisionFrameCount;
|
nuclear@0
|
301 results.LatencyVisionFrame = latencyStatisticsData.LatencyVisionFrame * invVisionFrameCount;
|
nuclear@0
|
302
|
nuclear@0
|
303 Results.SetState(results);
|
nuclear@0
|
304
|
nuclear@0
|
305 {
|
nuclear@0
|
306 Lock::Locker locker(&calculateResultsLock);
|
nuclear@0
|
307 calculateResultsSubject.GetPtr()->Call(&results);
|
nuclear@0
|
308 }
|
nuclear@0
|
309
|
nuclear@0
|
310 // Reset for next frame
|
nuclear@0
|
311 resetPerfStats();
|
nuclear@0
|
312 }
|
nuclear@0
|
313 }
|
nuclear@0
|
314
|
nuclear@0
|
315
|
nuclear@0
|
316 }} // namespace OVR::CAPI
|