aboutsummaryrefslogtreecommitdiffstats
path: root/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2015-03-28 01:43:35 +0100
committerSven Gothel <[email protected]>2015-03-28 01:43:35 +0100
commit4207f9c279e832e3afcb3f5fc6cd8d84cb4cfe4c (patch)
treecf3671058d55b47ab6cb6f36f369928606137628 /LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp
parentc29cd1a2fbff6282bab956ad61661ac9d48c4e6e (diff)
Bump OculusVR RIFT SDK to 0.5.0.1vanilla_0.5.0.1
Diffstat (limited to 'LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp')
-rw-r--r--LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp946
1 files changed, 0 insertions, 946 deletions
diff --git a/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp
deleted file mode 100644
index 8f44063..0000000
--- a/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp
+++ /dev/null
@@ -1,946 +0,0 @@
-/************************************************************************************
-
-Filename : CAPI_FrameTimeManager.cpp
-Content : Manage frame timing and pose prediction for rendering
-Created : November 30, 2013
-Authors : Volga Aksoy, Michael Antonov
-
-Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
-
-Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
-you may not use the Oculus VR Rift SDK except in compliance with the License,
-which is provided at the time of installation or download, or which
-otherwise accompanies this software in either electronic or hard copy form.
-
-You may obtain a copy of the License at
-
-http://www.oculusvr.com/licenses/LICENSE-3.2
-
-Unless required by applicable law or agreed to in writing, the Oculus VR SDK
-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.
-
-************************************************************************************/
-
-#include "CAPI_FrameTimeManager.h"
-
-#include "../Kernel/OVR_Log.h"
-
-namespace OVR { namespace CAPI {
-
-
-//-------------------------------------------------------------------------------------
-// ***** FrameLatencyTracker
-
-
-FrameLatencyTracker::FrameLatencyTracker()
-{
- Reset();
-}
-
-
-void FrameLatencyTracker::Reset()
-{
- TrackerEnabled = true;
- WaitMode = SampleWait_Zeroes;
- MatchCount = 0;
- memset(FrameEndTimes, 0, sizeof(FrameEndTimes));
- FrameIndex = 0;
- //FrameDeltas
- RenderLatencySeconds = 0.0;
- TimewarpLatencySeconds = 0.0;
- LatencyRecordTime = 0.0;
-
- FrameDeltas.Clear();
-}
-
-
-unsigned char FrameLatencyTracker::GetNextDrawColor()
-{
- if (!TrackerEnabled || (WaitMode == SampleWait_Zeroes) ||
- (FrameIndex >= FramesTracked))
- {
- return (unsigned char)Util::FrameTimeRecord::ReadbackIndexToColor(0);
- }
-
- OVR_ASSERT(FrameIndex < FramesTracked);
- return (unsigned char)Util::FrameTimeRecord::ReadbackIndexToColor(FrameIndex+1);
-}
-
-
-void FrameLatencyTracker::SaveDrawColor(unsigned char drawColor, double endFrameTime,
- double renderIMUTime, double timewarpIMUTime )
-{
- if (!TrackerEnabled || (WaitMode == SampleWait_Zeroes))
- return;
-
- if (FrameIndex < FramesTracked)
- {
- OVR_ASSERT(Util::FrameTimeRecord::ReadbackIndexToColor(FrameIndex+1) == drawColor);
- OVR_UNUSED(drawColor);
-
- // saves {color, endFrame time}
- FrameEndTimes[FrameIndex].ReadbackIndex = FrameIndex + 1;
- FrameEndTimes[FrameIndex].TimeSeconds = endFrameTime;
- FrameEndTimes[FrameIndex].RenderIMUTimeSeconds = renderIMUTime;
- FrameEndTimes[FrameIndex].TimewarpIMUTimeSeconds= timewarpIMUTime;
- FrameEndTimes[FrameIndex].MatchedRecord = false;
- FrameIndex++;
- }
- else
- {
- // If the request was outstanding for too long, switch to zero mode to restart.
- if (endFrameTime > (FrameEndTimes[FrameIndex-1].TimeSeconds + 0.15))
- {
- if (MatchCount == 0)
- {
- // If nothing was matched, we have no latency reading.
- RenderLatencySeconds = 0.0;
- TimewarpLatencySeconds = 0.0;
- }
-
- WaitMode = SampleWait_Zeroes;
- MatchCount = 0;
- FrameIndex = 0;
- }
- }
-}
-
-
-void FrameLatencyTracker::MatchRecord(const Util::FrameTimeRecordSet &r)
-{
- if (!TrackerEnabled)
- return;
-
- if (WaitMode == SampleWait_Zeroes)
- {
- // Do we have all zeros?
- if (r.IsAllZeroes())
- {
- OVR_ASSERT(FrameIndex == 0);
- WaitMode = SampleWait_Match;
- MatchCount = 0;
- }
- return;
- }
-
- // We are in Match Mode. Wait until all colors are matched or timeout,
- // at which point we go back to zeros.
-
- for (int i = 0; i < FrameIndex; i++)
- {
- int recordIndex = 0;
- int consecutiveMatch = 0;
-
- OVR_ASSERT(FrameEndTimes[i].ReadbackIndex != 0);
-
- if (r.FindReadbackIndex(&recordIndex, FrameEndTimes[i].ReadbackIndex))
- {
- // Advance forward to see that we have several more matches.
- int ri = recordIndex + 1;
- int j = i + 1;
-
- consecutiveMatch++;
-
- for (; (j < FrameIndex) && (ri < Util::FrameTimeRecordSet::RecordCount); j++, ri++)
- {
- if (r[ri].ReadbackIndex != FrameEndTimes[j].ReadbackIndex)
- break;
- consecutiveMatch++;
- }
-
- // Match at least 2 items in the row, to avoid accidentally matching color.
- if (consecutiveMatch > 1)
- {
- // Record latency values for all but last samples. Keep last 2 samples
- // for the future to simplify matching.
- for (int q = 0; q < consecutiveMatch; q++)
- {
- const Util::FrameTimeRecord &scanoutFrame = r[recordIndex+q];
- FrameTimeRecordEx &renderFrame = FrameEndTimes[i+q];
-
- if (!renderFrame.MatchedRecord)
- {
- double deltaSeconds = scanoutFrame.TimeSeconds - renderFrame.TimeSeconds;
- if (deltaSeconds > 0.0)
- {
- FrameDeltas.AddTimeDelta(deltaSeconds);
-
- // FIRMWARE HACK: don't take new readings if they're 10ms higher than previous reading
- // but only do that for 1 second, after that accept it regardless of the timing difference
- double newRenderLatency = scanoutFrame.TimeSeconds - renderFrame.RenderIMUTimeSeconds;
- if( newRenderLatency < RenderLatencySeconds + 0.01 ||
- scanoutFrame.TimeSeconds > LatencyRecordTime + 1.0)
- {
- LatencyRecordTime = scanoutFrame.TimeSeconds;
- RenderLatencySeconds = scanoutFrame.TimeSeconds - renderFrame.RenderIMUTimeSeconds;
- TimewarpLatencySeconds = (renderFrame.TimewarpIMUTimeSeconds == 0.0) ? 0.0 :
- (scanoutFrame.TimeSeconds - renderFrame.TimewarpIMUTimeSeconds);
- }
- }
-
- renderFrame.MatchedRecord = true;
- MatchCount++;
- }
- }
-
- // Exit for.
- break;
- }
- }
- } // for ( i => FrameIndex )
-
-
- // If we matched all frames, start over.
- if (MatchCount == FramesTracked)
- {
- WaitMode = SampleWait_Zeroes;
- MatchCount = 0;
- FrameIndex = 0;
- }
-}
-
-bool FrameLatencyTracker::IsLatencyTimingAvailable()
-{
- return ovr_GetTimeInSeconds() < (LatencyRecordTime + 2.0);
-}
-
-void FrameLatencyTracker::GetLatencyTimings(float& latencyRender, float& latencyTimewarp, float& latencyPostPresent)
-{
- if (!IsLatencyTimingAvailable())
- {
- latencyRender = 0.0f;
- latencyTimewarp = 0.0f;
- latencyPostPresent = 0.0f;
- }
- else
- {
- latencyRender = (float)RenderLatencySeconds;
- latencyTimewarp = (float)TimewarpLatencySeconds;
- latencyPostPresent = (float)FrameDeltas.GetMedianTimeDelta();
- }
-}
-
-
-//-------------------------------------------------------------------------------------
-// ***** FrameTimeManager
-
-FrameTimeManager::FrameTimeManager(bool vsyncEnabled) :
- RenderInfo(),
- FrameTimeDeltas(),
- DistortionRenderTimes(),
- ScreenLatencyTracker(),
- VsyncEnabled(vsyncEnabled),
- DynamicPrediction(true),
- SdkRender(false),
- //DirectToRift(false), Initialized below.
- //VSyncToScanoutDelay(0.0), Initialized below.
- //NoVSyncToScanoutDelay(0.0), Initialized below.
- ScreenSwitchingDelay(0.0),
- FrameTiming(),
- LocklessTiming(),
- RenderIMUTimeSeconds(0.0),
- TimewarpIMUTimeSeconds(0.0)
-{
- // If driver is in use,
- DirectToRift = !Display::InCompatibilityMode(false);
- if (DirectToRift)
- {
- // The latest driver provides a post-present vsync-to-scan-out delay
- // that is roughly zero. The latency tester will provide real numbers
- // but when it is unavailable for some reason, we should default to
- // an expected value.
- VSyncToScanoutDelay = 0.0001f;
- }
- else
- {
- // HACK: SyncToScanoutDelay observed close to 1 frame in video cards.
- // Overwritten by dynamic latency measurement on DK2.
- VSyncToScanoutDelay = 0.013f;
- }
- NoVSyncToScanoutDelay = 0.004f;
-}
-
-void FrameTimeManager::Init(HmdRenderInfo& renderInfo)
-{
- // Set up prediction distances.
- // With-Vsync timings.
- RenderInfo = renderInfo;
-
- ScreenSwitchingDelay = RenderInfo.Shutter.PixelSettleTime * 0.5f +
- RenderInfo.Shutter.PixelPersistence * 0.5f;
-}
-
-void FrameTimeManager::ResetFrameTiming(unsigned frameIndex,
- bool dynamicPrediction,
- bool sdkRender)
-{
- DynamicPrediction = dynamicPrediction;
- SdkRender = sdkRender;
-
- FrameTimeDeltas.Clear();
- DistortionRenderTimes.Clear();
- ScreenLatencyTracker.Reset();
- //Revisit dynamic pre-Timewarp delay adjustment logic
- //TimewarpAdjuster.Reset();
-
- FrameTiming.FrameIndex = frameIndex;
- FrameTiming.NextFrameTime = 0.0;
- FrameTiming.ThisFrameTime = 0.0;
- FrameTiming.Inputs.FrameDelta = calcFrameDelta();
- // This one is particularly critical, and has been missed in the past because
- // this init function wasn't called for app-rendered.
- FrameTiming.Inputs.ScreenDelay = calcScreenDelay();
- FrameTiming.Inputs.TimewarpWaitDelta = 0.0f;
-
- LocklessTiming.SetState(FrameTiming);
-}
-
-
-double FrameTimeManager::calcFrameDelta() const
-{
- // Timing difference between frame is tracked by FrameTimeDeltas, or
- // is a hard-coded value of 1/FrameRate.
- double frameDelta;
-
- if (!VsyncEnabled)
- {
- frameDelta = 0.0;
- }
- else if (FrameTimeDeltas.GetCount() > 3)
- {
- frameDelta = FrameTimeDeltas.GetMedianTimeDelta();
- if (frameDelta > (RenderInfo.Shutter.VsyncToNextVsync + 0.001))
- frameDelta = RenderInfo.Shutter.VsyncToNextVsync;
- }
- else
- {
- frameDelta = RenderInfo.Shutter.VsyncToNextVsync;
- }
-
- return frameDelta;
-}
-
-
-double FrameTimeManager::calcScreenDelay() const
-{
- double screenDelay = ScreenSwitchingDelay;
- double measuredVSyncToScanout;
-
- // Use real-time DK2 latency tester HW for prediction if its is working.
- // Do sanity check under 60 ms
- if (!VsyncEnabled)
- {
- screenDelay += NoVSyncToScanoutDelay;
- }
- else if ( DynamicPrediction &&
- (ScreenLatencyTracker.FrameDeltas.GetCount() > 3) &&
- (measuredVSyncToScanout = ScreenLatencyTracker.FrameDeltas.GetMedianTimeDelta(),
- (measuredVSyncToScanout > -0.0001) && (measuredVSyncToScanout < 0.06)) )
- {
- screenDelay += measuredVSyncToScanout;
- }
- else
- {
- screenDelay += VSyncToScanoutDelay;
- }
-
- return screenDelay;
-}
-
-double FrameTimeManager::calcTimewarpWaitDelta() const
-{
- // If timewarp timing hasn't been calculated, we should wait.
- if (!VsyncEnabled)
- return 0.0;
-
- if (SdkRender)
- {
- if (NeedDistortionTimeMeasurement())
- return 0.0;
- return -(DistortionRenderTimes.GetMedianTimeDelta() + 0.0035);
-
- //Revisit dynamic pre-Timewarp delay adjustment logic
- /*return -(DistortionRenderTimes.GetMedianTimeDelta() + 0.002 +
- TimewarpAdjuster.GetDelayReduction());*/
- }
-
- // Just a hard-coded "high" value for game-drawn code.
- // TBD: Just return 0 and let users calculate this themselves?
- return -0.004;
-
- //Revisit dynamic pre-Timewarp delay adjustment logic
- //return -(0.003 + TimewarpAdjuster.GetDelayReduction());
-}
-
-//Revisit dynamic pre-Timewarp delay adjustment logic
-/*
-void FrameTimeManager::updateTimewarpTiming()
-{
- // If timewarp timing changes based on this sample, update it.
- double newTimewarpWaitDelta = calcTimewarpWaitDelta();
- if (newTimewarpWaitDelta != FrameTiming.Inputs.TimewarpWaitDelta)
- {
- FrameTiming.Inputs.TimewarpWaitDelta = newTimewarpWaitDelta;
- LocklessTiming.SetState(FrameTiming);
- }
-}
-*/
-
-void FrameTimeManager::Timing::InitTimingFromInputs(const FrameTimeManager::TimingInputs& inputs,
- HmdShutterTypeEnum shutterType,
- double thisFrameTime, unsigned int frameIndex)
-{
- // ThisFrameTime comes from the end of last frame, unless it it changed.
- double nextFrameBase;
- double frameDelta = inputs.FrameDelta;
-
- FrameIndex = frameIndex;
-
- ThisFrameTime = thisFrameTime;
- NextFrameTime = ThisFrameTime + frameDelta;
- nextFrameBase = NextFrameTime + inputs.ScreenDelay;
- MidpointTime = nextFrameBase + frameDelta * 0.5;
- TimewarpPointTime = (inputs.TimewarpWaitDelta == 0.0) ?
- 0.0 : (NextFrameTime + inputs.TimewarpWaitDelta);
-
- // Calculate absolute points in time when eye rendering or corresponding time-warp
- // screen edges will become visible.
- // This only matters with VSync.
- switch(shutterType)
- {
- case HmdShutter_RollingTopToBottom:
- EyeRenderTimes[0] = MidpointTime;
- EyeRenderTimes[1] = MidpointTime;
- TimeWarpStartEndTimes[0][0] = nextFrameBase;
- TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta;
- TimeWarpStartEndTimes[1][0] = nextFrameBase;
- TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
- break;
- case HmdShutter_RollingLeftToRight:
- EyeRenderTimes[0] = nextFrameBase + frameDelta * 0.25;
- EyeRenderTimes[1] = nextFrameBase + frameDelta * 0.75;
-
- /*
- // TBD: MA: It is probably better if mesh sets it up per-eye.
- // Would apply if screen is 0 -> 1 for each eye mesh
- TimeWarpStartEndTimes[0][0] = nextFrameBase;
- TimeWarpStartEndTimes[0][1] = MidpointTime;
- TimeWarpStartEndTimes[1][0] = MidpointTime;
- TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
- */
-
- // Mesh is set up to vary from Edge of scree 0 -> 1 across both eyes
- TimeWarpStartEndTimes[0][0] = nextFrameBase;
- TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta;
- TimeWarpStartEndTimes[1][0] = nextFrameBase;
- TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
-
- break;
- case HmdShutter_RollingRightToLeft:
-
- EyeRenderTimes[0] = nextFrameBase + frameDelta * 0.75;
- EyeRenderTimes[1] = nextFrameBase + frameDelta * 0.25;
-
- // This is *Correct* with Tom's distortion mesh organization.
- TimeWarpStartEndTimes[0][0] = nextFrameBase ;
- TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta;
- TimeWarpStartEndTimes[1][0] = nextFrameBase ;
- TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
- break;
- case HmdShutter_Global:
- // TBD
- EyeRenderTimes[0] = MidpointTime;
- EyeRenderTimes[1] = MidpointTime;
- TimeWarpStartEndTimes[0][0] = MidpointTime;
- TimeWarpStartEndTimes[0][1] = MidpointTime;
- TimeWarpStartEndTimes[1][0] = MidpointTime;
- TimeWarpStartEndTimes[1][1] = MidpointTime;
- break;
- default:
- break;
- }
-}
-
-
-double FrameTimeManager::BeginFrame(unsigned frameIndex)
-{
- RenderIMUTimeSeconds = 0.0;
- TimewarpIMUTimeSeconds = 0.0;
-
- // TPH - putting an assert so this doesn't remain a hidden problem.
- OVR_ASSERT(FrameTiming.Inputs.ScreenDelay != 0);
-
- // ThisFrameTime comes from the end of last frame, unless it it changed.
- double thisFrameTime = (FrameTiming.NextFrameTime != 0.0) ?
- FrameTiming.NextFrameTime : ovr_GetTimeInSeconds();
-
- // We are starting to process a new frame...
- FrameTiming.InitTimingFromInputs(FrameTiming.Inputs, RenderInfo.Shutter.Type,
- thisFrameTime, frameIndex);
-
- return FrameTiming.ThisFrameTime;
-}
-
-
-void FrameTimeManager::EndFrame()
-{
- // Record timing since last frame; must be called after Present & sync.
- FrameTiming.NextFrameTime = ovr_GetTimeInSeconds();
- if (FrameTiming.ThisFrameTime > 0.0)
- {
- //Revisit dynamic pre-Timewarp delay adjustment logic
- /*
- double actualFrameDelta = FrameTiming.NextFrameTime - FrameTiming.ThisFrameTime;
-
- if (VsyncEnabled)
- TimewarpAdjuster.UpdateTimewarpWaitIfSkippedFrames(this, actualFrameDelta,
- FrameTiming.NextFrameTime);
-
- FrameTimeDeltas.AddTimeDelta(actualFrameDelta);
- */
- FrameTimeDeltas.AddTimeDelta(FrameTiming.NextFrameTime - FrameTiming.ThisFrameTime);
- FrameTiming.Inputs.FrameDelta = calcFrameDelta();
- }
-
- // Write to Lock-less
- LocklessTiming.SetState(FrameTiming);
-}
-
-// Thread-safe function to query timing for a future frame
-
-FrameTimeManager::Timing FrameTimeManager::GetFrameTiming(unsigned frameIndex)
-{
- Timing frameTiming = LocklessTiming.GetState();
-
- if (frameTiming.ThisFrameTime == 0.0)
- {
- // If timing hasn't been initialized, starting based on "now" is the best guess.
- frameTiming.InitTimingFromInputs(frameTiming.Inputs, RenderInfo.Shutter.Type,
- ovr_GetTimeInSeconds(), frameIndex);
- }
-
- else if (frameIndex > frameTiming.FrameIndex)
- {
- unsigned frameDelta = frameIndex - frameTiming.FrameIndex;
- double thisFrameTime = frameTiming.NextFrameTime +
- double(frameDelta-1) * frameTiming.Inputs.FrameDelta;
- // Don't run away too far into the future beyond rendering.
- OVR_DEBUG_LOG_COND(frameDelta >= 6, ("GetFrameTiming is 6 or more frames in future beyond rendering!"));
-
- frameTiming.InitTimingFromInputs(frameTiming.Inputs, RenderInfo.Shutter.Type,
- thisFrameTime, frameIndex);
- }
-
- return frameTiming;
-}
-
-
-double FrameTimeManager::GetEyePredictionTime(ovrEyeType eye, unsigned int frameIndex)
-{
- if (VsyncEnabled)
- {
- FrameTimeManager::Timing frameTiming = GetFrameTiming(frameIndex);
-
- // Special case: ovrEye_Count predicts to midpoint
- return (eye == ovrEye_Count) ? frameTiming.MidpointTime : frameTiming.EyeRenderTimes[eye];
- }
-
- // No VSync: Best guess for the near future
- return ovr_GetTimeInSeconds() + ScreenSwitchingDelay + NoVSyncToScanoutDelay;
-}
-
-ovrTrackingState FrameTimeManager::GetEyePredictionTracking(ovrHmd hmd, ovrEyeType eye, unsigned int frameIndex)
-{
- double eyeRenderTime = GetEyePredictionTime(eye, frameIndex);
- ovrTrackingState eyeState = ovrHmd_GetTrackingState(hmd, eyeRenderTime);
-
- // Record view pose sampling time for Latency reporting.
- if (RenderIMUTimeSeconds == 0.0)
- {
- // TODO: Figure out why this are not as accurate as ovr_GetTimeInSeconds()
- //RenderIMUTimeSeconds = eyeState.RawSensorData.TimeInSeconds;
- RenderIMUTimeSeconds = ovr_GetTimeInSeconds();
- }
-
- return eyeState;
-}
-
-Posef FrameTimeManager::GetEyePredictionPose(ovrHmd hmd, ovrEyeType eye)
-{
- double eyeRenderTime = GetEyePredictionTime(eye, 0);
- ovrTrackingState eyeState = ovrHmd_GetTrackingState(hmd, eyeRenderTime);
-
- // Record view pose sampling time for Latency reporting.
- if (RenderIMUTimeSeconds == 0.0)
- {
- // TODO: Figure out why this are not as accurate as ovr_GetTimeInSeconds()
- //RenderIMUTimeSeconds = eyeState.RawSensorData.TimeInSeconds;
- RenderIMUTimeSeconds = ovr_GetTimeInSeconds();
- }
-
- return eyeState.HeadPose.ThePose;
-}
-
-void FrameTimeManager::GetTimewarpPredictions(ovrEyeType eye, double timewarpStartEnd[2])
-{
- if (VsyncEnabled)
- {
- timewarpStartEnd[0] = FrameTiming.TimeWarpStartEndTimes[eye][0];
- timewarpStartEnd[1] = FrameTiming.TimeWarpStartEndTimes[eye][1];
- return;
- }
-
- // Free-running, so this will be displayed immediately.
- // Unfortunately we have no idea which bit of the screen is actually going to be displayed.
- // TODO: guess which bit of the screen is being displayed!
- // (e.g. use DONOTWAIT on present and see when the return isn't WASSTILLWAITING?)
-
- // We have no idea where scan-out is currently, so we can't usefully warp the screen spatially.
- timewarpStartEnd[0] = ovr_GetTimeInSeconds() + ScreenSwitchingDelay + NoVSyncToScanoutDelay;
- timewarpStartEnd[1] = timewarpStartEnd[0];
-}
-
-
-void FrameTimeManager::GetTimewarpMatrices(ovrHmd hmd, ovrEyeType eyeId,
- ovrPosef renderPose, ovrMatrix4f twmOut[2],
- double debugTimingOffsetInSeconds)
-{
- if (!hmd)
- {
- return;
- }
-
- double timewarpStartEnd[2] = { 0.0, 0.0 };
- GetTimewarpPredictions(eyeId, timewarpStartEnd);
-
- //TPH, to vary timing, to allow developers to debug, to shunt the predicted time forward
- //and back, and see if the SDK is truly delivering the correct time. Also to allow
- //illustration of the detrimental effects when this is not done right.
- timewarpStartEnd[0] += debugTimingOffsetInSeconds;
- timewarpStartEnd[1] += debugTimingOffsetInSeconds;
-
-
- //HMDState* p = (HMDState*)hmd;
- ovrTrackingState startState = ovrHmd_GetTrackingState(hmd, timewarpStartEnd[0]);
- ovrTrackingState endState = ovrHmd_GetTrackingState(hmd, timewarpStartEnd[1]);
-
- if (TimewarpIMUTimeSeconds == 0.0)
- {
- // TODO: Figure out why this are not as accurate as ovr_GetTimeInSeconds()
- //TimewarpIMUTimeSeconds = startState.RawSensorData.TimeInSeconds;
- TimewarpIMUTimeSeconds = ovr_GetTimeInSeconds();
- }
-
- Quatf quatFromStart = startState.HeadPose.ThePose.Orientation;
- Quatf quatFromEnd = endState.HeadPose.ThePose.Orientation;
- Quatf quatFromEye = renderPose.Orientation; //EyeRenderPoses[eyeId].Orientation;
- quatFromEye.Invert(); // because we need the view matrix, not the camera matrix
-
- Quatf timewarpStartQuat = quatFromEye * quatFromStart;
- Quatf timewarpEndQuat = quatFromEye * quatFromEnd;
-
- Matrix4f timewarpStart(timewarpStartQuat);
- Matrix4f timewarpEnd(timewarpEndQuat);
-
-
- // The real-world orientations have: X=right, Y=up, Z=backwards.
- // The vectors inside the mesh are in NDC to keep the shader simple: X=right, Y=down, Z=forwards.
- // So we need to perform a similarity transform on this delta matrix.
- // The verbose code would look like this:
- /*
- Matrix4f matBasisChange;
- matBasisChange.SetIdentity();
- matBasisChange.M[0][0] = 1.0f;
- matBasisChange.M[1][1] = -1.0f;
- matBasisChange.M[2][2] = -1.0f;
- Matrix4f matBasisChangeInv = matBasisChange.Inverted();
- matRenderFromNow = matBasisChangeInv * matRenderFromNow * matBasisChange;
- */
- // ...but of course all the above is a constant transform and much more easily done.
- // We flip the signs of the Y&Z row, then flip the signs of the Y&Z column,
- // and of course most of the flips cancel:
- // +++ +-- +--
- // +++ -> flip Y&Z columns -> +-- -> flip Y&Z rows -> -++
- // +++ +-- -++
- timewarpStart.M[0][1] = -timewarpStart.M[0][1];
- timewarpStart.M[0][2] = -timewarpStart.M[0][2];
- timewarpStart.M[1][0] = -timewarpStart.M[1][0];
- timewarpStart.M[2][0] = -timewarpStart.M[2][0];
-
- timewarpEnd .M[0][1] = -timewarpEnd .M[0][1];
- timewarpEnd .M[0][2] = -timewarpEnd .M[0][2];
- timewarpEnd .M[1][0] = -timewarpEnd .M[1][0];
- timewarpEnd .M[2][0] = -timewarpEnd .M[2][0];
-
- twmOut[0] = timewarpStart;
- twmOut[1] = timewarpEnd;
-}
-
-
-// Used by renderer to determine if it should time distortion rendering.
-bool FrameTimeManager::NeedDistortionTimeMeasurement() const
-{
- if (!VsyncEnabled)
- return false;
- return DistortionRenderTimes.GetCount() < DistortionRenderTimes.Capacity;
-}
-
-
-void FrameTimeManager::AddDistortionTimeMeasurement(double distortionTimeSeconds)
-{
- DistortionRenderTimes.AddTimeDelta(distortionTimeSeconds);
-
- //Revisit dynamic pre-Timewarp delay adjustment logic
- //updateTimewarpTiming();
-
- // If timewarp timing changes based on this sample, update it.
- double newTimewarpWaitDelta = calcTimewarpWaitDelta();
- if (newTimewarpWaitDelta != FrameTiming.Inputs.TimewarpWaitDelta)
- {
- FrameTiming.Inputs.TimewarpWaitDelta = newTimewarpWaitDelta;
- LocklessTiming.SetState(FrameTiming);
- }
-}
-
-
-void FrameTimeManager::UpdateFrameLatencyTrackingAfterEndFrame(
- unsigned char frameLatencyTestColor[3],
- const Util::FrameTimeRecordSet& rs)
-{
- // FrameTiming.NextFrameTime in this context (after EndFrame) is the end frame time.
- ScreenLatencyTracker.SaveDrawColor(frameLatencyTestColor[0],
- FrameTiming.NextFrameTime,
- RenderIMUTimeSeconds,
- TimewarpIMUTimeSeconds);
-
- ScreenLatencyTracker.MatchRecord(rs);
-
- // If screen delay changed, update timing.
- double newScreenDelay = calcScreenDelay();
- if (newScreenDelay != FrameTiming.Inputs.ScreenDelay)
- {
- FrameTiming.Inputs.ScreenDelay = newScreenDelay;
- LocklessTiming.SetState(FrameTiming);
- }
-}
-
-
-//-----------------------------------------------------------------------------------
-//Revisit dynamic pre-Timewarp delay adjustment logic
-/*
-void FrameTimeManager::TimewarpDelayAdjuster::Reset()
-{
- State = State_WaitingToReduceLevel;
- DelayLevel = 0;
- InitialFrameCounter = 0;
- TimewarpDelayReductionSeconds = 0.0;
- DelayLevelFinishTime = 0.0;
-
- memset(WaitTimeIndexForLevel, 0, sizeof(WaitTimeIndexForLevel));
- // If we are at level 0, waits are infinite.
- WaitTimeIndexForLevel[0] = MaxTimeIndex;
-}
-
-
-void FrameTimeManager::TimewarpDelayAdjuster::
- UpdateTimewarpWaitIfSkippedFrames(FrameTimeManager* manager,
- double measuredFrameDelta, double nextFrameTime)
-{
- // Times in seconds
- const static double delayTimingTiers[7] = { 1.0, 5.0, 15.0, 30.0, 60.0, 120.0, 1000000.0 };
-
- const double currentFrameDelta = manager->FrameTiming.Inputs.FrameDelta;
-
-
- // Once we detected frame spike, we skip several frames before testing again.
- if (InitialFrameCounter > 0)
- {
- InitialFrameCounter --;
- return;
- }
-
- // Skipped frame would usually take 2x longer then regular frame
- if (measuredFrameDelta > currentFrameDelta * 1.8)
- {
- if (State == State_WaitingToReduceLevel)
- {
- // If we got here, escalate the level again.
- if (DelayLevel < MaxDelayLevel)
- {
- DelayLevel++;
- InitialFrameCounter = 3;
- }
- }
-
- else if (State == State_VerifyingAfterReduce)
- {
- // So we went down to this level and tried to wait to see if there was
- // as skipped frame and there is -> go back up a level and incrment its timing tier
- if (DelayLevel < MaxDelayLevel)
- {
- DelayLevel++;
- State = State_WaitingToReduceLevel;
-
- // For higher level delays reductions, i.e. more then half a frame,
- // we don't go into the infinite wait tier.
- int maxTimingTier = MaxTimeIndex;
- if (DelayLevel > MaxInfiniteTimingLevel)
- maxTimingTier--;
-
- if (WaitTimeIndexForLevel[DelayLevel] < maxTimingTier )
- WaitTimeIndexForLevel[DelayLevel]++;
- }
- }
-
- DelayLevelFinishTime = nextFrameTime +
- delayTimingTiers[WaitTimeIndexForLevel[DelayLevel]];
- TimewarpDelayReductionSeconds = currentFrameDelta * 0.125 * DelayLevel;
- manager->updateTimewarpTiming();
-
- }
-
- else if (nextFrameTime > DelayLevelFinishTime)
- {
- if (State == State_WaitingToReduceLevel)
- {
- if (DelayLevel > 0)
- {
- DelayLevel--;
- State = State_VerifyingAfterReduce;
- // Always use 1 sec to see if "down sampling mode" caused problems
- DelayLevelFinishTime = nextFrameTime + 1.0f;
- }
- }
- else if (State == State_VerifyingAfterReduce)
- {
- // Prior display level successfully reduced,
- // try to see we we could go down further after wait.
- WaitTimeIndexForLevel[DelayLevel+1] = 0;
- State = State_WaitingToReduceLevel;
- DelayLevelFinishTime = nextFrameTime +
- delayTimingTiers[WaitTimeIndexForLevel[DelayLevel]];
- }
-
- // TBD: Update TimeWarpTiming
- TimewarpDelayReductionSeconds = currentFrameDelta * 0.125 * DelayLevel;
- manager->updateTimewarpTiming();
- }
-
-
- //static int oldDelayLevel = 0;
-
- //if (oldDelayLevel != DelayLevel)
- //{
- //OVR_DEBUG_LOG(("DelayLevel:%d tReduction = %0.5f ", DelayLevel, TimewarpDelayReductionSeconds));
- //oldDelayLevel = DelayLevel;
- //}
- }
- */
-
-//-----------------------------------------------------------------------------------
-// ***** TimeDeltaCollector
-
-void TimeDeltaCollector::AddTimeDelta(double timeSeconds)
-{
- // avoid adding invalid timing values
- if(timeSeconds < 0.0f)
- return;
-
- if (Count == Capacity)
- {
- for(int i=0; i< Count-1; i++)
- TimeBufferSeconds[i] = TimeBufferSeconds[i+1];
- Count--;
- }
- TimeBufferSeconds[Count++] = timeSeconds;
-
- ReCalcMedian = true;
-}
-
-// KevinJ: Better median function
-double CalculateListMedianRecursive(const double inputList[TimeDeltaCollector::Capacity], int inputListLength, int lessThanSum, int greaterThanSum)
-{
- double lessThanMedian[TimeDeltaCollector::Capacity], greaterThanMedian[TimeDeltaCollector::Capacity];
- int lessThanMedianListLength = 0, greaterThanMedianListLength = 0;
- double median = inputList[0];
- int i;
- for (i = 1; i < inputListLength; i++)
- {
- // If same value, spread among lists evenly
- if (inputList[i] < median || ((i & 1) == 0 && inputList[i] == median))
- lessThanMedian[lessThanMedianListLength++] = inputList[i];
- else
- greaterThanMedian[greaterThanMedianListLength++] = inputList[i];
- }
- if (lessThanMedianListLength + lessThanSum == greaterThanMedianListLength + greaterThanSum + 1 ||
- lessThanMedianListLength + lessThanSum == greaterThanMedianListLength + greaterThanSum - 1)
- return median;
-
- if (lessThanMedianListLength + lessThanSum < greaterThanMedianListLength + greaterThanSum)
- {
- lessThanMedian[lessThanMedianListLength++] = median;
- return CalculateListMedianRecursive(greaterThanMedian, greaterThanMedianListLength, lessThanMedianListLength + lessThanSum, greaterThanSum);
- }
- else
- {
- greaterThanMedian[greaterThanMedianListLength++] = median;
- return CalculateListMedianRecursive(lessThanMedian, lessThanMedianListLength, lessThanSum, greaterThanMedianListLength + greaterThanSum);
- }
-}
-// KevinJ: Excludes Firmware hack
-double TimeDeltaCollector::GetMedianTimeDeltaNoFirmwareHack() const
-{
- if (ReCalcMedian)
- {
- ReCalcMedian = false;
- Median = CalculateListMedianRecursive(TimeBufferSeconds, Count, 0, 0);
- }
- return Median;
-}
-double TimeDeltaCollector::GetMedianTimeDelta() const
-{
- if(ReCalcMedian)
- {
- double SortedList[Capacity];
- bool used[Capacity];
-
- memset(used, 0, sizeof(used));
- SortedList[0] = 0.0; // In case Count was 0...
-
- // Probably the slowest way to find median...
- for (int i=0; i<Count; i++)
- {
- double smallestDelta = 1000000.0;
- int index = 0;
-
- for (int j = 0; j < Count; j++)
- {
- if (!used[j])
- {
- if (TimeBufferSeconds[j] < smallestDelta)
- {
- smallestDelta = TimeBufferSeconds[j];
- index = j;
- }
- }
- }
-
- // Mark as used
- used[index] = true;
- SortedList[i] = smallestDelta;
- }
-
- // FIRMWARE HACK: Don't take the actual median, but err on the low time side
- Median = SortedList[Count/4];
- ReCalcMedian = false;
- }
-
- return Median;
-}
-
-
-}} // namespace OVR::CAPI
-