ovr_sdk
diff LibOVR/Src/CAPI/CAPI_FrameTimeManager.h @ 0:1b39a1b46319
initial 0.4.4
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Wed, 14 Jan 2015 06:51:16 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.h Wed Jan 14 06:51:16 2015 +0200 1.3 @@ -0,0 +1,327 @@ 1.4 +/************************************************************************************ 1.5 + 1.6 +Filename : CAPI_FrameTimeManager.h 1.7 +Content : Manage frame timing and pose prediction for rendering 1.8 +Created : November 30, 2013 1.9 +Authors : Volga Aksoy, Michael Antonov 1.10 + 1.11 +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. 1.12 + 1.13 +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); 1.14 +you may not use the Oculus VR Rift SDK except in compliance with the License, 1.15 +which is provided at the time of installation or download, or which 1.16 +otherwise accompanies this software in either electronic or hard copy form. 1.17 + 1.18 +You may obtain a copy of the License at 1.19 + 1.20 +http://www.oculusvr.com/licenses/LICENSE-3.2 1.21 + 1.22 +Unless required by applicable law or agreed to in writing, the Oculus VR SDK 1.23 +distributed under the License is distributed on an "AS IS" BASIS, 1.24 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.25 +See the License for the specific language governing permissions and 1.26 +limitations under the License. 1.27 + 1.28 +************************************************************************************/ 1.29 + 1.30 +#ifndef OVR_CAPI_FrameTimeManager_h 1.31 +#define OVR_CAPI_FrameTimeManager_h 1.32 + 1.33 +#include "../OVR_CAPI.h" 1.34 +#include "../Kernel/OVR_Timer.h" 1.35 +#include "../Kernel/OVR_Math.h" 1.36 +#include "../Util/Util_Render_Stereo.h" 1.37 + 1.38 +namespace OVR { namespace CAPI { 1.39 + 1.40 + 1.41 +//------------------------------------------------------------------------------------- 1.42 +// ***** TimeDeltaCollector 1.43 + 1.44 +// Helper class to collect median times between frames, so that we know 1.45 +// how long to wait. 1.46 +struct TimeDeltaCollector 1.47 +{ 1.48 + TimeDeltaCollector() : Median(-1.0), Count(0), ReCalcMedian(true) { } 1.49 + 1.50 + void AddTimeDelta(double timeSeconds); 1.51 + void Clear() { Count = 0; } 1.52 + 1.53 + double GetMedianTimeDelta() const; 1.54 + double GetMedianTimeDeltaNoFirmwareHack() const; 1.55 + 1.56 + double GetCount() const { return Count; } 1.57 + 1.58 + enum { Capacity = 12 }; 1.59 +private: 1.60 + double TimeBufferSeconds[Capacity]; 1.61 + mutable double Median; 1.62 + int Count; 1.63 + mutable bool ReCalcMedian; 1.64 +}; 1.65 + 1.66 + 1.67 +//------------------------------------------------------------------------------------- 1.68 +// ***** FrameLatencyTracker 1.69 + 1.70 +// FrameLatencyTracker tracks frame Present to display Scan-out timing, as reported by 1.71 +// the DK2 internal latency tester pixel read-back. The computed value is used in 1.72 +// FrameTimeManager for prediction. View Render and TimeWarp to scan-out latencies are 1.73 +// also reported for debugging. 1.74 +// 1.75 +// The class operates by generating color values from GetNextDrawColor() that must 1.76 +// be rendered on the back end and then looking for matching values in FrameTimeRecordSet 1.77 +// structure as reported by HW. 1.78 + 1.79 +class FrameLatencyTracker 1.80 +{ 1.81 +public: 1.82 + 1.83 + enum { FramesTracked = Util::LT2_IncrementCount-1 }; 1.84 + 1.85 + FrameLatencyTracker(); 1.86 + 1.87 + // DrawColor == 0 is special in that it doesn't need saving of timestamp 1.88 + unsigned char GetNextDrawColor(); 1.89 + 1.90 + void SaveDrawColor(unsigned char drawColor, double endFrameTime, 1.91 + double renderIMUTime, double timewarpIMUTime ); 1.92 + 1.93 + void MatchRecord(const Util::FrameTimeRecordSet &r); 1.94 + 1.95 + bool IsLatencyTimingAvailable(); 1.96 + void GetLatencyTimings(float& latencyRender, float& latencyTimewarp, float& latencyPostPresent); 1.97 + 1.98 + void Reset(); 1.99 + 1.100 +public: 1.101 + 1.102 + struct FrameTimeRecordEx : public Util::FrameTimeRecord 1.103 + { 1.104 + bool MatchedRecord; 1.105 + double RenderIMUTimeSeconds; 1.106 + double TimewarpIMUTimeSeconds; 1.107 + }; 1.108 + 1.109 + // True if rendering read-back is enabled. 1.110 + bool TrackerEnabled; 1.111 + 1.112 + enum SampleWaitType { 1.113 + SampleWait_Zeroes, // We are waiting for a record with all zeros. 1.114 + SampleWait_Match // We are issuing & matching colors. 1.115 + }; 1.116 + 1.117 + SampleWaitType WaitMode; 1.118 + int MatchCount; 1.119 + // Records of frame timings that we are trying to measure. 1.120 + FrameTimeRecordEx FrameEndTimes[FramesTracked]; 1.121 + int FrameIndex; 1.122 + // Median filter for (ScanoutTimeSeconds - PostPresent frame time) 1.123 + TimeDeltaCollector FrameDeltas; 1.124 + // Latency reporting results 1.125 + double RenderLatencySeconds; 1.126 + double TimewarpLatencySeconds; 1.127 + double LatencyRecordTime; 1.128 +}; 1.129 + 1.130 + 1.131 + 1.132 +//------------------------------------------------------------------------------------- 1.133 +// ***** FrameTimeManager 1.134 + 1.135 +// FrameTimeManager keeps track of rendered frame timing and handles predictions for 1.136 +// orientations and time-warp. 1.137 + 1.138 +class FrameTimeManager 1.139 +{ 1.140 +public: 1.141 + FrameTimeManager(bool vsyncEnabled); 1.142 + 1.143 + // Data that affects frame timing computation. 1.144 + struct TimingInputs 1.145 + { 1.146 + // Hard-coded value or dynamic as reported by FrameTimeDeltas.GetMedianTimeDelta(). 1.147 + double FrameDelta; 1.148 + // Screen delay from present to scan-out, as potentially reported by ScreenLatencyTracker. 1.149 + double ScreenDelay; 1.150 + // Negative value of how many seconds before EndFrame we start timewarp. 0.0 if not used. 1.151 + double TimewarpWaitDelta; 1.152 + 1.153 + TimingInputs() 1.154 + : FrameDelta(0), ScreenDelay(0), TimewarpWaitDelta(0) 1.155 + { } 1.156 + }; 1.157 + 1.158 + // Timing values for a specific frame. 1.159 + struct Timing 1.160 + { 1.161 + TimingInputs Inputs; 1.162 + 1.163 + // Index of a frame that started at ThisFrameTime. 1.164 + unsigned int FrameIndex; 1.165 + // Predicted absolute times for when this frame will show up on screen. 1.166 + // Generally, all values will be >= NextFrameTime, since that's the time we expect next 1.167 + // vsync to succeed. 1.168 + double ThisFrameTime; 1.169 + double TimewarpPointTime; 1.170 + double NextFrameTime; 1.171 + double MidpointTime; 1.172 + double EyeRenderTimes[2]; 1.173 + double TimeWarpStartEndTimes[2][2]; 1.174 + 1.175 + Timing() 1.176 + { 1.177 + memset(this, 0, sizeof(Timing)); 1.178 + } 1.179 + 1.180 + void InitTimingFromInputs(const TimingInputs& inputs, HmdShutterTypeEnum shutterType, 1.181 + double thisFrameTime, unsigned int frameIndex); 1.182 + }; 1.183 + 1.184 + 1.185 + // Called on startup to provided data on HMD timing. 1.186 + void Init(HmdRenderInfo& renderInfo); 1.187 + 1.188 + // Called with each new ConfigureRendering. 1.189 + void ResetFrameTiming(unsigned frameIndex, 1.190 + bool dynamicPrediction, bool sdkRender); 1.191 + 1.192 + void SetVsync(bool enabled) { VsyncEnabled = enabled; } 1.193 + 1.194 + // BeginFrame returns time of the call 1.195 + // TBD: Should this be a predicted time value instead ? 1.196 + double BeginFrame(unsigned frameIndex); 1.197 + void EndFrame(); 1.198 + 1.199 + // Thread-safe function to query timing for a future frame 1.200 + Timing GetFrameTiming(unsigned frameIndex); 1.201 + 1.202 + // if eye == ovrEye_Count, timing is for MidpointTime as opposed to any specific eye 1.203 + double GetEyePredictionTime(ovrEyeType eye, unsigned int frameIndex); 1.204 + ovrTrackingState GetEyePredictionTracking(ovrHmd hmd, ovrEyeType eye, unsigned int frameIndex); 1.205 + Posef GetEyePredictionPose(ovrHmd hmd, ovrEyeType eye); 1.206 + 1.207 + void GetTimewarpPredictions(ovrEyeType eye, double timewarpStartEnd[2]); 1.208 + void GetTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2],double debugTimingOffsetInSeconds = 0.0); 1.209 + 1.210 + // Used by renderer to determine if it should time distortion rendering. 1.211 + bool NeedDistortionTimeMeasurement() const; 1.212 + void AddDistortionTimeMeasurement(double distortionTimeSeconds); 1.213 + 1.214 + 1.215 + // DK2 Latency test interface 1.216 + 1.217 + // Get next draw color for DK2 latency tester (3-byte RGB) 1.218 + void GetFrameLatencyTestDrawColor(unsigned char outColor[3]) 1.219 + { 1.220 + outColor[0] = ScreenLatencyTracker.GetNextDrawColor(); 1.221 + outColor[1] = ScreenLatencyTracker.IsLatencyTimingAvailable() ? 255 : 0; 1.222 + outColor[2] = ScreenLatencyTracker.IsLatencyTimingAvailable() ? 0 : 255; 1.223 + } 1.224 + 1.225 + // Must be called after EndFrame() to update latency tester timings. 1.226 + // Must pass color reported by NextFrameColor for this frame. 1.227 + void UpdateFrameLatencyTrackingAfterEndFrame(unsigned char frameLatencyTestColor[3], 1.228 + const Util::FrameTimeRecordSet& rs); 1.229 + 1.230 + void GetLatencyTimings(float& latencyRender, float& latencyTimewarp, float& latencyPostPresent) 1.231 + { 1.232 + return ScreenLatencyTracker.GetLatencyTimings(latencyRender, latencyTimewarp, latencyPostPresent); 1.233 + } 1.234 + 1.235 + const Timing& GetFrameTiming() const { return FrameTiming; } 1.236 + 1.237 +private: 1.238 + double calcFrameDelta() const; 1.239 + double calcScreenDelay() const; 1.240 + double calcTimewarpWaitDelta() const; 1.241 + 1.242 + //Revisit dynamic pre-Timewarp delay adjustment logic 1.243 + /* 1.244 + void updateTimewarpTiming(); 1.245 + 1.246 + 1.247 + 1.248 + // TimewarpDelayAdjuster implements a simple state machine that reduces the amount 1.249 + // of time-warp waiting based on skipped frames. 1.250 + struct TimewarpDelayAdjuster 1.251 + { 1.252 + enum StateInLevel 1.253 + { 1.254 + // We are ok at this level, and will be waiting for some time before trying to reduce. 1.255 + State_WaitingToReduceLevel, 1.256 + // After decrementing a level, we are verifying that this won't cause skipped frames. 1.257 + State_VerifyingAfterReduce 1.258 + }; 1.259 + 1.260 + enum { 1.261 + MaxDelayLevel = 5, 1.262 + MaxInfiniteTimingLevel = 3, 1.263 + MaxTimeIndex = 6 1.264 + }; 1.265 + 1.266 + StateInLevel State; 1.267 + // Current level. Higher levels means larger delay reduction (smaller overall time-warp delay). 1.268 + int DelayLevel; 1.269 + // Index for the amount of time we'd wait in this level. If attempt to decrease level fails, 1.270 + // with is incrementing causing the level to become "sticky". 1.271 + int WaitTimeIndexForLevel[MaxTimeIndex + 1]; 1.272 + // We skip few frames after each escalation to avoid too rapid of a reduction. 1.273 + int InitialFrameCounter; 1.274 + // What th currect "reduction" currently is. 1.275 + double TimewarpDelayReductionSeconds; 1.276 + // When we should try changing the level again. 1.277 + double DelayLevelFinishTime; 1.278 + 1.279 + public: 1.280 + TimewarpDelayAdjuster() { Reset(); } 1.281 + 1.282 + void Reset(); 1.283 + 1.284 + void UpdateTimewarpWaitIfSkippedFrames(FrameTimeManager* manager, 1.285 + double measuredFrameDelta, 1.286 + double nextFrameTime); 1.287 + 1.288 + double GetDelayReduction() const { return TimewarpDelayReductionSeconds; } 1.289 + }; 1.290 + */ 1.291 + 1.292 + 1.293 + HmdRenderInfo RenderInfo; 1.294 + // Timings are collected through a median filter, to avoid outliers. 1.295 + TimeDeltaCollector FrameTimeDeltas; 1.296 + TimeDeltaCollector DistortionRenderTimes; 1.297 + FrameLatencyTracker ScreenLatencyTracker; 1.298 + 1.299 + // Timing changes if we have no Vsync (all prediction is reduced to fixed interval). 1.300 + bool VsyncEnabled; 1.301 + // Set if we are rendering via the SDK, so DistortionRenderTimes is valid. 1.302 + bool DynamicPrediction; 1.303 + // Set if SDk is doing the rendering. 1.304 + bool SdkRender; 1.305 + // Direct to rift. 1.306 + bool DirectToRift; 1.307 + 1.308 + // Total frame delay due to VsyncToFirstScanline, persistence and settle time. 1.309 + // Computed from RenderInfor.Shutter. 1.310 + double VSyncToScanoutDelay; 1.311 + double NoVSyncToScanoutDelay; 1.312 + double ScreenSwitchingDelay; 1.313 + 1.314 + //Revisit dynamic pre-Timewarp delay adjustment logic 1.315 + //TimewarpDelayAdjuster TimewarpAdjuster; 1.316 + 1.317 + // Current (or last) frame timing info. Used as a source for LocklessTiming. 1.318 + Timing FrameTiming; 1.319 + // TBD: Don't we need NextFrame here as well? 1.320 + LocklessUpdater<Timing, Timing> LocklessTiming; 1.321 + 1.322 + // IMU Read timings 1.323 + double RenderIMUTimeSeconds; 1.324 + double TimewarpIMUTimeSeconds; 1.325 +}; 1.326 + 1.327 + 1.328 +}} // namespace OVR::CAPI 1.329 + 1.330 +#endif // OVR_CAPI_FrameTimeManager_h