rev |
line source |
nuclear@0
|
1 /************************************************************************************
|
nuclear@0
|
2
|
nuclear@0
|
3 Filename : CAPI_FrameTimeManager.h
|
nuclear@0
|
4 Content : Manage frame timing and pose prediction for rendering
|
nuclear@0
|
5 Created : November 30, 2013
|
nuclear@0
|
6 Authors : Volga Aksoy, Michael Antonov
|
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 #ifndef OVR_CAPI_FrameTimeManager_h
|
nuclear@0
|
28 #define OVR_CAPI_FrameTimeManager_h
|
nuclear@0
|
29
|
nuclear@0
|
30 #include "../OVR_CAPI.h"
|
nuclear@0
|
31 #include "../Kernel/OVR_Timer.h"
|
nuclear@0
|
32 #include "../Kernel/OVR_Math.h"
|
nuclear@0
|
33 #include "../Util/Util_Render_Stereo.h"
|
nuclear@0
|
34
|
nuclear@0
|
35 namespace OVR { namespace CAPI {
|
nuclear@0
|
36
|
nuclear@0
|
37
|
nuclear@0
|
38 //-------------------------------------------------------------------------------------
|
nuclear@0
|
39 // ***** TimeDeltaCollector
|
nuclear@0
|
40
|
nuclear@0
|
41 // Helper class to collect median times between frames, so that we know
|
nuclear@0
|
42 // how long to wait.
|
nuclear@0
|
43 struct TimeDeltaCollector
|
nuclear@0
|
44 {
|
nuclear@0
|
45 TimeDeltaCollector() : Median(-1.0), Count(0), ReCalcMedian(true) { }
|
nuclear@0
|
46
|
nuclear@0
|
47 void AddTimeDelta(double timeSeconds);
|
nuclear@0
|
48 void Clear() { Count = 0; }
|
nuclear@0
|
49
|
nuclear@0
|
50 double GetMedianTimeDelta() const;
|
nuclear@0
|
51 double GetMedianTimeDeltaNoFirmwareHack() const;
|
nuclear@0
|
52
|
nuclear@0
|
53 double GetCount() const { return Count; }
|
nuclear@0
|
54
|
nuclear@0
|
55 enum { Capacity = 12 };
|
nuclear@0
|
56 private:
|
nuclear@0
|
57 double TimeBufferSeconds[Capacity];
|
nuclear@0
|
58 mutable double Median;
|
nuclear@0
|
59 int Count;
|
nuclear@0
|
60 mutable bool ReCalcMedian;
|
nuclear@0
|
61 };
|
nuclear@0
|
62
|
nuclear@0
|
63
|
nuclear@0
|
64 //-------------------------------------------------------------------------------------
|
nuclear@0
|
65 // ***** FrameLatencyTracker
|
nuclear@0
|
66
|
nuclear@0
|
67 // FrameLatencyTracker tracks frame Present to display Scan-out timing, as reported by
|
nuclear@0
|
68 // the DK2 internal latency tester pixel read-back. The computed value is used in
|
nuclear@0
|
69 // FrameTimeManager for prediction. View Render and TimeWarp to scan-out latencies are
|
nuclear@0
|
70 // also reported for debugging.
|
nuclear@0
|
71 //
|
nuclear@0
|
72 // The class operates by generating color values from GetNextDrawColor() that must
|
nuclear@0
|
73 // be rendered on the back end and then looking for matching values in FrameTimeRecordSet
|
nuclear@0
|
74 // structure as reported by HW.
|
nuclear@0
|
75
|
nuclear@0
|
76 class FrameLatencyTracker
|
nuclear@0
|
77 {
|
nuclear@0
|
78 public:
|
nuclear@0
|
79
|
nuclear@0
|
80 enum { FramesTracked = Util::LT2_IncrementCount-1 };
|
nuclear@0
|
81
|
nuclear@0
|
82 FrameLatencyTracker();
|
nuclear@0
|
83
|
nuclear@0
|
84 // DrawColor == 0 is special in that it doesn't need saving of timestamp
|
nuclear@0
|
85 unsigned char GetNextDrawColor();
|
nuclear@0
|
86
|
nuclear@0
|
87 void SaveDrawColor(unsigned char drawColor, double endFrameTime,
|
nuclear@0
|
88 double renderIMUTime, double timewarpIMUTime );
|
nuclear@0
|
89
|
nuclear@0
|
90 void MatchRecord(const Util::FrameTimeRecordSet &r);
|
nuclear@0
|
91
|
nuclear@0
|
92 bool IsLatencyTimingAvailable();
|
nuclear@0
|
93 void GetLatencyTimings(float& latencyRender, float& latencyTimewarp, float& latencyPostPresent);
|
nuclear@0
|
94
|
nuclear@0
|
95 void Reset();
|
nuclear@0
|
96
|
nuclear@0
|
97 public:
|
nuclear@0
|
98
|
nuclear@0
|
99 struct FrameTimeRecordEx : public Util::FrameTimeRecord
|
nuclear@0
|
100 {
|
nuclear@0
|
101 bool MatchedRecord;
|
nuclear@0
|
102 double RenderIMUTimeSeconds;
|
nuclear@0
|
103 double TimewarpIMUTimeSeconds;
|
nuclear@0
|
104 };
|
nuclear@0
|
105
|
nuclear@0
|
106 // True if rendering read-back is enabled.
|
nuclear@0
|
107 bool TrackerEnabled;
|
nuclear@0
|
108
|
nuclear@0
|
109 enum SampleWaitType {
|
nuclear@0
|
110 SampleWait_Zeroes, // We are waiting for a record with all zeros.
|
nuclear@0
|
111 SampleWait_Match // We are issuing & matching colors.
|
nuclear@0
|
112 };
|
nuclear@0
|
113
|
nuclear@0
|
114 SampleWaitType WaitMode;
|
nuclear@0
|
115 int MatchCount;
|
nuclear@0
|
116 // Records of frame timings that we are trying to measure.
|
nuclear@0
|
117 FrameTimeRecordEx FrameEndTimes[FramesTracked];
|
nuclear@0
|
118 int FrameIndex;
|
nuclear@0
|
119 // Median filter for (ScanoutTimeSeconds - PostPresent frame time)
|
nuclear@0
|
120 TimeDeltaCollector FrameDeltas;
|
nuclear@0
|
121 // Latency reporting results
|
nuclear@0
|
122 double RenderLatencySeconds;
|
nuclear@0
|
123 double TimewarpLatencySeconds;
|
nuclear@0
|
124 double LatencyRecordTime;
|
nuclear@0
|
125 };
|
nuclear@0
|
126
|
nuclear@0
|
127
|
nuclear@0
|
128
|
nuclear@0
|
129 //-------------------------------------------------------------------------------------
|
nuclear@0
|
130 // ***** FrameTimeManager
|
nuclear@0
|
131
|
nuclear@0
|
132 // FrameTimeManager keeps track of rendered frame timing and handles predictions for
|
nuclear@0
|
133 // orientations and time-warp.
|
nuclear@0
|
134
|
nuclear@0
|
135 class FrameTimeManager
|
nuclear@0
|
136 {
|
nuclear@0
|
137 public:
|
nuclear@0
|
138 FrameTimeManager(bool vsyncEnabled);
|
nuclear@0
|
139
|
nuclear@0
|
140 // Data that affects frame timing computation.
|
nuclear@0
|
141 struct TimingInputs
|
nuclear@0
|
142 {
|
nuclear@0
|
143 // Hard-coded value or dynamic as reported by FrameTimeDeltas.GetMedianTimeDelta().
|
nuclear@0
|
144 double FrameDelta;
|
nuclear@0
|
145 // Screen delay from present to scan-out, as potentially reported by ScreenLatencyTracker.
|
nuclear@0
|
146 double ScreenDelay;
|
nuclear@0
|
147 // Negative value of how many seconds before EndFrame we start timewarp. 0.0 if not used.
|
nuclear@0
|
148 double TimewarpWaitDelta;
|
nuclear@0
|
149
|
nuclear@0
|
150 TimingInputs()
|
nuclear@0
|
151 : FrameDelta(0), ScreenDelay(0), TimewarpWaitDelta(0)
|
nuclear@0
|
152 { }
|
nuclear@0
|
153 };
|
nuclear@0
|
154
|
nuclear@0
|
155 // Timing values for a specific frame.
|
nuclear@0
|
156 struct Timing
|
nuclear@0
|
157 {
|
nuclear@0
|
158 TimingInputs Inputs;
|
nuclear@0
|
159
|
nuclear@0
|
160 // Index of a frame that started at ThisFrameTime.
|
nuclear@0
|
161 unsigned int FrameIndex;
|
nuclear@0
|
162 // Predicted absolute times for when this frame will show up on screen.
|
nuclear@0
|
163 // Generally, all values will be >= NextFrameTime, since that's the time we expect next
|
nuclear@0
|
164 // vsync to succeed.
|
nuclear@0
|
165 double ThisFrameTime;
|
nuclear@0
|
166 double TimewarpPointTime;
|
nuclear@0
|
167 double NextFrameTime;
|
nuclear@0
|
168 double MidpointTime;
|
nuclear@0
|
169 double EyeRenderTimes[2];
|
nuclear@0
|
170 double TimeWarpStartEndTimes[2][2];
|
nuclear@0
|
171
|
nuclear@0
|
172 Timing()
|
nuclear@0
|
173 {
|
nuclear@0
|
174 memset(this, 0, sizeof(Timing));
|
nuclear@0
|
175 }
|
nuclear@0
|
176
|
nuclear@0
|
177 void InitTimingFromInputs(const TimingInputs& inputs, HmdShutterTypeEnum shutterType,
|
nuclear@0
|
178 double thisFrameTime, unsigned int frameIndex);
|
nuclear@0
|
179 };
|
nuclear@0
|
180
|
nuclear@0
|
181
|
nuclear@0
|
182 // Called on startup to provided data on HMD timing.
|
nuclear@0
|
183 void Init(HmdRenderInfo& renderInfo);
|
nuclear@0
|
184
|
nuclear@0
|
185 // Called with each new ConfigureRendering.
|
nuclear@0
|
186 void ResetFrameTiming(unsigned frameIndex,
|
nuclear@0
|
187 bool dynamicPrediction, bool sdkRender);
|
nuclear@0
|
188
|
nuclear@0
|
189 void SetVsync(bool enabled) { VsyncEnabled = enabled; }
|
nuclear@0
|
190
|
nuclear@0
|
191 // BeginFrame returns time of the call
|
nuclear@0
|
192 // TBD: Should this be a predicted time value instead ?
|
nuclear@0
|
193 double BeginFrame(unsigned frameIndex);
|
nuclear@0
|
194 void EndFrame();
|
nuclear@0
|
195
|
nuclear@0
|
196 // Thread-safe function to query timing for a future frame
|
nuclear@0
|
197 Timing GetFrameTiming(unsigned frameIndex);
|
nuclear@0
|
198
|
nuclear@0
|
199 // if eye == ovrEye_Count, timing is for MidpointTime as opposed to any specific eye
|
nuclear@0
|
200 double GetEyePredictionTime(ovrEyeType eye, unsigned int frameIndex);
|
nuclear@0
|
201 ovrTrackingState GetEyePredictionTracking(ovrHmd hmd, ovrEyeType eye, unsigned int frameIndex);
|
nuclear@0
|
202 Posef GetEyePredictionPose(ovrHmd hmd, ovrEyeType eye);
|
nuclear@0
|
203
|
nuclear@0
|
204 void GetTimewarpPredictions(ovrEyeType eye, double timewarpStartEnd[2]);
|
nuclear@0
|
205 void GetTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2],double debugTimingOffsetInSeconds = 0.0);
|
nuclear@0
|
206
|
nuclear@0
|
207 // Used by renderer to determine if it should time distortion rendering.
|
nuclear@0
|
208 bool NeedDistortionTimeMeasurement() const;
|
nuclear@0
|
209 void AddDistortionTimeMeasurement(double distortionTimeSeconds);
|
nuclear@0
|
210
|
nuclear@0
|
211
|
nuclear@0
|
212 // DK2 Latency test interface
|
nuclear@0
|
213
|
nuclear@0
|
214 // Get next draw color for DK2 latency tester (3-byte RGB)
|
nuclear@0
|
215 void GetFrameLatencyTestDrawColor(unsigned char outColor[3])
|
nuclear@0
|
216 {
|
nuclear@0
|
217 outColor[0] = ScreenLatencyTracker.GetNextDrawColor();
|
nuclear@0
|
218 outColor[1] = ScreenLatencyTracker.IsLatencyTimingAvailable() ? 255 : 0;
|
nuclear@0
|
219 outColor[2] = ScreenLatencyTracker.IsLatencyTimingAvailable() ? 0 : 255;
|
nuclear@0
|
220 }
|
nuclear@0
|
221
|
nuclear@0
|
222 // Must be called after EndFrame() to update latency tester timings.
|
nuclear@0
|
223 // Must pass color reported by NextFrameColor for this frame.
|
nuclear@0
|
224 void UpdateFrameLatencyTrackingAfterEndFrame(unsigned char frameLatencyTestColor[3],
|
nuclear@0
|
225 const Util::FrameTimeRecordSet& rs);
|
nuclear@0
|
226
|
nuclear@0
|
227 void GetLatencyTimings(float& latencyRender, float& latencyTimewarp, float& latencyPostPresent)
|
nuclear@0
|
228 {
|
nuclear@0
|
229 return ScreenLatencyTracker.GetLatencyTimings(latencyRender, latencyTimewarp, latencyPostPresent);
|
nuclear@0
|
230 }
|
nuclear@0
|
231
|
nuclear@0
|
232 const Timing& GetFrameTiming() const { return FrameTiming; }
|
nuclear@0
|
233
|
nuclear@0
|
234 private:
|
nuclear@0
|
235 double calcFrameDelta() const;
|
nuclear@0
|
236 double calcScreenDelay() const;
|
nuclear@0
|
237 double calcTimewarpWaitDelta() const;
|
nuclear@0
|
238
|
nuclear@0
|
239 //Revisit dynamic pre-Timewarp delay adjustment logic
|
nuclear@0
|
240 /*
|
nuclear@0
|
241 void updateTimewarpTiming();
|
nuclear@0
|
242
|
nuclear@0
|
243
|
nuclear@0
|
244
|
nuclear@0
|
245 // TimewarpDelayAdjuster implements a simple state machine that reduces the amount
|
nuclear@0
|
246 // of time-warp waiting based on skipped frames.
|
nuclear@0
|
247 struct TimewarpDelayAdjuster
|
nuclear@0
|
248 {
|
nuclear@0
|
249 enum StateInLevel
|
nuclear@0
|
250 {
|
nuclear@0
|
251 // We are ok at this level, and will be waiting for some time before trying to reduce.
|
nuclear@0
|
252 State_WaitingToReduceLevel,
|
nuclear@0
|
253 // After decrementing a level, we are verifying that this won't cause skipped frames.
|
nuclear@0
|
254 State_VerifyingAfterReduce
|
nuclear@0
|
255 };
|
nuclear@0
|
256
|
nuclear@0
|
257 enum {
|
nuclear@0
|
258 MaxDelayLevel = 5,
|
nuclear@0
|
259 MaxInfiniteTimingLevel = 3,
|
nuclear@0
|
260 MaxTimeIndex = 6
|
nuclear@0
|
261 };
|
nuclear@0
|
262
|
nuclear@0
|
263 StateInLevel State;
|
nuclear@0
|
264 // Current level. Higher levels means larger delay reduction (smaller overall time-warp delay).
|
nuclear@0
|
265 int DelayLevel;
|
nuclear@0
|
266 // Index for the amount of time we'd wait in this level. If attempt to decrease level fails,
|
nuclear@0
|
267 // with is incrementing causing the level to become "sticky".
|
nuclear@0
|
268 int WaitTimeIndexForLevel[MaxTimeIndex + 1];
|
nuclear@0
|
269 // We skip few frames after each escalation to avoid too rapid of a reduction.
|
nuclear@0
|
270 int InitialFrameCounter;
|
nuclear@0
|
271 // What th currect "reduction" currently is.
|
nuclear@0
|
272 double TimewarpDelayReductionSeconds;
|
nuclear@0
|
273 // When we should try changing the level again.
|
nuclear@0
|
274 double DelayLevelFinishTime;
|
nuclear@0
|
275
|
nuclear@0
|
276 public:
|
nuclear@0
|
277 TimewarpDelayAdjuster() { Reset(); }
|
nuclear@0
|
278
|
nuclear@0
|
279 void Reset();
|
nuclear@0
|
280
|
nuclear@0
|
281 void UpdateTimewarpWaitIfSkippedFrames(FrameTimeManager* manager,
|
nuclear@0
|
282 double measuredFrameDelta,
|
nuclear@0
|
283 double nextFrameTime);
|
nuclear@0
|
284
|
nuclear@0
|
285 double GetDelayReduction() const { return TimewarpDelayReductionSeconds; }
|
nuclear@0
|
286 };
|
nuclear@0
|
287 */
|
nuclear@0
|
288
|
nuclear@0
|
289
|
nuclear@0
|
290 HmdRenderInfo RenderInfo;
|
nuclear@0
|
291 // Timings are collected through a median filter, to avoid outliers.
|
nuclear@0
|
292 TimeDeltaCollector FrameTimeDeltas;
|
nuclear@0
|
293 TimeDeltaCollector DistortionRenderTimes;
|
nuclear@0
|
294 FrameLatencyTracker ScreenLatencyTracker;
|
nuclear@0
|
295
|
nuclear@0
|
296 // Timing changes if we have no Vsync (all prediction is reduced to fixed interval).
|
nuclear@0
|
297 bool VsyncEnabled;
|
nuclear@0
|
298 // Set if we are rendering via the SDK, so DistortionRenderTimes is valid.
|
nuclear@0
|
299 bool DynamicPrediction;
|
nuclear@0
|
300 // Set if SDk is doing the rendering.
|
nuclear@0
|
301 bool SdkRender;
|
nuclear@0
|
302 // Direct to rift.
|
nuclear@0
|
303 bool DirectToRift;
|
nuclear@0
|
304
|
nuclear@0
|
305 // Total frame delay due to VsyncToFirstScanline, persistence and settle time.
|
nuclear@0
|
306 // Computed from RenderInfor.Shutter.
|
nuclear@0
|
307 double VSyncToScanoutDelay;
|
nuclear@0
|
308 double NoVSyncToScanoutDelay;
|
nuclear@0
|
309 double ScreenSwitchingDelay;
|
nuclear@0
|
310
|
nuclear@0
|
311 //Revisit dynamic pre-Timewarp delay adjustment logic
|
nuclear@0
|
312 //TimewarpDelayAdjuster TimewarpAdjuster;
|
nuclear@0
|
313
|
nuclear@0
|
314 // Current (or last) frame timing info. Used as a source for LocklessTiming.
|
nuclear@0
|
315 Timing FrameTiming;
|
nuclear@0
|
316 // TBD: Don't we need NextFrame here as well?
|
nuclear@0
|
317 LocklessUpdater<Timing, Timing> LocklessTiming;
|
nuclear@0
|
318
|
nuclear@0
|
319 // IMU Read timings
|
nuclear@0
|
320 double RenderIMUTimeSeconds;
|
nuclear@0
|
321 double TimewarpIMUTimeSeconds;
|
nuclear@0
|
322 };
|
nuclear@0
|
323
|
nuclear@0
|
324
|
nuclear@0
|
325 }} // namespace OVR::CAPI
|
nuclear@0
|
326
|
nuclear@0
|
327 #endif // OVR_CAPI_FrameTimeManager_h
|