ovr_sdk

view LibOVR/Src/OVR_CAPI.cpp @ 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 source
1 /************************************************************************************
3 Filename : OVR_CAPI.cpp
4 Content : Experimental simple C interface to the HMD - version 1.
5 Created : November 30, 2013
6 Authors : Michael Antonov
8 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
10 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
11 you may not use the Oculus VR Rift SDK except in compliance with the License,
12 which is provided at the time of installation or download, or which
13 otherwise accompanies this software in either electronic or hard copy form.
15 You may obtain a copy of the License at
17 http://www.oculusvr.com/licenses/LICENSE-3.2
19 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
25 ************************************************************************************/
27 #include "OVR_CAPI.h"
28 #include "Kernel/OVR_Timer.h"
29 #include "Kernel/OVR_Math.h"
30 #include "Kernel/OVR_System.h"
31 #include "OVR_Stereo.h"
32 #include "OVR_Profile.h"
33 #include "../Include/OVR_Version.h"
35 #include "CAPI/CAPI_HMDState.h"
36 #include "CAPI/CAPI_FrameTimeManager.h"
38 #include "Service/Service_NetClient.h"
39 #ifdef OVR_SINGLE_PROCESS
40 #include "Service/Service_NetServer.h"
41 #endif
43 #ifdef OVR_OS_WIN32
44 #include "Displays/OVR_Win32_ShimFunctions.h"
45 #endif
48 using namespace OVR;
49 using namespace OVR::Util::Render;
50 using namespace OVR::Tracking;
52 //-------------------------------------------------------------------------------------
53 // Math
54 namespace OVR {
57 // ***** FovPort
59 // C-interop support: FovPort <-> ovrFovPort
60 FovPort::FovPort(const ovrFovPort &src)
61 : UpTan(src.UpTan), DownTan(src.DownTan), LeftTan(src.LeftTan), RightTan(src.RightTan)
62 { }
64 FovPort::operator ovrFovPort () const
65 {
66 ovrFovPort result;
67 result.LeftTan = LeftTan;
68 result.RightTan = RightTan;
69 result.UpTan = UpTan;
70 result.DownTan = DownTan;
71 return result;
72 }
74 // Converts Fov Tan angle units to [-1,1] render target NDC space
75 Vector2f FovPort::TanAngleToRendertargetNDC(Vector2f const &tanEyeAngle)
76 {
77 ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(*this);
78 return tanEyeAngle * eyeToSourceNDC.Scale + eyeToSourceNDC.Offset;
79 }
81 // ***** SensorDataType
83 SensorDataType::SensorDataType(const ovrSensorData& s)
84 {
85 Acceleration = s.Accelerometer;
86 RotationRate = s.Gyro;
87 MagneticField = s.Magnetometer;
88 Temperature = s.Temperature;
89 AbsoluteTimeSeconds = s.TimeInSeconds;
90 }
92 SensorDataType::operator ovrSensorData () const
93 {
94 ovrSensorData result;
95 result.Accelerometer = Acceleration;
96 result.Gyro = RotationRate;
97 result.Magnetometer = MagneticField;
98 result.Temperature = Temperature;
99 result.TimeInSeconds = (float) AbsoluteTimeSeconds;
100 return result;
101 }
104 // ***** SensorState
106 TrackingState::TrackingState(const ovrTrackingState& s)
107 {
108 HeadPose = s.HeadPose;
109 CameraPose = s.CameraPose;
110 LeveledCameraPose = s.LeveledCameraPose;
111 RawSensorData = s.RawSensorData;
112 StatusFlags = s.StatusFlags;
113 LastVisionProcessingTime = s.LastVisionProcessingTime;
114 LastVisionFrameLatency = s.LastVisionFrameLatency;
115 LastCameraFrameCounter = s.LastCameraFrameCounter;
116 }
118 TrackingState::operator ovrTrackingState() const
119 {
120 ovrTrackingState result;
121 result.HeadPose = HeadPose;
122 result.CameraPose = CameraPose;
123 result.LeveledCameraPose = LeveledCameraPose;
124 result.RawSensorData = RawSensorData;
125 result.StatusFlags = StatusFlags;
126 result.LastVisionProcessingTime = LastVisionProcessingTime;
127 result.LastVisionFrameLatency = LastVisionFrameLatency;
128 result.LastCameraFrameCounter = LastCameraFrameCounter;
129 return result;
130 }
133 } // namespace OVR
135 //-------------------------------------------------------------------------------------
137 using namespace OVR::CAPI;
139 #ifdef __cplusplus
140 extern "C" {
141 #endif
144 // Used to generate projection from ovrEyeDesc::Fov
145 OVR_EXPORT ovrMatrix4f ovrMatrix4f_Projection(ovrFovPort fov, float znear, float zfar, ovrBool rightHanded)
146 {
147 return CreateProjection(rightHanded ? true : false, fov, znear, zfar);
148 }
151 OVR_EXPORT ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale,
152 float orthoDistance, float hmdToEyeViewOffsetX)
153 {
155 float orthoHorizontalOffset = hmdToEyeViewOffsetX / orthoDistance;
157 // Current projection maps real-world vector (x,y,1) to the RT.
158 // We want to find the projection that maps the range [-FovPixels/2,FovPixels/2] to
159 // the physical [-orthoHalfFov,orthoHalfFov]
160 // Note moving the offset from M[0][2]+M[1][2] to M[0][3]+M[1][3] - this means
161 // we don't have to feed in Z=1 all the time.
162 // The horizontal offset math is a little hinky because the destination is
163 // actually [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]
164 // So we need to first map [-FovPixels/2,FovPixels/2] to
165 // [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]:
166 // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset;
167 // = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset;
168 // But then we need the same mapping as the existing projection matrix, i.e.
169 // x2 = x1 * Projection.M[0][0] + Projection.M[0][2];
170 // = x0 * (2*orthoHalfFov/FovPixels + orthoHorizontalOffset) * Projection.M[0][0] + Projection.M[0][2];
171 // = x0 * Projection.M[0][0]*2*orthoHalfFov/FovPixels +
172 // orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2];
173 // So in the new projection matrix we need to scale by Projection.M[0][0]*2*orthoHalfFov/FovPixels and
174 // offset by orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2].
176 Matrix4f ortho;
177 ortho.M[0][0] = projection.M[0][0] * orthoScale.x;
178 ortho.M[0][1] = 0.0f;
179 ortho.M[0][2] = 0.0f;
180 ortho.M[0][3] = -projection.M[0][2] + ( orthoHorizontalOffset * projection.M[0][0] );
182 ortho.M[1][0] = 0.0f;
183 ortho.M[1][1] = -projection.M[1][1] * orthoScale.y; // Note sign flip (text rendering uses Y=down).
184 ortho.M[1][2] = 0.0f;
185 ortho.M[1][3] = -projection.M[1][2];
187 /*
188 if ( fabsf ( zNear - zFar ) < 0.001f )
189 {
190 ortho.M[2][0] = 0.0f;
191 ortho.M[2][1] = 0.0f;
192 ortho.M[2][2] = 0.0f;
193 ortho.M[2][3] = zFar;
194 }
195 else
196 {
197 ortho.M[2][0] = 0.0f;
198 ortho.M[2][1] = 0.0f;
199 ortho.M[2][2] = zFar / (zNear - zFar);
200 ortho.M[2][3] = (zFar * zNear) / (zNear - zFar);
201 }
202 */
204 // MA: Undo effect of sign
205 ortho.M[2][0] = 0.0f;
206 ortho.M[2][1] = 0.0f;
207 //ortho.M[2][2] = projection.M[2][2] * projection.M[3][2] * -1.0f; // reverse right-handedness
208 ortho.M[2][2] = 0.0f;
209 ortho.M[2][3] = 0.0f;
210 //projection.M[2][3];
212 // No perspective correction for ortho.
213 ortho.M[3][0] = 0.0f;
214 ortho.M[3][1] = 0.0f;
215 ortho.M[3][2] = 0.0f;
216 ortho.M[3][3] = 1.0f;
218 return ortho;
219 }
222 OVR_EXPORT double ovr_GetTimeInSeconds()
223 {
224 return Timer::GetSeconds();
225 }
227 // Waits until the specified absolute time.
228 OVR_EXPORT double ovr_WaitTillTime(double absTime)
229 {
230 double initialTime = ovr_GetTimeInSeconds();
231 double newTime = initialTime;
233 while(newTime < absTime)
234 {
235 for (int j = 0; j < 5; j++)
236 OVR_PROCESSOR_PAUSE();
238 newTime = ovr_GetTimeInSeconds();
239 }
241 // How long we waited
242 return newTime - initialTime;
243 }
246 //-------------------------------------------------------------------------------------
248 // 1. Init/shutdown.
250 static ovrBool CAPI_SystemInitCalled = 0;
251 static ovrBool CAPI_ovrInitializeCalled = 0;
253 static OVR::Service::NetClient* CAPI_pNetClient = 0;
255 OVR_EXPORT ovrBool ovr_InitializeRenderingShim()
256 {
257 OVR::System::DirectDisplayInitialize();
258 return OVR::System::DirectDisplayEnabled();
259 }
261 OVR_EXPORT ovrBool ovr_Initialize()
262 {
263 if (CAPI_ovrInitializeCalled)
264 return 1;
266 // We must set up the system for the plugin to work
267 if (!OVR::System::IsInitialized())
268 {
269 OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All));
270 CAPI_SystemInitCalled = 1;
271 }
273 if (!OVR::System::DirectDisplayEnabled() && !OVR::Display::InCompatibilityMode(false))
274 {
275 OVR_ASSERT(false);
276 return 0;
277 }
279 CAPI_pNetClient = NetClient::GetInstance();
281 #ifdef OVR_SINGLE_PROCESS
283 // If the server could not start running,
284 if (Service::NetServer::GetInstance()->IsInitialized())
285 {
286 CAPI_pNetClient->Connect(true);
287 }
288 else
289 {
290 // This normally will happen if the OVRService is running in the background,
291 // or another SingleProcess-mode app is running in the background.
292 // In this case, it's using the hardware and we should not also attempt to use
293 // the hardware.
294 LogError("{ERR-079} [LibOVR] Server is already running");
295 }
296 #else
297 CAPI_pNetClient->Connect(true);
298 #endif
300 CAPI_ovrInitializeCalled = 1;
302 return 1;
303 }
305 OVR_EXPORT void ovr_Shutdown()
306 {
307 // We should clean up the system to be complete
308 if (OVR::System::IsInitialized() && CAPI_SystemInitCalled)
309 {
310 OVR::System::Destroy();
311 }
313 CAPI_SystemInitCalled = 0;
314 CAPI_ovrInitializeCalled = 0;
315 }
318 // There is a thread safety issue with ovrHmd_Detect in that multiple calls from different
319 // threads can corrupt the global array state. This would lead to two problems:
320 // a) Create(index) enumerator may miss or overshoot items. Probably not a big deal
321 // as game logic can easily be written to only do Detect(s)/Creates in one place.
322 // The alternative would be to return list handle.
323 // b) TBD: Un-mutexed Detect access from two threads could lead to crash. We should
324 // probably check this.
325 //
327 OVR_EXPORT int ovrHmd_Detect()
328 {
329 if (!CAPI_ovrInitializeCalled)
330 return 0;
332 return CAPI_pNetClient->Hmd_Detect();
333 }
336 // ovrHmd_Create us explicitly separated from ConfigureTracking and ConfigureRendering to allow creation of
337 // a relatively light-weight handle that would reference the device going forward and would
338 // survive future ovrHmd_Detect calls. That is once ovrHMD is returned, index is no longer
339 // necessary and can be changed by a ovrHmd_Detect call.
340 OVR_EXPORT ovrHmd ovrHmd_Create(int index)
341 {
342 if (!CAPI_ovrInitializeCalled)
343 return 0;
345 double t0 = Timer::GetSeconds();
346 HMDNetworkInfo netInfo;
348 // There may be some delay before the HMD is fully detected.
349 // Since we are also trying to create the HMD immediately it may lose this race and
350 // get "NO HMD DETECTED." Wait a bit longer to avoid this.
351 while (!CAPI_pNetClient->Hmd_Create(index, &netInfo) ||
352 netInfo.NetId == InvalidVirtualHmdId)
353 {
354 // If two seconds elapse and still no HMD detected,
355 if (Timer::GetSeconds() - t0 > 2.)
356 {
357 if (!NetClient::GetInstance()->IsConnected(false, false))
358 {
359 NetClient::GetInstance()->SetLastError("Not connected to service");
360 }
361 else
362 {
363 NetClient::GetInstance()->SetLastError("No HMD Detected");
364 }
366 return 0;
367 }
368 }
370 // Create HMD State object
371 HMDState* hmds = HMDState::CreateHMDState(CAPI_pNetClient, netInfo);
372 if (!hmds)
373 {
374 CAPI_pNetClient->Hmd_Release(netInfo.NetId);
376 NetClient::GetInstance()->SetLastError("Unable to create HMD state");
377 return 0;
378 }
380 // Reset frame timing so that FrameTimeManager values are properly initialized in AppRendered mode.
381 ovrHmd_ResetFrameTiming(hmds->pHmdDesc, 0);
383 return hmds->pHmdDesc;
384 }
387 OVR_EXPORT ovrBool ovrHmd_AttachToWindow( ovrHmd hmd, void* window,
388 const ovrRecti* destMirrorRect,
389 const ovrRecti* sourceRenderTargetRect )
390 {
391 OVR_UNUSED( destMirrorRect );
392 OVR_UNUSED( sourceRenderTargetRect );
394 if (!CAPI_ovrInitializeCalled)
395 return false;
397 if (!hmd || !hmd->Handle)
398 return false;
399 #ifndef OVR_OS_MAC
400 HMDState* hmds = (HMDState*)hmd->Handle;
401 CAPI_pNetClient->Hmd_AttachToWindow(hmds->GetNetId(), window);
402 hmds->pWindow = window;
403 #endif
404 #ifdef OVR_OS_WIN32
405 Win32::DisplayShim::GetInstance().hWindow = (HWND)window;
406 #endif
407 #ifdef OVR_OS_MAC
408 OVR_UNUSED(window);
409 #endif
411 return true;
412 }
414 OVR_EXPORT ovrHmd ovrHmd_CreateDebug(ovrHmdType type)
415 {
416 if (!CAPI_ovrInitializeCalled)
417 return 0;
419 HMDState* hmds = HMDState::CreateHMDState(type);
421 return hmds->pHmdDesc;
422 }
424 OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmddesc)
425 {
426 if (!hmddesc || !hmddesc->Handle)
427 return;
429 // TBD: Any extra shutdown?
430 HMDState* hmds = (HMDState*)hmddesc->Handle;
432 { // Thread checker in its own scope, to avoid access after 'delete'.
433 // Essentially just checks that no other RenderAPI function is executing.
434 ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_Destroy");
435 }
437 #ifdef OVR_OS_WIN32
438 if (hmds->pWindow)
439 {
440 // ? ok to call
441 //CAPI_pNetClient->Hmd_AttachToWindow(hmds->GetNetId(), 0);
442 hmds->pWindow = 0;
443 Win32::DisplayShim::GetInstance().hWindow = (HWND)0;
444 }
445 #endif
447 delete (HMDState*)hmddesc->Handle;
448 }
451 OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmddesc)
452 {
453 if (!CAPI_ovrInitializeCalled)
454 {
455 return "System initialize not called";
456 }
458 VirtualHmdId netId = InvalidVirtualHmdId;
460 if (hmddesc && hmddesc->Handle)
461 {
462 HMDState* p = (HMDState*)hmddesc->Handle;
463 netId = p->GetNetId();
464 }
466 return CAPI_pNetClient->Hmd_GetLastError(netId);
467 }
469 #define OVR_VERSION_LIBOVR_PFX "libOVR:"
471 // Returns version string representing libOVR version. Static, so
472 // string remains valid for app lifespan
473 OVR_EXPORT const char* ovr_GetVersionString()
474 {
475 static const char* version = OVR_VERSION_LIBOVR_PFX OVR_VERSION_STRING;
476 return version + sizeof(OVR_VERSION_LIBOVR_PFX) - 1;
477 }
481 //-------------------------------------------------------------------------------------
483 // Returns capability bits that are enabled at this time; described by ovrHmdCapBits.
484 // Note that this value is different font ovrHmdDesc::Caps, which describes what
485 // capabilities are available.
486 OVR_EXPORT unsigned int ovrHmd_GetEnabledCaps(ovrHmd hmddesc)
487 {
488 HMDState* p = (HMDState*)hmddesc->Handle;
489 return p ? p->EnabledHmdCaps : 0;
490 }
492 // Modifies capability bits described by ovrHmdCapBits that can be modified,
493 // such as ovrHmdCap_LowPersistance.
494 OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmddesc, unsigned int capsBits)
495 {
496 HMDState* p = (HMDState*)hmddesc->Handle;
497 if (p)
498 {
499 p->SetEnabledHmdCaps(capsBits);
500 }
501 }
504 //-------------------------------------------------------------------------------------
505 // *** Sensor
507 // Sensor APIs are separated from Create & Configure for several reasons:
508 // - They need custom parameters that control allocation of heavy resources
509 // such as Vision tracking, which you don't want to create unless necessary.
510 // - A game may want to switch some sensor settings based on user input,
511 // or at lease enable/disable features such as Vision for debugging.
512 // - The same or syntactically similar sensor interface is likely to be used if we
513 // introduce controllers.
514 //
515 // - Sensor interface functions are all Thread-safe, unlike the frame/render API
516 // functions that have different rules (all frame access functions
517 // must be on render thread)
519 OVR_EXPORT ovrBool ovrHmd_ConfigureTracking(ovrHmd hmddesc, unsigned int supportedCaps,
520 unsigned int requiredCaps)
521 {
522 if (hmddesc)
523 {
524 HMDState* p = (HMDState*)hmddesc->Handle;
525 return p->ConfigureTracking(supportedCaps, requiredCaps);
526 }
528 return 0;
529 }
531 OVR_EXPORT void ovrHmd_RecenterPose(ovrHmd hmddesc)
532 {
533 if (hmddesc)
534 {
535 HMDState* p = (HMDState*)hmddesc->Handle;
536 p->TheSensorStateReader.RecenterPose();
537 }
538 }
540 OVR_EXPORT ovrTrackingState ovrHmd_GetTrackingState(ovrHmd hmddesc, double absTime)
541 {
542 ovrTrackingState result;
544 if (hmddesc)
545 {
546 HMDState* p = (HMDState*)hmddesc->Handle;
547 result = p->PredictedTrackingState(absTime);
549 // Instrument data from eye pose
550 p->LagStats.InstrumentEyePose(result);
551 }
552 else
553 memset(&result, 0, sizeof(result));
555 #ifdef OVR_OS_WIN32
556 // Set up display code for Windows
557 Win32::DisplayShim::GetInstance().Active = (result.StatusFlags & ovrStatus_HmdConnected) != 0;
558 #endif
560 return result;
561 }
564 //-------------------------------------------------------------------------------------
565 // *** General Setup
567 // Per HMD -> calculateIdealPixelSize
568 OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmddesc, ovrEyeType eye, ovrFovPort fov,
569 float pixelsPerDisplayPixel)
570 {
571 ovrHmdStruct * hmd = hmddesc->Handle;
572 if (!hmd) return Sizei(0);
574 HMDState* hmds = (HMDState*)hmd;
575 return hmds->RenderState.GetFOVTextureSize(eye, fov, pixelsPerDisplayPixel);
576 }
579 //-------------------------------------------------------------------------------------
582 OVR_EXPORT
583 ovrBool ovrHmd_ConfigureRendering( ovrHmd hmddesc,
584 const ovrRenderAPIConfig* apiConfig,
585 unsigned int distortionCaps,
586 const ovrFovPort eyeFovIn[2],
587 ovrEyeRenderDesc eyeRenderDescOut[2] )
588 {
589 ovrHmdStruct * hmd = hmddesc->Handle;
590 if (!hmd) return 0;
591 return ((HMDState*)hmd)->ConfigureRendering(eyeRenderDescOut, eyeFovIn,
592 apiConfig, distortionCaps);
593 }
597 // TBD: MA - Deprecated, need alternative
598 void ovrHmd_SetVsync(ovrHmd hmddesc, ovrBool vsync)
599 {
600 ovrHmdStruct * hmd = hmddesc->Handle;
601 if (!hmd) return;
603 return ((HMDState*)hmd)->TimeManager.SetVsync(vsync? true : false);
604 }
607 OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmddesc, unsigned int frameIndex)
608 {
609 HMDState* hmds = (HMDState*)hmddesc->Handle;
610 if (!hmds)
611 {
612 ovrFrameTiming f;
613 memset(&f, 0, sizeof(f));
614 return f;
615 }
617 // Check: Proper configure and threading state for the call.
618 hmds->checkRenderingConfigured("ovrHmd_BeginFrame");
619 OVR_DEBUG_LOG_COND(hmds->BeginFrameCalled, ("ovrHmd_BeginFrame called multiple times."));
620 ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_BeginFrame");
622 hmds->BeginFrameCalled = true;
623 hmds->BeginFrameThreadId = OVR::GetCurrentThreadId();
625 return ovrHmd_BeginFrameTiming(hmddesc, frameIndex);
626 }
629 // Renders textures to frame buffer
630 OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmddesc,
631 const ovrPosef renderPose[2],
632 const ovrTexture eyeTexture[2])
633 {
634 HMDState* hmds = (HMDState*)hmddesc->Handle;
635 if (!hmds) return;
637 // Instrument when the EndFrame() call started
638 hmds->LagStats.InstrumentEndFrameStart(ovr_GetTimeInSeconds());
640 hmds->SubmitEyeTextures(renderPose, eyeTexture);
642 // Debug state checks: Must be in BeginFrame, on the same thread.
643 hmds->checkBeginFrameScope("ovrHmd_EndFrame");
644 ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame");
646 hmds->pRenderer->SetLatencyTestColor(hmds->LatencyTestActive ? hmds->LatencyTestDrawColor : NULL);
648 ovrHmd_GetLatencyTest2DrawColor(hmddesc, NULL); // We don't actually need to draw color, so send NULL
650 if (hmds->pRenderer)
651 {
652 hmds->pRenderer->SaveGraphicsState();
654 // See if we need to show the HSWDisplay.
655 if (hmds->pHSWDisplay) // Until we know that these are valid, assume any of them can't be.
656 {
657 ovrHSWDisplayState hswDisplayState;
658 hmds->pHSWDisplay->TickState(&hswDisplayState, true); // This may internally call HASWarning::Display.
660 if (hswDisplayState.Displayed)
661 {
662 hmds->pHSWDisplay->Render(ovrEye_Left, &eyeTexture[ovrEye_Left]);
663 hmds->pHSWDisplay->Render(ovrEye_Right, &eyeTexture[ovrEye_Right]);
664 }
665 }
667 hmds->pRenderer->EndFrame(true);
668 hmds->pRenderer->RestoreGraphicsState();
669 }
671 // Call after present
672 ovrHmd_EndFrameTiming(hmddesc);
674 // Instrument latency tester
675 hmds->LagStats.InstrumentLatencyTimings(hmds->TimeManager);
677 // Instrument when the EndFrame() call ended
678 hmds->LagStats.InstrumentEndFrameEnd(ovr_GetTimeInSeconds());
680 // Out of BeginFrame
681 hmds->BeginFrameThreadId = 0;
682 hmds->BeginFrameCalled = false;
683 }
686 // Not exposed as part of public API
687 OVR_EXPORT void ovrHmd_RegisterPostDistortionCallback(ovrHmd hmddesc, PostDistortionCallback callback)
688 {
689 HMDState* hmds = (HMDState*)hmddesc->Handle;
690 if (!hmds) return;
692 if (hmds->pRenderer)
693 {
694 hmds->pRenderer->RegisterPostDistortionCallback(callback);
695 }
696 }
700 //-------------------------------------------------------------------------------------
701 // ***** Frame Timing logic
704 OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
705 {
706 ovrHmdStruct * hmd = hmddesc->Handle;
707 ovrFrameTiming f;
708 memset(&f, 0, sizeof(f));
710 HMDState* hmds = (HMDState*)hmd;
711 if (hmds)
712 {
713 FrameTimeManager::Timing frameTiming = hmds->TimeManager.GetFrameTiming(frameIndex);
715 f.ThisFrameSeconds = frameTiming.ThisFrameTime;
716 f.NextFrameSeconds = frameTiming.NextFrameTime;
717 f.TimewarpPointSeconds = frameTiming.TimewarpPointTime;
718 f.ScanoutMidpointSeconds = frameTiming.MidpointTime;
719 f.EyeScanoutSeconds[0] = frameTiming.EyeRenderTimes[0];
720 f.EyeScanoutSeconds[1] = frameTiming.EyeRenderTimes[1];
722 // Compute DeltaSeconds.
723 f.DeltaSeconds = (hmds->LastGetFrameTimeSeconds == 0.0f) ? 0.0f :
724 (float) (f.ThisFrameSeconds - hmds->LastFrameTimeSeconds);
725 hmds->LastGetFrameTimeSeconds = f.ThisFrameSeconds;
726 if (f.DeltaSeconds > 1.0f)
727 f.DeltaSeconds = 1.0f;
728 }
730 return f;
731 }
733 OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
734 {
735 ovrHmdStruct * hmd = hmddesc->Handle;
736 ovrFrameTiming f;
737 memset(&f, 0, sizeof(f));
739 HMDState* hmds = (HMDState*)hmd;
740 if (!hmds) return f;
742 // Check: Proper state for the call.
743 OVR_DEBUG_LOG_COND(hmds->BeginFrameTimingCalled,
744 ("ovrHmd_BeginFrameTiming called multiple times."));
745 hmds->BeginFrameTimingCalled = true;
747 double thisFrameTime = hmds->TimeManager.BeginFrame(frameIndex);
749 const FrameTimeManager::Timing &frameTiming = hmds->TimeManager.GetFrameTiming();
751 f.ThisFrameSeconds = thisFrameTime;
752 f.NextFrameSeconds = frameTiming.NextFrameTime;
753 f.TimewarpPointSeconds = frameTiming.TimewarpPointTime;
754 f.ScanoutMidpointSeconds= frameTiming.MidpointTime;
755 f.EyeScanoutSeconds[0] = frameTiming.EyeRenderTimes[0];
756 f.EyeScanoutSeconds[1] = frameTiming.EyeRenderTimes[1];
758 // Compute DeltaSeconds.
759 f.DeltaSeconds = (hmds->LastFrameTimeSeconds == 0.0f) ? 0.0f :
760 (float) (thisFrameTime - hmds->LastFrameTimeSeconds);
761 hmds->LastFrameTimeSeconds = thisFrameTime;
762 if (f.DeltaSeconds > 1.0f)
763 f.DeltaSeconds = 1.0f;
765 return f;
766 }
769 OVR_EXPORT void ovrHmd_EndFrameTiming(ovrHmd hmddesc)
770 {
771 HMDState* hmds = (HMDState*)hmddesc->Handle;
772 if (!hmds) return;
774 // Debug state checks: Must be in BeginFrameTiming, on the same thread.
775 hmds->checkBeginFrameTimingScope("ovrHmd_EndTiming");
776 // MA TBD: Correct check or not?
777 // ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame");
779 hmds->TimeManager.EndFrame();
780 hmds->BeginFrameTimingCalled = false;
782 bool dk2LatencyTest = (hmds->EnabledHmdCaps & ovrHmdCap_DynamicPrediction) != 0;
783 if (dk2LatencyTest)
784 {
785 Util::FrameTimeRecordSet recordset;
786 hmds->TheLatencyTestStateReader.GetRecordSet(recordset);
787 hmds->TimeManager.UpdateFrameLatencyTrackingAfterEndFrame( hmds->LatencyTest2DrawColor,
788 recordset);
789 }
790 }
793 OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
794 {
795 HMDState* hmds = (HMDState*)hmddesc->Handle;
796 if (!hmds) return;
798 hmds->TimeManager.ResetFrameTiming(frameIndex,
799 false,
800 hmds->RenderingConfigured);
801 hmds->LastFrameTimeSeconds = 0.0;
802 hmds->LastGetFrameTimeSeconds = 0.0;
803 }
805 OVR_EXPORT void ovrHmd_GetEyePoses(ovrHmd hmd, unsigned int frameIndex, ovrVector3f hmdToEyeViewOffset[2],
806 ovrPosef outEyePoses[2], ovrTrackingState* outHmdTrackingState)
807 {
808 HMDState* hmds = (HMDState*)hmd->Handle;
809 if (!hmds) return;
811 hmds->LatencyTestActive = hmds->ProcessLatencyTest(hmds->LatencyTestDrawColor);
813 ovrTrackingState hmdTrackingState = hmds->TimeManager.GetEyePredictionTracking(hmd, ovrEye_Count, frameIndex);
814 ovrPosef hmdPose = hmdTrackingState.HeadPose.ThePose;
816 // caller passed in a valid pointer, so copy to output
817 if(outHmdTrackingState)
818 *outHmdTrackingState = hmdTrackingState;
820 // Currently HmdToEyeViewOffset is only a 3D vector
821 // (Negate HmdToEyeViewOffset because offset is a view matrix offset and not a camera offset)
822 outEyePoses[0] = Posef(hmdPose.Orientation, ((Posef)hmdPose).Apply(-((Vector3f)hmdToEyeViewOffset[0])));
823 outEyePoses[1] = Posef(hmdPose.Orientation, ((Posef)hmdPose).Apply(-((Vector3f)hmdToEyeViewOffset[1])));
825 // Instrument data from eye pose
826 hmds->LagStats.InstrumentEyePose(hmdTrackingState);
827 }
829 ovrPosef ovrHmd_GetHmdPosePerEye(ovrHmd hmd, ovrEyeType eye)
830 {
831 HMDState* hmds = (HMDState*)hmd->Handle;
832 if (!hmds) return ovrPosef();
834 // This isn't a great place, but since we removed ovrHmd_BeginEyeRender...
835 // Only process latency tester for drawing the left eye (assumes left eye is drawn first)
836 if (hmds->pRenderer && eye == 0)
837 {
838 hmds->LatencyTestActive = hmds->ProcessLatencyTest(hmds->LatencyTestDrawColor);
839 }
841 hmds->checkBeginFrameTimingScope("ovrHmd_GetEyePose");
842 return hmds->TimeManager.GetEyePredictionPose(hmd, eye);
843 }
845 OVR_EXPORT void ovrHmd_AddDistortionTimeMeasurement(ovrHmd hmddesc, double distortionTimeSeconds)
846 {
847 if (!hmddesc)
848 return;
849 HMDState* hmds = (HMDState*)hmddesc->Handle;
851 hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices");
852 hmds->TimeManager.AddDistortionTimeMeasurement(distortionTimeSeconds);
853 }
857 OVR_EXPORT void ovrHmd_GetEyeTimewarpMatricesDebug(ovrHmd hmddesc, ovrEyeType eye,
858 ovrPosef renderPose, ovrMatrix4f twmOut[2],double debugTimingOffsetInSeconds)
859 {
860 if (!hmddesc)
861 return;
862 HMDState* hmds = (HMDState*)hmddesc->Handle;
864 // Debug checks: BeginFrame was called, on the same thread.
865 hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices");
867 hmds->TimeManager.GetTimewarpMatrices(hmddesc, eye, renderPose, twmOut, debugTimingOffsetInSeconds);
869 /*
870 // MA: Took this out because new latency test approach just sames
871 // the sample times in FrameTimeManager.
872 // TODO: if no timewarp, then test latency in begin eye render
873 if (eye == 0)
874 {
875 hmds->ProcessLatencyTest2(hmds->LatencyTest2DrawColor, -1.0f);
876 }
877 */
878 }
881 OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmddesc, ovrEyeType eye,
882 ovrPosef renderPose, ovrMatrix4f twmOut[2])
883 {
884 return(ovrHmd_GetEyeTimewarpMatricesDebug(hmddesc, eye, renderPose, twmOut, 0.0));
885 }
889 OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmddesc,
890 ovrEyeType eyeType, ovrFovPort fov)
891 {
892 HMDState* hmds = (HMDState*)hmddesc->Handle;
893 ovrEyeRenderDesc erd;
895 if (!hmds)
896 {
897 memset(&erd, 0, sizeof(erd));
898 return erd;
899 }
901 return hmds->RenderState.CalcRenderDesc(eyeType, fov);
902 }
906 #define OVR_OFFSET_OF(s, field) ((size_t)&((s*)0)->field)
911 OVR_EXPORT ovrBool ovrHmd_CreateDistortionMeshDebug( ovrHmd hmddesc,
912 ovrEyeType eyeType, ovrFovPort fov,
913 unsigned int distortionCaps,
914 ovrDistortionMesh *meshData,
915 float debugEyeReliefOverrideInMetres)
916 {
917 // The 'internal' function below can be found in CAPI_HMDState.
918 // Not ideal, but navigating the convolutions of what compiles
919 // where, meant they are in the few places which actually lets these compile.
920 // Please relocate (if you wish) to a more meaningful place if you can navigate the compiler gymnastics :)
921 return(ovrHmd_CreateDistortionMeshInternal( hmddesc->Handle,
922 eyeType, fov,
923 distortionCaps,
924 meshData,
925 debugEyeReliefOverrideInMetres));
927 }
928 OVR_EXPORT ovrBool ovrHmd_CreateDistortionMesh( ovrHmd hmddesc,
929 ovrEyeType eyeType, ovrFovPort fov,
930 unsigned int distortionCaps,
931 ovrDistortionMesh *meshData)
932 {
933 return(ovrHmd_CreateDistortionMeshDebug( hmddesc, eyeType, fov, distortionCaps,meshData, 0));
934 }
938 // Frees distortion mesh allocated by ovrHmd_GenerateDistortionMesh. meshData elements
939 // are set to null and 0s after the call.
940 OVR_EXPORT void ovrHmd_DestroyDistortionMesh(ovrDistortionMesh* meshData)
941 {
942 if (meshData->pVertexData)
943 DistortionMeshDestroy((DistortionMeshVertexData*)meshData->pVertexData,
944 meshData->pIndexData);
945 meshData->pVertexData = 0;
946 meshData->pIndexData = 0;
947 meshData->VertexCount = 0;
948 meshData->IndexCount = 0;
949 }
953 // Computes updated 'uvScaleOffsetOut' to be used with a distortion if render target size or
954 // viewport changes after the fact. This can be used to adjust render size every frame, if desired.
955 OVR_EXPORT void ovrHmd_GetRenderScaleAndOffset( ovrFovPort fov,
956 ovrSizei textureSize, ovrRecti renderViewport,
957 ovrVector2f uvScaleOffsetOut[2] )
958 {
959 // Find the mapping from TanAngle space to target NDC space.
960 ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(fov);
961 // Find the mapping from TanAngle space to textureUV space.
962 ScaleAndOffset2D eyeToSourceUV = CreateUVScaleAndOffsetfromNDCScaleandOffset(
963 eyeToSourceNDC,
964 renderViewport, textureSize );
966 uvScaleOffsetOut[0] = eyeToSourceUV.Scale;
967 uvScaleOffsetOut[1] = eyeToSourceUV.Offset;
968 }
971 //-------------------------------------------------------------------------------------
972 // ***** Latency Test interface
974 OVR_EXPORT ovrBool ovrHmd_GetLatencyTestDrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3])
975 {
976 HMDState* p = (HMDState*)hmddesc->Handle;
977 rgbColorOut[0] = p->LatencyTestDrawColor[0];
978 rgbColorOut[1] = p->LatencyTestDrawColor[1];
979 rgbColorOut[2] = p->LatencyTestDrawColor[2];
980 return p->LatencyTestActive;
981 }
983 OVR_EXPORT ovrBool ovrHmd_ProcessLatencyTest(ovrHmd hmddesc, unsigned char rgbColorOut[3])
984 {
985 OVR_UNUSED(hmddesc);
986 return NetClient::GetInstance()->LatencyUtil_ProcessInputs(Timer::GetSeconds(), rgbColorOut);
987 }
989 OVR_EXPORT const char* ovrHmd_GetLatencyTestResult(ovrHmd hmddesc)
990 {
991 OVR_UNUSED(hmddesc);
992 return NetClient::GetInstance()->LatencyUtil_GetResultsString();
993 }
995 OVR_EXPORT ovrBool ovrHmd_GetLatencyTest2DrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3])
996 {
997 HMDState* hmds = (HMDState*)hmddesc->Handle;
998 if (!hmds) return false;
1000 // TBD: Move directly into renderer
1001 bool dk2LatencyTest = (hmds->EnabledHmdCaps & ovrHmdCap_DynamicPrediction) != 0;
1002 if (dk2LatencyTest)
1004 hmds->TimeManager.GetFrameLatencyTestDrawColor(hmds->LatencyTest2DrawColor);
1005 if(rgbColorOut != NULL)
1007 rgbColorOut[0] = hmds->LatencyTest2DrawColor[0];
1008 rgbColorOut[1] = hmds->LatencyTest2DrawColor[1];
1009 rgbColorOut[2] = hmds->LatencyTest2DrawColor[2];
1012 if(hmds->pRenderer != NULL)
1013 hmds->pRenderer->SetLatencyTest2Color(hmds->LatencyTest2DrawColor);
1015 else
1017 if(hmds->pRenderer != NULL)
1018 hmds->pRenderer->SetLatencyTest2Color(NULL);
1021 return dk2LatencyTest;
1025 OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmddesc)
1027 HMDState* p = (HMDState*)hmddesc->Handle;
1029 // MA Test
1030 float latencyRender, latencyTimewarp, latencyPostPresent;
1032 p->TimeManager.GetLatencyTimings(latencyRender, latencyTimewarp, latencyPostPresent);
1033 return latencyPostPresent;
1037 //-------------------------------------------------------------------------------------
1038 // ***** Health and Safety Warning Display interface
1039 //
1041 OVR_EXPORT void ovrHmd_GetHSWDisplayState(ovrHmd hmd, ovrHSWDisplayState *hswDisplayState)
1043 OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
1045 if (pHMDState)
1047 OVR::CAPI::HSWDisplay* pHSWDisplay = pHMDState->pHSWDisplay;
1049 if(pHSWDisplay)
1050 pHSWDisplay->TickState(hswDisplayState); // This may internally call HSWDisplay::Display.
1054 OVR_EXPORT ovrBool ovrHmd_DismissHSWDisplay(ovrHmd hmd)
1056 OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
1058 if (pHMDState)
1060 OVR::CAPI::HSWDisplay* pHSWDisplay = pHMDState->pHSWDisplay;
1062 if(pHSWDisplay)
1063 return (pHSWDisplay->Dismiss() ? 1 : 0);
1066 return false;
1070 // -----------------------------------------------------------------------------------
1071 // ***** Property Access
1072 OVR_EXPORT ovrBool ovrHmd_GetBool(ovrHmd hmddesc,
1073 const char* propertyName,
1074 ovrBool defaultVal)
1076 OVR_ASSERT(propertyName);
1077 if (hmddesc)
1079 HMDState* hmds = (HMDState*)hmddesc->Handle;
1080 OVR_ASSERT(hmds);
1081 if (hmds)
1083 return hmds->getBoolValue(propertyName, (defaultVal != 0));
1087 return NetClient::GetInstance()->GetBoolValue(InvalidVirtualHmdId, propertyName, (defaultVal != 0)) ? 1 : 0;
1090 OVR_EXPORT ovrBool ovrHmd_SetBool(ovrHmd hmddesc,
1091 const char* propertyName,
1092 ovrBool value)
1094 OVR_ASSERT(propertyName);
1095 if (hmddesc)
1097 HMDState* hmds = (HMDState*)hmddesc->Handle;
1098 OVR_ASSERT(hmds);
1099 if (hmds)
1101 return hmds->setBoolValue(propertyName, value != 0) ? 1 : 0;
1105 return NetClient::GetInstance()->SetBoolValue(InvalidVirtualHmdId, propertyName, (value != 0)) ? 1 : 0;
1108 OVR_EXPORT int ovrHmd_GetInt(ovrHmd hmddesc,
1109 const char* propertyName,
1110 int defaultVal)
1112 OVR_ASSERT(propertyName);
1113 if (hmddesc)
1115 HMDState* hmds = (HMDState*)hmddesc->Handle;
1116 OVR_ASSERT(hmds);
1117 if (hmds)
1119 return hmds->getIntValue(propertyName, defaultVal);
1123 return NetClient::GetInstance()->GetIntValue(InvalidVirtualHmdId, propertyName, defaultVal);
1126 OVR_EXPORT ovrBool ovrHmd_SetInt(ovrHmd hmddesc,
1127 const char* propertyName,
1128 int value)
1130 OVR_ASSERT(propertyName);
1131 if (hmddesc)
1133 HMDState* hmds = (HMDState*)hmddesc->Handle;
1134 OVR_ASSERT(hmds);
1135 if (hmds)
1137 return hmds->setIntValue(propertyName, value) ? 1 : 0;
1141 return NetClient::GetInstance()->SetIntValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0;
1144 OVR_EXPORT float ovrHmd_GetFloat(ovrHmd hmddesc,
1145 const char* propertyName,
1146 float defaultVal)
1148 OVR_ASSERT(propertyName);
1149 if (hmddesc)
1151 HMDState* hmds = (HMDState*)hmddesc->Handle;
1152 OVR_ASSERT(hmds);
1153 if (hmds)
1155 return hmds->getFloatValue(propertyName, defaultVal);
1159 return (float)NetClient::GetInstance()->GetNumberValue(InvalidVirtualHmdId, propertyName, defaultVal);
1162 OVR_EXPORT ovrBool ovrHmd_SetFloat(ovrHmd hmddesc,
1163 const char* propertyName,
1164 float value)
1166 OVR_ASSERT(propertyName);
1167 if (hmddesc)
1169 HMDState* hmds = (HMDState*)hmddesc->Handle;
1170 OVR_ASSERT(hmds);
1171 if (hmds)
1173 return hmds->setFloatValue(propertyName, value) ? 1 : 0;
1177 return NetClient::GetInstance()->SetNumberValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0;
1180 OVR_EXPORT unsigned int ovrHmd_GetFloatArray(ovrHmd hmddesc,
1181 const char* propertyName,
1182 float values[],
1183 unsigned int arraySize)
1185 OVR_ASSERT(propertyName);
1186 OVR_ASSERT(hmddesc);
1187 if (hmddesc)
1189 HMDState* hmds = (HMDState*)hmddesc->Handle;
1190 OVR_ASSERT(hmds);
1191 if (hmds)
1193 return hmds->getFloatArray(propertyName, values, arraySize);
1197 // FIXME: Currently it takes a few lines of code to do this, so just not supported for now.
1198 return 0;
1201 // Modify float[] property; false if property doesn't exist or is readonly.
1202 OVR_EXPORT ovrBool ovrHmd_SetFloatArray(ovrHmd hmddesc,
1203 const char* propertyName,
1204 float values[],
1205 unsigned int arraySize)
1207 OVR_ASSERT(propertyName);
1208 OVR_ASSERT(hmddesc);
1209 if (hmddesc)
1211 HMDState* hmds = (HMDState*)hmddesc->Handle;
1212 OVR_ASSERT(hmds);
1213 if (hmds)
1215 return hmds->setFloatArray(propertyName, values, arraySize) ? 1 : 0;
1219 // FIXME: Currently it takes a few lines of code to do this, so just not supported for now.
1220 return 0;
1223 OVR_EXPORT const char* ovrHmd_GetString(ovrHmd hmddesc,
1224 const char* propertyName,
1225 const char* defaultVal)
1227 OVR_ASSERT(propertyName);
1228 if (hmddesc)
1230 HMDState* hmds = (HMDState*)hmddesc->Handle;
1231 if (hmds)
1233 return hmds->getString(propertyName, defaultVal);
1237 return NetClient::GetInstance()->GetStringValue(InvalidVirtualHmdId, propertyName, defaultVal);
1240 OVR_EXPORT ovrBool ovrHmd_SetString(ovrHmd hmddesc,
1241 const char* propertyName,
1242 const char* value)
1244 OVR_ASSERT(propertyName);
1245 if (hmddesc)
1247 HMDState* hmds = (HMDState*)hmddesc->Handle;
1248 if (hmds)
1250 return hmds->setString(propertyName, value) ? 1 : 0;
1254 return NetClient::GetInstance()->SetStringValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0;
1257 // -----------------------------------------------------------------------------------
1258 // ***** Logging
1260 OVR_EXPORT ovrBool ovrHmd_StartPerfLog(ovrHmd hmd, const char* fileName, const char* userData1)
1262 OVR_ASSERT(fileName && fileName[0]);
1264 OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
1266 if (pHMDState)
1268 ovrBool started = pHMDState->LagStatsCSV.Start(fileName, userData1) ? 1 : 0;
1269 if (started)
1270 pHMDState->LagStats.AddResultsObserver(pHMDState->LagStatsCSV.GetObserver());
1271 return started;
1273 return 0;
1275 OVR_EXPORT ovrBool ovrHmd_StopPerfLog(ovrHmd hmd)
1277 OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
1279 if (pHMDState)
1281 return pHMDState->LagStatsCSV.Stop() ? 1 : 0;
1283 return false;
1287 #ifdef __cplusplus
1288 } // extern "C"
1289 #endif