aboutsummaryrefslogtreecommitdiffstats
path: root/LibOVR/Src/CAPI/CAPI_DistortionTiming.h
blob: 422daf3baba4486f202df740805f759c1fbd4d20 (plain)
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
/************************************************************************************

Filename    :   CAPI_DistortionTiming.h
Content     :   Implements timing for the distortion renderer
Created     :   Dec 16, 2014
Authors     :   Chris Taylor

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.

************************************************************************************/

#ifndef OVR_CAPI_DistortionTiming_h
#define OVR_CAPI_DistortionTiming_h

#include "CAPI_HMDRenderState.h"
#include "CAPI_FrameLatencyTracker.h"
#include "CAPI_FrameTimeManager3.h"
#include "CAPI_HMDRenderState.h"
#include <Kernel/OVR_Lockless.h>
#include <OVR_CAPI.h> // ovrFrameTiming

/*
    -----------------------------
    Distortion Timing Terminology
    -----------------------------

    To fix on one set of terminology, a frame life-cycle is defined the following:

    (1) Get prediction time for app left/right eye rendering.
    (2) App renders left/right eyes.
    (3) Get prediction time for timewarp.
    (4) SDK renders distortion/timewarp/chroma, perhaps measuring the time it takes.
    (5) SDK presents frame and waits for end of frame to occur.
    (6) End of frame occurs at Vsync.
    (7) App goes back to step 1 and starts rendering the next frame.
    (8) Scanout starts some time later for frame from step 6.
    (9) Display panel emits photons some time later for scanout from step 8.

    "Frame interval" is the time interval between Vsyncs, whether or not Vsync is on.
    "Frame end" time means the time at which the scanout starts at scanline 0.
    "Visible midpoint" is when the middle scanline is half-visible to the user's eye.

    "Start of scanout" is when the hardware begins scanout.  The pixels may not be fully
    illuminated at this point.  A hardware-specific rise time on the order of a millisecond
    or two must be added to get photons time.

    All timing is done in units of seconds.

    We approximate the scanline start-end interval with the frame interval.
*/

namespace OVR { namespace CAPI {


//-----------------------------------------------------------------------------
// AppTiming
//
// This structure provides the measurements for the current app frame.
struct AppTiming
{
    // When half of the frame image data has been visible to the eye.
    double VisibleMidpointTime;

    // When the Rift starts scanning out, not including ScreenSwitchingDelay.
    double ScanoutStartTime;

    // Time between frames.
    double FrameInterval;

    void Clear()
    {
        FrameInterval       = 0.013; // A value that should not break anything.
        ScanoutStartTime    = 0.; // Predict to current time.
        VisibleMidpointTime = 0.; // Predict to current time.
    }
};


//-----------------------------------------------------------------------------
// TimewarpTiming
//
// This structure provides the measurements for the current frame timewarp.
struct TimewarpTiming
{
    // Time at which scanout is predicted to start.
    double ScanoutTime;

    // The time when Just-In-Time timewarp should be started.
    // The app should busy/idle-wait until this time before doing timewarp.
    double JIT_TimewarpTime;

    // Left and right eye start and end render times, respectively.
    double EyeStartEndTimes[2][2];
};


//-----------------------------------------------------------------------------
// LocklessAppTimingBase
//
// Base timing info shared via lockless data structure.
// The AppDistortionTimer can use a copy of this data to derive an AppTiming
// object for a given frame index.
struct LocklessAppTimingBase
{
    // Is the data valid?
    // 0 = Not valid.
    uint32_t IsValid;

    // Frame index of the last EndFrame() call to update timing.
    uint32_t LastEndFrameIndex;

    // Frame start time targetted by the last EndFrame() call to update timing.
    double LastStartFrameTime;

    // Last known Vsync time from distortion timer.
    double LastKnownVsyncTime;

    // Vsync fuzz factor used to measure uncertainty in timing.
    double VsyncFuzzFactor;

    // Most updated measurement of the frame interval.
    double FrameInterval;

    // Scanout delay measured by the builtin latency tester.
    double ScanoutDelay;

    // Screen switching delay calculated in distortion timer.
    double ScreenSwitchingDelay;
};


//-----------------------------------------------------------------------------
// DistortionTimer
//
// This is a calculator for the app and timewarp/distortion timing.
class DistortionTimer
{
    typedef OVR::CAPI::FTM3::FrameTimeManagerCore TimeMan;

public:
    DistortionTimer();
    ~DistortionTimer();

    // Returns false if distortion timing could not be initialized.
    bool Initialize(HMDRenderState const* renderState,
                    FrameLatencyTracker const* lagTester);
    void Reset();

    //-------------------------------------------------------------------------
    // Timewarp Timing

    // Calculate timing for current frame timewarp.
    // Result can be retrieved via GetTimewarpTiming().
    void CalculateTimewarpTiming(uint32_t frameIndex, double previousKnownVsyncTime = 0.);

    // Called after CalculateTimewarpTiming() will retrieve the timewarp
    // timing for this frame.
    TimewarpTiming const* GetTimewarpTiming()
    {
        return &CurrentFrameTimewarpTiming;
    }

    // Add a distortion draw call timing measurement.
    void AddDistortionTimeMeasurement(double distortionTimeSeconds);

    // Returns true if more distortion timing measurements are needed.
    bool NeedDistortionTimeMeasurement() const
    {
        // NOTE: Even when Vsync is off this measurement is still valid and useful.
        return !DistortionRenderTimes.AtCapacity();
    }

    // Insert right after spin-wait for Present query to finish for the renderer.
    void SetLastPresentTime()
    {
        // Update vsync time.  This is the post-present time, which is expected to be
        // after the Vsync has completed and our query event put in after the Present
        // has indicated that it is signaled.  However this is not reliable.
        LastPresentTime = Timer::GetSeconds();
    }

    // Returns the time to use for the current frame for latency tester present time,
    // which is not the same as the LastPresentTime.
    double GetLatencyTesterPresentTime() const
    {
        return LatencyTesterPresentTime;
    }

    // Set/get the Timewarp IMU time, which is the time at which the IMU was sampled.
    void SetTimewarpIMUTime(double t)
    {
        LastTimewarpIMUTime = t;
    }

    double GetTimewarpIMUTime() const
    {
        return LastTimewarpIMUTime;
    }

protected:
    // Vsync/no-vsync versions split out for readability.
    AppTiming getAppTimingWithVsync();
    AppTiming getAppTimingNoVsync();

protected:
    // Last time that Present() was called for post-present latency measurement.
    // Provided by SetLastPresentTime() cooperatively with the distortion renderer.
    double LastPresentTime;

    // The time to use for the latency tester for present time, which is the reference
    // time used for calculating present-scanout delay.
    double LatencyTesterPresentTime;

    // Last known Vsync time, provided cooperatively by the distortion renderer
    // via the GetTimewarpTiming() call or internal estimation.
    double LastKnownVsyncTime;

    // Time in seconds that the Vsync measurement may be in error.
    // It is assumed to be pretty tight for D3D11 and Display Driver data but for
    // end frame -based timing we need to add some buffer to avoid misprediction.
    double LastKnownVsyncFuzzBuffer;

    // The current app frame index, initially zero.
    uint32_t AppFrameIndex;
    // Updated in getAppTiming()
    // Read in CalculateTimewarpTiming()

protected:
    // Calculator for the time it takes to render distortion.
    mutable OVR::CAPI::FTM3::MedianCalculator DistortionRenderTimes;

    // Current estimate for timewarp render time.
    double EstimatedTimewarpRenderTime;

protected:
#ifdef OVR_OS_WIN32
    ScopedFileHANDLE DeviceHandle;

    // Attempt to use the display driver for getting a previous vsync
    bool getDriverVsyncTime(double* previousKnownVsyncTime);
#endif

    // Get Vsync to next Vsync interval.
    // NOTE: Technically the Vsync-Vsync frame interval is not the same as the scanout
    // start to end interval because there is a back porch that implies some blanking time
    double getFrameInterval() const;

    // Get Frame End to Scanout delay.  Measured by DK2 Latency Tester if available.
    // This works for Vsync on or off.
    double getScanoutDelay();

protected:
    // Update the TimeManager during timewarp calculation
    void submitDisplayFrame(double frameEndTime, double frameInterval);

    // Update LastKnownVsyncTime.
    // Pass zero if no Vsync timing information is available.
    void updateLastKnownVsyncTime(double previousKnownVsyncTime = 0.);

    double getJITTimewarpTime(double frameEndTime);

protected:
    // DK2 Latency Tester object
    FrameLatencyTracker const* LatencyTester;

    // Render state parameters from HMD
    HMDRenderState const* RenderState;

    // Constant screen switching delay calculated from the shutter info.
    // This is the time it takes between pixels starting to scan out and
    // for the visible light to rise to half the expected brightness value.
    // For OLEDs on the DK2 this is about 1 millisecond.
    double ScreenSwitchingDelay;

    // Time Manager
    TimeMan TimeManager;

    // The last predicted vsync time from the previous frame.
    double LastTimewarpFrameEndTime;

    // Has the timing object already been initialized?
    bool AlreadyInitialized;

protected:
    // Updated by CalculateTimewarpTiming(). 
    TimewarpTiming CurrentFrameTimewarpTiming;

    // Time when sensor was sampled for timewarp pose.
    double LastTimewarpIMUTime;

protected:
    // Lockless data used by application for eye pose timing via the
    // provided AppDistortionTimer class.
    LocklessUpdater<LocklessAppTimingBase> LocklessAppTimingBaseUpdater;

    void ClearAppTimingUpdater()
    {
        LocklessAppTimingBase cleared = LocklessAppTimingBase();
        LocklessAppTimingBaseUpdater.SetState(cleared);
    }

public:
    LocklessUpdater<LocklessAppTimingBase>* GetUpdater()
    {
        return &LocklessAppTimingBaseUpdater;
    }
};


// TODO: This header needs to be split up.


//-----------------------------------------------------------------------------
// AppRenderTimer
//
// This is an app-side calculator for predicted render times based on frame
// indices provided by the app.
class AppRenderTimer
{
public:
    AppRenderTimer();
    ~AppRenderTimer();

    void SetUpdater(LocklessUpdater<LocklessAppTimingBase>* updater)
    {
        AppTimingBaseUpdater = updater;
    }

    bool IsValid() const
    {
        return AppTimingBaseUpdater != nullptr;
    }

    // Returns true on success.
    // Pass in 0 for frameIndex to use the next scanout time,
    // or a non-zero incrementing number for each frame to support queue ahead.
    void GetAppTimingForIndex(AppTiming& result, bool vsyncOn, uint32_t frameIndex = 0);

protected:
    LocklessUpdater<LocklessAppTimingBase>* AppTimingBaseUpdater;
};


//-----------------------------------------------------------------------------
// AppTimingHistory
//
// Keep a history of recent application render timing data, to keep a record of
// when frame indices are expected to scanout.  This is used later to compare
// with when those frames scan out to self-test the timing code.
//
// This class is not thread-safe.
class AppTimingHistory
{
public:
    AppTimingHistory();
    ~AppTimingHistory();

    void Clear();
    void SetScanoutTimeForFrame(uint32_t frameIndex, double scanoutTime);

    // Returns 0.0 if not found.
    double LookupScanoutTime(uint32_t frameIndex);

protected:
    static const int kFramesMax = 8;

    struct Record
    {
        uint32_t FrameIndex;
        double   ScanoutTime;
    };

    int    LastWriteIndex;
    Record History[kFramesMax];
};


}} // namespace OVR::CAPI

#endif // OVR_CAPI_DistortionTiming_h