diff options
Diffstat (limited to 'LibOVR/Src/OVR_CAPI.cpp')
-rw-r--r-- | LibOVR/Src/OVR_CAPI.cpp | 974 |
1 files changed, 669 insertions, 305 deletions
diff --git a/LibOVR/Src/OVR_CAPI.cpp b/LibOVR/Src/OVR_CAPI.cpp index a7f921f..bfc0527 100644 --- a/LibOVR/Src/OVR_CAPI.cpp +++ b/LibOVR/Src/OVR_CAPI.cpp @@ -5,16 +5,16 @@ Content : Experimental simple C interface to the HMD - version 1. Created : November 30, 2013 Authors : Michael Antonov -Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. -Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +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.1 +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, @@ -30,14 +30,24 @@ limitations under the License. #include "Kernel/OVR_System.h" #include "OVR_Stereo.h" #include "OVR_Profile.h" +#include "../Include/OVR_Version.h" -#include "CAPI/CAPI_GlobalState.h" #include "CAPI/CAPI_HMDState.h" #include "CAPI/CAPI_FrameTimeManager.h" +#include "Service/Service_NetClient.h" +#ifdef OVR_SINGLE_PROCESS +#include "Service/Service_NetServer.h" +#endif + +#ifdef OVR_OS_WIN32 +#include "Displays/OVR_Win32_ShimFunctions.h" +#endif + using namespace OVR; using namespace OVR::Util::Render; +using namespace OVR::Tracking; //------------------------------------------------------------------------------------- // Math @@ -68,24 +78,54 @@ Vector2f FovPort::TanAngleToRendertargetNDC(Vector2f const &tanEyeAngle) return tanEyeAngle * eyeToSourceNDC.Scale + eyeToSourceNDC.Offset; } +// ***** SensorDataType + +SensorDataType::SensorDataType(const ovrSensorData& s) +{ + Acceleration = s.Accelerometer; + RotationRate = s.Gyro; + MagneticField = s.Magnetometer; + Temperature = s.Temperature; + AbsoluteTimeSeconds = s.TimeInSeconds; +} + +SensorDataType::operator ovrSensorData () const +{ + ovrSensorData result; + result.Accelerometer = Acceleration; + result.Gyro = RotationRate; + result.Magnetometer = MagneticField; + result.Temperature = Temperature; + result.TimeInSeconds = (float) AbsoluteTimeSeconds; + return result; +} + // ***** SensorState -SensorState::SensorState(const ovrSensorState& s) +TrackingState::TrackingState(const ovrTrackingState& s) { - Predicted = s.Predicted; - Recorded = s.Recorded; - Temperature = s.Temperature; - StatusFlags = s.StatusFlags; + HeadPose = s.HeadPose; + CameraPose = s.CameraPose; + LeveledCameraPose = s.LeveledCameraPose; + RawSensorData = s.RawSensorData; + StatusFlags = s.StatusFlags; + LastVisionProcessingTime = s.LastVisionProcessingTime; + LastVisionFrameLatency = s.LastVisionFrameLatency; + LastCameraFrameCounter = s.LastCameraFrameCounter; } -SensorState::operator ovrSensorState() const +TrackingState::operator ovrTrackingState() const { - ovrSensorState result; - result.Predicted = Predicted; - result.Recorded = Recorded; - result.Temperature = Temperature; + ovrTrackingState result; + result.HeadPose = HeadPose; + result.CameraPose = CameraPose; + result.LeveledCameraPose = LeveledCameraPose; + result.RawSensorData = RawSensorData; result.StatusFlags = StatusFlags; + result.LastVisionProcessingTime = LastVisionProcessingTime; + result.LastVisionFrameLatency = LastVisionFrameLatency; + result.LastCameraFrameCounter = LastCameraFrameCounter; return result; } @@ -109,10 +149,10 @@ OVR_EXPORT ovrMatrix4f ovrMatrix4f_Projection(ovrFovPort fov, float znear, float OVR_EXPORT ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale, - float orthoDistance, float eyeViewAdjustX) + float orthoDistance, float hmdToEyeViewOffsetX) { - float orthoHorizontalOffset = eyeViewAdjustX / orthoDistance; + float orthoHorizontalOffset = hmdToEyeViewOffsetX / orthoDistance; // Current projection maps real-world vector (x,y,1) to the RT. // We want to find the projection that maps the range [-FovPixels/2,FovPixels/2] to @@ -125,7 +165,7 @@ OVR_EXPORT ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ov // [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]: // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset; // = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset; - // But then we need the sam mapping as the existing projection matrix, i.e. + // But then we need the same mapping as the existing projection matrix, i.e. // x2 = x1 * Projection.M[0][0] + Projection.M[0][2]; // = x0 * (2*orthoHalfFov/FovPixels + orthoHorizontalOffset) * Projection.M[0][0] + Projection.M[0][2]; // = x0 * Projection.M[0][0]*2*orthoHalfFov/FovPixels + @@ -187,14 +227,14 @@ OVR_EXPORT double ovr_GetTimeInSeconds() // Waits until the specified absolute time. OVR_EXPORT double ovr_WaitTillTime(double absTime) { - volatile int i; double initialTime = ovr_GetTimeInSeconds(); double newTime = initialTime; while(newTime < absTime) { - for (int j = 0; j < 50; j++) - i = 0; + for (int j = 0; j < 5; j++) + OVR_PROCESSOR_PAUSE(); + newTime = ovr_GetTimeInSeconds(); } @@ -202,44 +242,76 @@ OVR_EXPORT double ovr_WaitTillTime(double absTime) return newTime - initialTime; } + //------------------------------------------------------------------------------------- // 1. Init/shutdown. static ovrBool CAPI_SystemInitCalled = 0; +static ovrBool CAPI_ovrInitializeCalled = 0; + +static OVR::Service::NetClient* CAPI_pNetClient = 0; + +OVR_EXPORT ovrBool ovr_InitializeRenderingShim() +{ + OVR::System::DirectDisplayInitialize(); + return OVR::System::DirectDisplayEnabled(); +} OVR_EXPORT ovrBool ovr_Initialize() { - if (OVR::CAPI::GlobalState::pInstance) + if (CAPI_ovrInitializeCalled) return 1; // We must set up the system for the plugin to work if (!OVR::System::IsInitialized()) - { + { OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); CAPI_SystemInitCalled = 1; } - // Constructor detects devices - GlobalState::pInstance = new GlobalState; + if (!OVR::System::DirectDisplayEnabled() && !OVR::Display::InCompatibilityMode(false)) + { + OVR_ASSERT(false); + return 0; + } + + CAPI_pNetClient = NetClient::GetInstance(); + +#ifdef OVR_SINGLE_PROCESS + + // If the server could not start running, + if (Service::NetServer::GetInstance()->IsInitialized()) + { + CAPI_pNetClient->Connect(true); + } + else + { + // This normally will happen if the OVRService is running in the background, + // or another SingleProcess-mode app is running in the background. + // In this case, it's using the hardware and we should not also attempt to use + // the hardware. + LogError("{ERR-079} [LibOVR] Server is already running"); + } +#else + CAPI_pNetClient->Connect(true); +#endif + + CAPI_ovrInitializeCalled = 1; + return 1; } OVR_EXPORT void ovr_Shutdown() -{ - if (!GlobalState::pInstance) - return; - - delete GlobalState::pInstance; - GlobalState::pInstance = 0; - +{ // We should clean up the system to be complete - if (CAPI_SystemInitCalled) + if (OVR::System::IsInitialized() && CAPI_SystemInitCalled) { OVR::System::Destroy(); - CAPI_SystemInitCalled = 0; - } - return; + } + + CAPI_SystemInitCalled = 0; + CAPI_ovrInitializeCalled = 0; } @@ -254,89 +326,178 @@ OVR_EXPORT void ovr_Shutdown() OVR_EXPORT int ovrHmd_Detect() { - if (!GlobalState::pInstance) + if (!CAPI_ovrInitializeCalled) return 0; - return GlobalState::pInstance->EnumerateDevices(); + + return CAPI_pNetClient->Hmd_Detect(); } -// ovrHmd_Create us explicitly separated from StartSensor and Configure to allow creation of +// ovrHmd_Create us explicitly separated from ConfigureTracking and ConfigureRendering to allow creation of // a relatively light-weight handle that would reference the device going forward and would // survive future ovrHmd_Detect calls. That is once ovrHMD is returned, index is no longer // necessary and can be changed by a ovrHmd_Detect call. - OVR_EXPORT ovrHmd ovrHmd_Create(int index) { - if (!GlobalState::pInstance) - return 0; - Ptr<HMDDevice> device = *GlobalState::pInstance->CreateDevice(index); - if (!device) + if (!CAPI_ovrInitializeCalled) return 0; - HMDState* hmds = new HMDState(device); + double t0 = Timer::GetSeconds(); + HMDNetworkInfo netInfo; + + // There may be some delay before the HMD is fully detected. + // Since we are also trying to create the HMD immediately it may lose this race and + // get "NO HMD DETECTED." Wait a bit longer to avoid this. + while (!CAPI_pNetClient->Hmd_Create(index, &netInfo) || + netInfo.NetId == InvalidVirtualHmdId) + { + // If two seconds elapse and still no HMD detected, + if (Timer::GetSeconds() - t0 > 2.) + { + if (!NetClient::GetInstance()->IsConnected(false, false)) + { + NetClient::GetInstance()->SetLastError("Not connected to service"); + } + else + { + NetClient::GetInstance()->SetLastError("No HMD Detected"); + } + + return 0; + } + } + + // Create HMD State object + HMDState* hmds = HMDState::CreateHMDState(CAPI_pNetClient, netInfo); if (!hmds) + { + CAPI_pNetClient->Hmd_Release(netInfo.NetId); + + NetClient::GetInstance()->SetLastError("Unable to create HMD state"); return 0; + } + + // Reset frame timing so that FrameTimeManager values are properly initialized in AppRendered mode. + ovrHmd_ResetFrameTiming(hmds->pHmdDesc, 0); - return hmds; + return hmds->pHmdDesc; +} + + +OVR_EXPORT ovrBool ovrHmd_AttachToWindow( ovrHmd hmd, void* window, + const ovrRecti* destMirrorRect, + const ovrRecti* sourceRenderTargetRect ) +{ + OVR_UNUSED( destMirrorRect ); + OVR_UNUSED( sourceRenderTargetRect ); + + if (!CAPI_ovrInitializeCalled) + return false; + + if (!hmd || !hmd->Handle) + return false; +#ifndef OVR_OS_MAC + HMDState* hmds = (HMDState*)hmd->Handle; + CAPI_pNetClient->Hmd_AttachToWindow(hmds->GetNetId(), window); + hmds->pWindow = window; +#endif +#ifdef OVR_OS_WIN32 + Win32::DisplayShim::GetInstance().hWindow = (HWND)window; +#endif +#ifdef OVR_OS_MAC + OVR_UNUSED(window); +#endif + + return true; } OVR_EXPORT ovrHmd ovrHmd_CreateDebug(ovrHmdType type) { - if (!GlobalState::pInstance) - return 0; + if (!CAPI_ovrInitializeCalled) + return 0; + + HMDState* hmds = HMDState::CreateHMDState(type); - HMDState* hmds = new HMDState(type); - return hmds; + return hmds->pHmdDesc; } -OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmd) +OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmddesc) { - if (!hmd) + if (!hmddesc || !hmddesc->Handle) return; + // TBD: Any extra shutdown? - HMDState* hmds = (HMDState*)hmd; + HMDState* hmds = (HMDState*)hmddesc->Handle; { // Thread checker in its own scope, to avoid access after 'delete'. // Essentially just checks that no other RenderAPI function is executing. ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_Destroy"); } - delete (HMDState*)hmd; +#ifdef OVR_OS_WIN32 + if (hmds->pWindow) + { + // ? ok to call + //CAPI_pNetClient->Hmd_AttachToWindow(hmds->GetNetId(), 0); + hmds->pWindow = 0; + Win32::DisplayShim::GetInstance().hWindow = (HWND)0; + } +#endif + + delete (HMDState*)hmddesc->Handle; } -OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmd) +OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmddesc) { - using namespace OVR; - if (!hmd) + if (!CAPI_ovrInitializeCalled) + { + return "System initialize not called"; + } + + VirtualHmdId netId = InvalidVirtualHmdId; + + if (hmddesc && hmddesc->Handle) { - if (!GlobalState::pInstance) - return "LibOVR not initialized."; - return GlobalState::pInstance->GetLastError(); + HMDState* p = (HMDState*)hmddesc->Handle; + netId = p->GetNetId(); } - HMDState* p = (HMDState*)hmd; - return p->GetLastError(); + + return CAPI_pNetClient->Hmd_GetLastError(netId); } +#define OVR_VERSION_LIBOVR_PFX "libOVR:" + +// Returns version string representing libOVR version. Static, so +// string remains valid for app lifespan +OVR_EXPORT const char* ovr_GetVersionString() +{ + static const char* version = OVR_VERSION_LIBOVR_PFX OVR_VERSION_STRING; + return version + sizeof(OVR_VERSION_LIBOVR_PFX) - 1; +} + + //------------------------------------------------------------------------------------- // Returns capability bits that are enabled at this time; described by ovrHmdCapBits. // Note that this value is different font ovrHmdDesc::Caps, which describes what // capabilities are available. -OVR_EXPORT unsigned int ovrHmd_GetEnabledCaps(ovrHmd hmd) +OVR_EXPORT unsigned int ovrHmd_GetEnabledCaps(ovrHmd hmddesc) { - HMDState* p = (HMDState*)hmd; + HMDState* p = (HMDState*)hmddesc->Handle; return p ? p->EnabledHmdCaps : 0; } // Modifies capability bits described by ovrHmdCapBits that can be modified, -// such as ovrHmd_LowPersistance. -OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmd, unsigned int capsBits) +// such as ovrHmdCap_LowPersistance. +OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmddesc, unsigned int capsBits) { - HMDState* p = (HMDState*)hmd; + HMDState* p = (HMDState*)hmddesc->Handle; if (p) + { p->SetEnabledHmdCaps(capsBits); + } } @@ -355,55 +516,59 @@ OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmd, unsigned int capsBits) // functions that have different rules (all frame access functions // must be on render thread) -OVR_EXPORT ovrBool ovrHmd_StartSensor(ovrHmd hmd, unsigned int supportedCaps, unsigned int requiredCaps) +OVR_EXPORT ovrBool ovrHmd_ConfigureTracking(ovrHmd hmddesc, unsigned int supportedCaps, + unsigned int requiredCaps) { - HMDState* p = (HMDState*)hmd; - // TBD: Decide if we null-check arguments. - return p->StartSensor(supportedCaps, requiredCaps); -} + if (hmddesc) + { + HMDState* p = (HMDState*)hmddesc->Handle; + return p->ConfigureTracking(supportedCaps, requiredCaps); + } -OVR_EXPORT void ovrHmd_StopSensor(ovrHmd hmd) -{ - HMDState* p = (HMDState*)hmd; - p->StopSensor(); + return 0; } -OVR_EXPORT void ovrHmd_ResetSensor(ovrHmd hmd) +OVR_EXPORT void ovrHmd_RecenterPose(ovrHmd hmddesc) { - HMDState* p = (HMDState*)hmd; - p->ResetSensor(); + if (hmddesc) + { + HMDState* p = (HMDState*)hmddesc->Handle; + p->TheSensorStateReader.RecenterPose(); + } } -OVR_EXPORT ovrSensorState ovrHmd_GetSensorState(ovrHmd hmd, double absTime) +OVR_EXPORT ovrTrackingState ovrHmd_GetTrackingState(ovrHmd hmddesc, double absTime) { - HMDState* p = (HMDState*)hmd; - return p->PredictedSensorState(absTime); -} + ovrTrackingState result; -// Returns information about a sensor. Only valid after SensorStart. -OVR_EXPORT ovrBool ovrHmd_GetSensorDesc(ovrHmd hmd, ovrSensorDesc* descOut) -{ - HMDState* p = (HMDState*)hmd; - return p->GetSensorDesc(descOut) ? 1 : 0; -} + if (hmddesc) + { + HMDState* p = (HMDState*)hmddesc->Handle; + result = p->PredictedTrackingState(absTime); + // Instrument data from eye pose + p->LagStats.InstrumentEyePose(result); + } + else + memset(&result, 0, sizeof(result)); +#ifdef OVR_OS_WIN32 + // Set up display code for Windows + Win32::DisplayShim::GetInstance().Active = (result.StatusFlags & ovrStatus_HmdConnected) != 0; +#endif -//------------------------------------------------------------------------------------- -// *** General Setup + return result; +} -OVR_EXPORT void ovrHmd_GetDesc(ovrHmd hmd, ovrHmdDesc* desc) -{ - HMDState* hmds = (HMDState*)hmd; - *desc = hmds->RenderState.GetDesc(); - desc->Handle = hmd; -} +//------------------------------------------------------------------------------------- +// *** General Setup // Per HMD -> calculateIdealPixelSize -OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, +OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmddesc, ovrEyeType eye, ovrFovPort fov, float pixelsPerDisplayPixel) { + ovrHmdStruct * hmd = hmddesc->Handle; if (!hmd) return Sizei(0); HMDState* hmds = (HMDState*)hmd; @@ -415,12 +580,13 @@ OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmd, ovrEyeType eye, ovrFovP OVR_EXPORT -ovrBool ovrHmd_ConfigureRendering( ovrHmd hmd, +ovrBool ovrHmd_ConfigureRendering( ovrHmd hmddesc, const ovrRenderAPIConfig* apiConfig, unsigned int distortionCaps, const ovrFovPort eyeFovIn[2], ovrEyeRenderDesc eyeRenderDescOut[2] ) { + ovrHmdStruct * hmd = hmddesc->Handle; if (!hmd) return 0; return ((HMDState*)hmd)->ConfigureRendering(eyeRenderDescOut, eyeFovIn, apiConfig, distortionCaps); @@ -429,17 +595,18 @@ ovrBool ovrHmd_ConfigureRendering( ovrHmd hmd, // TBD: MA - Deprecated, need alternative -void ovrHmd_SetVsync(ovrHmd hmd, ovrBool vsync) +void ovrHmd_SetVsync(ovrHmd hmddesc, ovrBool vsync) { + ovrHmdStruct * hmd = hmddesc->Handle; if (!hmd) return; return ((HMDState*)hmd)->TimeManager.SetVsync(vsync? true : false); } -OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmd, unsigned int frameIndex) +OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmddesc, unsigned int frameIndex) { - HMDState* hmds = (HMDState*)hmd; + HMDState* hmds = (HMDState*)hmddesc->Handle; if (!hmds) { ovrFrameTiming f; @@ -449,56 +616,66 @@ OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmd, unsigned int frameIndex) // Check: Proper configure and threading state for the call. hmds->checkRenderingConfigured("ovrHmd_BeginFrame"); - OVR_ASSERT_LOG(hmds->BeginFrameCalled == false, ("ovrHmd_BeginFrame called multiple times.")); + OVR_DEBUG_LOG_COND(hmds->BeginFrameCalled, ("ovrHmd_BeginFrame called multiple times.")); ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_BeginFrame"); hmds->BeginFrameCalled = true; hmds->BeginFrameThreadId = OVR::GetCurrentThreadId(); - return ovrHmd_BeginFrameTiming(hmd, frameIndex); + return ovrHmd_BeginFrameTiming(hmddesc, frameIndex); } // Renders textures to frame buffer -OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmd) +OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmddesc, + const ovrPosef renderPose[2], + const ovrTexture eyeTexture[2]) { - HMDState* hmds = (HMDState*)hmd; + HMDState* hmds = (HMDState*)hmddesc->Handle; if (!hmds) return; + // Instrument when the EndFrame() call started + hmds->LagStats.InstrumentEndFrameStart(ovr_GetTimeInSeconds()); + + hmds->SubmitEyeTextures(renderPose, eyeTexture); + // Debug state checks: Must be in BeginFrame, on the same thread. hmds->checkBeginFrameScope("ovrHmd_EndFrame"); ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame"); + + hmds->pRenderer->SetLatencyTestColor(hmds->LatencyTestActive ? hmds->LatencyTestDrawColor : NULL); - // TBD: Move directly into renderer - bool dk2LatencyTest = (hmds->HMDInfo.HmdType == HmdType_DK2) && - (hmds->EnabledHmdCaps & ovrHmdCap_LatencyTest); - if (dk2LatencyTest) + ovrHmd_GetLatencyTest2DrawColor(hmddesc, NULL); // We don't actually need to draw color, so send NULL + + if (hmds->pRenderer) { - hmds->LatencyTest2DrawColor[0] = hmds->TimeManager.GetFrameLatencyTestDrawColor(); - hmds->LatencyTest2DrawColor[1] = hmds->LatencyTest2DrawColor[0]; - hmds->LatencyTest2DrawColor[2] = hmds->LatencyTest2DrawColor[0]; - } + hmds->pRenderer->SaveGraphicsState(); - if (hmds->pRenderer) - { - hmds->pRenderer->SaveGraphicsState(); - hmds->pRenderer->EndFrame(true, - hmds->LatencyTestActive ? hmds->LatencyTestDrawColor : NULL, - - // MA: Use this color since we are running DK2 test all the time. - dk2LatencyTest ? hmds->LatencyTest2DrawColor : 0 - //hmds->LatencyTest2Active ? hmds->LatencyTest2DrawColor : NULL - ); - hmds->pRenderer->RestoreGraphicsState(); + // See if we need to show the HSWDisplay. + if (hmds->pHSWDisplay) // Until we know that these are valid, assume any of them can't be. + { + ovrHSWDisplayState hswDisplayState; + hmds->pHSWDisplay->TickState(&hswDisplayState, true); // This may internally call HASWarning::Display. + + if (hswDisplayState.Displayed) + { + hmds->pHSWDisplay->Render(ovrEye_Left, &eyeTexture[ovrEye_Left]); + hmds->pHSWDisplay->Render(ovrEye_Right, &eyeTexture[ovrEye_Right]); + } + } + + hmds->pRenderer->EndFrame(true); + hmds->pRenderer->RestoreGraphicsState(); } + // Call after present - ovrHmd_EndFrameTiming(hmd); + ovrHmd_EndFrameTiming(hmddesc); - if (dk2LatencyTest) - { - hmds->TimeManager.UpdateFrameLatencyTrackingAfterEndFrame( - hmds->LatencyTest2DrawColor[0], hmds->LatencyUtil2.GetLocklessState()); - } + // Instrument latency tester + hmds->LagStats.InstrumentLatencyTimings(hmds->TimeManager); + + // Instrument when the EndFrame() call ended + hmds->LagStats.InstrumentEndFrameEnd(ovr_GetTimeInSeconds()); // Out of BeginFrame hmds->BeginFrameThreadId = 0; @@ -506,28 +683,27 @@ OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmd) } -OVR_EXPORT ovrPosef ovrHmd_BeginEyeRender(ovrHmd hmd, ovrEyeType eye) -{ - HMDState* hmds = (HMDState*)hmd; - if (!hmds) return ovrPosef(); - return hmds->BeginEyeRender(eye); -} - -OVR_EXPORT void ovrHmd_EndEyeRender(ovrHmd hmd, ovrEyeType eye, - ovrPosef renderPose, ovrTexture* eyeTexture) +// Not exposed as part of public API +OVR_EXPORT void ovrHmd_RegisterPostDistortionCallback(ovrHmd hmddesc, PostDistortionCallback callback) { - HMDState* hmds = (HMDState*)hmd; + HMDState* hmds = (HMDState*)hmddesc->Handle; if (!hmds) return; - hmds->EndEyeRender(eye, renderPose, eyeTexture); + + if (hmds->pRenderer) + { + hmds->pRenderer->RegisterPostDistortionCallback(callback); + } } + //------------------------------------------------------------------------------------- // ***** Frame Timing logic -OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmd, unsigned int frameIndex) +OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmddesc, unsigned int frameIndex) { + ovrHmdStruct * hmd = hmddesc->Handle; ovrFrameTiming f; memset(&f, 0, sizeof(f)); @@ -536,12 +712,12 @@ OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmd, unsigned int frameIn { FrameTimeManager::Timing frameTiming = hmds->TimeManager.GetFrameTiming(frameIndex); - f.ThisFrameSeconds = frameTiming.ThisFrameTime; + f.ThisFrameSeconds = frameTiming.ThisFrameTime; f.NextFrameSeconds = frameTiming.NextFrameTime; - f.TimewarpPointSeconds = frameTiming.TimewarpPointTime; - f.ScanoutMidpointSeconds= frameTiming.MidpointTime; - f.EyeScanoutSeconds[0] = frameTiming.EyeRenderTimes[0]; - f.EyeScanoutSeconds[1] = frameTiming.EyeRenderTimes[1]; + f.TimewarpPointSeconds = frameTiming.TimewarpPointTime; + f.ScanoutMidpointSeconds = frameTiming.MidpointTime; + f.EyeScanoutSeconds[0] = frameTiming.EyeRenderTimes[0]; + f.EyeScanoutSeconds[1] = frameTiming.EyeRenderTimes[1]; // Compute DeltaSeconds. f.DeltaSeconds = (hmds->LastGetFrameTimeSeconds == 0.0f) ? 0.0f : @@ -554,8 +730,9 @@ OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmd, unsigned int frameIn return f; } -OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmd, unsigned int frameIndex) +OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmddesc, unsigned int frameIndex) { + ovrHmdStruct * hmd = hmddesc->Handle; ovrFrameTiming f; memset(&f, 0, sizeof(f)); @@ -563,16 +740,16 @@ OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmd, unsigned int frame if (!hmds) return f; // Check: Proper state for the call. - OVR_ASSERT_LOG(hmds->BeginFrameTimingCalled == false, - ("ovrHmd_BeginFrameTiming called multiple times.")); + OVR_DEBUG_LOG_COND(hmds->BeginFrameTimingCalled, + ("ovrHmd_BeginFrameTiming called multiple times.")); hmds->BeginFrameTimingCalled = true; double thisFrameTime = hmds->TimeManager.BeginFrame(frameIndex); const FrameTimeManager::Timing &frameTiming = hmds->TimeManager.GetFrameTiming(); - f.ThisFrameSeconds = thisFrameTime; - f.NextFrameSeconds = frameTiming.NextFrameTime; + f.ThisFrameSeconds = thisFrameTime; + f.NextFrameSeconds = frameTiming.NextFrameTime; f.TimewarpPointSeconds = frameTiming.TimewarpPointTime; f.ScanoutMidpointSeconds= frameTiming.MidpointTime; f.EyeScanoutSeconds[0] = frameTiming.EyeRenderTimes[0]; @@ -580,7 +757,7 @@ OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmd, unsigned int frame // Compute DeltaSeconds. f.DeltaSeconds = (hmds->LastFrameTimeSeconds == 0.0f) ? 0.0f : - (float) (thisFrameTime - hmds->LastFrameTimeSeconds); + (float) (thisFrameTime - hmds->LastFrameTimeSeconds); hmds->LastFrameTimeSeconds = thisFrameTime; if (f.DeltaSeconds > 1.0f) f.DeltaSeconds = 1.0f; @@ -589,24 +766,33 @@ OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmd, unsigned int frame } -OVR_EXPORT void ovrHmd_EndFrameTiming(ovrHmd hmd) +OVR_EXPORT void ovrHmd_EndFrameTiming(ovrHmd hmddesc) { - HMDState* hmds = (HMDState*)hmd; + HMDState* hmds = (HMDState*)hmddesc->Handle; if (!hmds) return; // Debug state checks: Must be in BeginFrameTiming, on the same thread. hmds->checkBeginFrameTimingScope("ovrHmd_EndTiming"); - // MA TBD: Correct chek or not? + // MA TBD: Correct check or not? // ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame"); hmds->TimeManager.EndFrame(); hmds->BeginFrameTimingCalled = false; + + bool dk2LatencyTest = (hmds->EnabledHmdCaps & ovrHmdCap_DynamicPrediction) != 0; + if (dk2LatencyTest) + { + Util::FrameTimeRecordSet recordset; + hmds->TheLatencyTestStateReader.GetRecordSet(recordset); + hmds->TimeManager.UpdateFrameLatencyTrackingAfterEndFrame( hmds->LatencyTest2DrawColor, + recordset); + } } -OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmd, unsigned int frameIndex) +OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmddesc, unsigned int frameIndex) { - HMDState* hmds = (HMDState*)hmd; + HMDState* hmds = (HMDState*)hmddesc->Handle; if (!hmds) return; hmds->TimeManager.ResetFrameTiming(frameIndex, @@ -616,29 +802,69 @@ OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmd, unsigned int frameIndex) hmds->LastGetFrameTimeSeconds = 0.0; } +OVR_EXPORT void ovrHmd_GetEyePoses(ovrHmd hmd, unsigned int frameIndex, ovrVector3f hmdToEyeViewOffset[2], + ovrPosef outEyePoses[2], ovrTrackingState* outHmdTrackingState) +{ + HMDState* hmds = (HMDState*)hmd->Handle; + if (!hmds) return; + + hmds->LatencyTestActive = hmds->ProcessLatencyTest(hmds->LatencyTestDrawColor); + + ovrTrackingState hmdTrackingState = hmds->TimeManager.GetEyePredictionTracking(hmd, ovrEye_Count, frameIndex); + ovrPosef hmdPose = hmdTrackingState.HeadPose.ThePose; + + // caller passed in a valid pointer, so copy to output + if(outHmdTrackingState) + *outHmdTrackingState = hmdTrackingState; + // Currently HmdToEyeViewOffset is only a 3D vector + // (Negate HmdToEyeViewOffset because offset is a view matrix offset and not a camera offset) + outEyePoses[0] = Posef(hmdPose.Orientation, ((Posef)hmdPose).Apply(-((Vector3f)hmdToEyeViewOffset[0]))); + outEyePoses[1] = Posef(hmdPose.Orientation, ((Posef)hmdPose).Apply(-((Vector3f)hmdToEyeViewOffset[1]))); + + // Instrument data from eye pose + hmds->LagStats.InstrumentEyePose(hmdTrackingState); +} -OVR_EXPORT ovrPosef ovrHmd_GetEyePose(ovrHmd hmd, ovrEyeType eye) +ovrPosef ovrHmd_GetHmdPosePerEye(ovrHmd hmd, ovrEyeType eye) { - HMDState* hmds = (HMDState*)hmd; + HMDState* hmds = (HMDState*)hmd->Handle; if (!hmds) return ovrPosef(); + // This isn't a great place, but since we removed ovrHmd_BeginEyeRender... + // Only process latency tester for drawing the left eye (assumes left eye is drawn first) + if (hmds->pRenderer && eye == 0) + { + hmds->LatencyTestActive = hmds->ProcessLatencyTest(hmds->LatencyTestDrawColor); + } + hmds->checkBeginFrameTimingScope("ovrHmd_GetEyePose"); return hmds->TimeManager.GetEyePredictionPose(hmd, eye); } +OVR_EXPORT void ovrHmd_AddDistortionTimeMeasurement(ovrHmd hmddesc, double distortionTimeSeconds) +{ + if (!hmddesc) + return; + HMDState* hmds = (HMDState*)hmddesc->Handle; -OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, - ovrPosef renderPose, ovrMatrix4f twmOut[2]) + hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices"); + hmds->TimeManager.AddDistortionTimeMeasurement(distortionTimeSeconds); +} + + + +OVR_EXPORT void ovrHmd_GetEyeTimewarpMatricesDebug(ovrHmd hmddesc, ovrEyeType eye, + ovrPosef renderPose, ovrMatrix4f twmOut[2],double debugTimingOffsetInSeconds) { - HMDState* hmds = (HMDState*)hmd; - if (!hmd) + if (!hmddesc) return; + HMDState* hmds = (HMDState*)hmddesc->Handle; // Debug checks: BeginFrame was called, on the same thread. hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices"); - hmds->TimeManager.GetTimewarpMatrices(hmd, eye, renderPose, twmOut); + hmds->TimeManager.GetTimewarpMatrices(hmddesc, eye, renderPose, twmOut, debugTimingOffsetInSeconds); /* // MA: Took this out because new latency test approach just sames @@ -652,20 +878,27 @@ OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, } +OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmddesc, ovrEyeType eye, + ovrPosef renderPose, ovrMatrix4f twmOut[2]) +{ + return(ovrHmd_GetEyeTimewarpMatricesDebug(hmddesc, eye, renderPose, twmOut, 0.0)); +} + + -OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmd, +OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmddesc, ovrEyeType eyeType, ovrFovPort fov) { + HMDState* hmds = (HMDState*)hmddesc->Handle; ovrEyeRenderDesc erd; - HMDState* hmds = (HMDState*)hmd; if (!hmds) { memset(&erd, 0, sizeof(erd)); return erd; } - return hmds->RenderState.calcRenderDesc(eyeType, fov); + return hmds->RenderState.CalcRenderDesc(eyeType, fov); } @@ -674,67 +907,34 @@ OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmd, -// Generate distortion mesh per eye. -// scaleAndOffsetOut - this will be needed for shader -OVR_EXPORT ovrBool ovrHmd_CreateDistortionMesh( ovrHmd hmd, + +OVR_EXPORT ovrBool ovrHmd_CreateDistortionMeshDebug( ovrHmd hmddesc, ovrEyeType eyeType, ovrFovPort fov, unsigned int distortionCaps, - ovrDistortionMesh *meshData ) + ovrDistortionMesh *meshData, + float debugEyeReliefOverrideInMetres) { - if (!meshData) - return 0; - HMDState* hmds = (HMDState*)hmd; - - // Not used now, but Chromatic flag or others could possibly be checked for in the future. - OVR_UNUSED1(distortionCaps); - -#if defined (OVR_OS_WIN32) - // TBD: We should probably be sharing some C API structures with C++ to avoid this mess... - OVR_COMPILER_ASSERT(sizeof(DistortionMeshVertexData) == sizeof(ovrDistortionVertex)); - OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, ScreenPosNDC) == OVR_OFFSET_OF(ovrDistortionVertex, Pos)); - OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TimewarpLerp) == OVR_OFFSET_OF(ovrDistortionVertex, TimeWarpFactor)); - OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, Shade) == OVR_OFFSET_OF(ovrDistortionVertex, VignetteFactor)); - OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TanEyeAnglesR) == OVR_OFFSET_OF(ovrDistortionVertex, TexR)); - OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TanEyeAnglesG) == OVR_OFFSET_OF(ovrDistortionVertex, TexG)); - OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TanEyeAnglesB) == OVR_OFFSET_OF(ovrDistortionVertex, TexB)); -#endif - - - // *** Calculate a part of "StereoParams" needed for mesh generation - - // Note that mesh distortion generation is invariant of RenderTarget UVs, allowing - // render target size and location to be changed after the fact dynamically. - // eyeToSourceUV is computed here for convenience, so that users don't need - // to call ovrHmd_GetRenderScaleAndOffset unless changing RT dynamically. - - const HmdRenderInfo& hmdri = hmds->RenderState.RenderInfo; - StereoEye stereoEye = (eyeType == ovrEye_Left) ? StereoEye_Left : StereoEye_Right; + // The 'internal' function below can be found in CAPI_HMDState. + // Not ideal, but navigating the convolutions of what compiles + // where, meant they are in the few places which actually lets these compile. + // Please relocate (if you wish) to a more meaningful place if you can navigate the compiler gymnastics :) + return(ovrHmd_CreateDistortionMeshInternal( hmddesc->Handle, + eyeType, fov, + distortionCaps, + meshData, + debugEyeReliefOverrideInMetres)); - const DistortionRenderDesc& distortion = hmds->RenderState.Distortion[eyeType]; - - // Find the mapping from TanAngle space to target NDC space. - ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(fov); - - int triangleCount = 0; - int vertexCount = 0; - - DistortionMeshCreate((DistortionMeshVertexData**)&meshData->pVertexData, (UInt16**)&meshData->pIndexData, - &vertexCount, &triangleCount, - (stereoEye == StereoEye_Right), - hmdri, distortion, eyeToSourceNDC); - - if (meshData->pVertexData) - { - // Convert to index - meshData->IndexCount = triangleCount * 3; - meshData->VertexCount = vertexCount; - return 1; - } - - return 0; +} +OVR_EXPORT ovrBool ovrHmd_CreateDistortionMesh( ovrHmd hmddesc, + ovrEyeType eyeType, ovrFovPort fov, + unsigned int distortionCaps, + ovrDistortionMesh *meshData) +{ + return(ovrHmd_CreateDistortionMeshDebug( hmddesc, eyeType, fov, distortionCaps,meshData, 0)); } + // Frees distortion mesh allocated by ovrHmd_GenerateDistortionMesh. meshData elements // are set to null and 0s after the call. OVR_EXPORT void ovrHmd_DestroyDistortionMesh(ovrDistortionMesh* meshData) @@ -771,155 +971,319 @@ OVR_EXPORT void ovrHmd_GetRenderScaleAndOffset( ovrFovPort fov, //------------------------------------------------------------------------------------- // ***** Latency Test interface -OVR_EXPORT ovrBool ovrHmd_GetLatencyTestDrawColor(ovrHmd hmd, unsigned char rgbColorOut[3]) +OVR_EXPORT ovrBool ovrHmd_GetLatencyTestDrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3]) { - HMDState* p = (HMDState*)hmd; + HMDState* p = (HMDState*)hmddesc->Handle; rgbColorOut[0] = p->LatencyTestDrawColor[0]; rgbColorOut[1] = p->LatencyTestDrawColor[1]; rgbColorOut[2] = p->LatencyTestDrawColor[2]; return p->LatencyTestActive; } -OVR_EXPORT const char* ovrHmd_GetLatencyTestResult(ovrHmd hmd) +OVR_EXPORT ovrBool ovrHmd_ProcessLatencyTest(ovrHmd hmddesc, unsigned char rgbColorOut[3]) +{ + OVR_UNUSED(hmddesc); + return NetClient::GetInstance()->LatencyUtil_ProcessInputs(Timer::GetSeconds(), rgbColorOut); +} + +OVR_EXPORT const char* ovrHmd_GetLatencyTestResult(ovrHmd hmddesc) { - HMDState* p = (HMDState*)hmd; - return p->LatencyUtil.GetResultsString(); + OVR_UNUSED(hmddesc); + return NetClient::GetInstance()->LatencyUtil_GetResultsString(); } -OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmd) +OVR_EXPORT ovrBool ovrHmd_GetLatencyTest2DrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3]) { - HMDState* p = (HMDState*)hmd; + HMDState* hmds = (HMDState*)hmddesc->Handle; + if (!hmds) return false; + + // TBD: Move directly into renderer + bool dk2LatencyTest = (hmds->EnabledHmdCaps & ovrHmdCap_DynamicPrediction) != 0; + if (dk2LatencyTest) + { + hmds->TimeManager.GetFrameLatencyTestDrawColor(hmds->LatencyTest2DrawColor); + if(rgbColorOut != NULL) + { + rgbColorOut[0] = hmds->LatencyTest2DrawColor[0]; + rgbColorOut[1] = hmds->LatencyTest2DrawColor[1]; + rgbColorOut[2] = hmds->LatencyTest2DrawColor[2]; + } + + if(hmds->pRenderer != NULL) + hmds->pRenderer->SetLatencyTest2Color(hmds->LatencyTest2DrawColor); + } + else + { + if(hmds->pRenderer != NULL) + hmds->pRenderer->SetLatencyTest2Color(NULL); + } + + return dk2LatencyTest; +} + + +OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmddesc) +{ + HMDState* p = (HMDState*)hmddesc->Handle; // MA Test - float latencies[3]; - p->TimeManager.GetLatencyTimings(latencies); - return latencies[2]; - // return p->LatencyUtil2.GetMeasuredLatency(); + float latencyRender, latencyTimewarp, latencyPostPresent; + + p->TimeManager.GetLatencyTimings(latencyRender, latencyTimewarp, latencyPostPresent); + return latencyPostPresent; } -// ----------------------------------------------------------------------------------- -// ***** Property Access +//------------------------------------------------------------------------------------- +// ***** Health and Safety Warning Display interface +// -OVR_EXPORT float ovrHmd_GetFloat(ovrHmd hmd, const char* propertyName, float defaultVal) +OVR_EXPORT void ovrHmd_GetHSWDisplayState(ovrHmd hmd, ovrHSWDisplayState *hswDisplayState) { - HMDState* hmds = (HMDState*)hmd; - if (hmds) - { - return hmds->getFloatValue(propertyName, defaultVal); - } + OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle; + + if (pHMDState) + { + OVR::CAPI::HSWDisplay* pHSWDisplay = pHMDState->pHSWDisplay; - return defaultVal; + if(pHSWDisplay) + pHSWDisplay->TickState(hswDisplayState); // This may internally call HSWDisplay::Display. + } } -OVR_EXPORT ovrBool ovrHmd_SetFloat(ovrHmd hmd, const char* propertyName, float value) +OVR_EXPORT ovrBool ovrHmd_DismissHSWDisplay(ovrHmd hmd) { - HMDState* hmds = (HMDState*)hmd; - if (hmds) + OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle; + + if (pHMDState) { - return hmds->setFloatValue(propertyName, value); + OVR::CAPI::HSWDisplay* pHSWDisplay = pHMDState->pHSWDisplay; + + if(pHSWDisplay) + return (pHSWDisplay->Dismiss() ? 1 : 0); } + return false; } +// ----------------------------------------------------------------------------------- +// ***** Property Access +OVR_EXPORT ovrBool ovrHmd_GetBool(ovrHmd hmddesc, + const char* propertyName, + ovrBool defaultVal) +{ + OVR_ASSERT(propertyName); + if (hmddesc) + { + HMDState* hmds = (HMDState*)hmddesc->Handle; + OVR_ASSERT(hmds); + if (hmds) + { + return hmds->getBoolValue(propertyName, (defaultVal != 0)); + } + } -OVR_EXPORT unsigned int ovrHmd_GetFloatArray(ovrHmd hmd, const char* propertyName, - float values[], unsigned int arraySize) + return NetClient::GetInstance()->GetBoolValue(InvalidVirtualHmdId, propertyName, (defaultVal != 0)) ? 1 : 0; +} + +OVR_EXPORT ovrBool ovrHmd_SetBool(ovrHmd hmddesc, + const char* propertyName, + ovrBool value) { - HMDState* hmds = (HMDState*)hmd; - if (hmds) - { - return hmds->getFloatArray(propertyName, values, arraySize); + OVR_ASSERT(propertyName); + if (hmddesc) + { + HMDState* hmds = (HMDState*)hmddesc->Handle; + OVR_ASSERT(hmds); + if (hmds) + { + return hmds->setBoolValue(propertyName, value != 0) ? 1 : 0; + } } - return 0; + return NetClient::GetInstance()->SetBoolValue(InvalidVirtualHmdId, propertyName, (value != 0)) ? 1 : 0; } +OVR_EXPORT int ovrHmd_GetInt(ovrHmd hmddesc, + const char* propertyName, + int defaultVal) +{ + OVR_ASSERT(propertyName); + if (hmddesc) + { + HMDState* hmds = (HMDState*)hmddesc->Handle; + OVR_ASSERT(hmds); + if (hmds) + { + return hmds->getIntValue(propertyName, defaultVal); + } + } + + return NetClient::GetInstance()->GetIntValue(InvalidVirtualHmdId, propertyName, defaultVal); +} -// Modify float[] property; false if property doesn't exist or is readonly. -OVR_EXPORT ovrBool ovrHmd_SetFloatArray(ovrHmd hmd, const char* propertyName, - float values[], unsigned int arraySize) +OVR_EXPORT ovrBool ovrHmd_SetInt(ovrHmd hmddesc, + const char* propertyName, + int value) { - HMDState* hmds = (HMDState*)hmd; - if (hmds) - { - return hmds->setFloatArray(propertyName, values, arraySize); + OVR_ASSERT(propertyName); + if (hmddesc) + { + HMDState* hmds = (HMDState*)hmddesc->Handle; + OVR_ASSERT(hmds); + if (hmds) + { + return hmds->setIntValue(propertyName, value) ? 1 : 0; + } } - return 0; + return NetClient::GetInstance()->SetIntValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0; } -OVR_EXPORT const char* ovrHmd_GetString(ovrHmd hmd, const char* propertyName, - const char* defaultVal) +OVR_EXPORT float ovrHmd_GetFloat(ovrHmd hmddesc, + const char* propertyName, + float defaultVal) { - HMDState* hmds = (HMDState*)hmd; - if (hmds) + OVR_ASSERT(propertyName); + if (hmddesc) { - return hmds->getString(propertyName, defaultVal); + HMDState* hmds = (HMDState*)hmddesc->Handle; + OVR_ASSERT(hmds); + if (hmds) + { + return hmds->getFloatValue(propertyName, defaultVal); + } } - return defaultVal; + return (float)NetClient::GetInstance()->GetNumberValue(InvalidVirtualHmdId, propertyName, defaultVal); } - -/* Not needed yet. -// Get array of strings, i.e. const char* [] property. -// Returns the number of elements filled in, 0 if property doesn't exist. -// Maximum of arraySize elements will be written. -// String memory is guaranteed to exist until next call to GetString or GetStringArray, or HMD is destroyed. -OVR_EXPORT -unsigned int ovrHmd_GetStringArray(ovrHmd hmd, const char* propertyName, - const char* values[], unsigned int arraySize) +OVR_EXPORT ovrBool ovrHmd_SetFloat(ovrHmd hmddesc, + const char* propertyName, + float value) { - HMDState* hmds = (HMDState*)hmd; - if (hmds && hmds->pHMD && arraySize) - { - Profile* p = hmds->pHMD->GetProfile(); + OVR_ASSERT(propertyName); + if (hmddesc) + { + HMDState* hmds = (HMDState*)hmddesc->Handle; + OVR_ASSERT(hmds); + if (hmds) + { + return hmds->setFloatValue(propertyName, value) ? 1 : 0; + } + } - hmds->LastGetStringValue[0] = 0; - if (p && p->GetValue(propertyName, hmds->LastGetStringValue, sizeof(hmds->LastGetStringValue))) + return NetClient::GetInstance()->SetNumberValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0; +} + +OVR_EXPORT unsigned int ovrHmd_GetFloatArray(ovrHmd hmddesc, + const char* propertyName, + float values[], + unsigned int arraySize) +{ + OVR_ASSERT(propertyName); + OVR_ASSERT(hmddesc); + if (hmddesc) + { + HMDState* hmds = (HMDState*)hmddesc->Handle; + OVR_ASSERT(hmds); + if (hmds) { - values[0] = hmds->LastGetStringValue; - return 1; + return hmds->getFloatArray(propertyName, values, arraySize); } } + // FIXME: Currently it takes a few lines of code to do this, so just not supported for now. return 0; } -*/ -// Returns array size of a property, 0 if property doesn't exist. -// Can be used to check existence of a property. -OVR_EXPORT unsigned int ovrHmd_GetArraySize(ovrHmd hmd, const char* propertyName) +// Modify float[] property; false if property doesn't exist or is readonly. +OVR_EXPORT ovrBool ovrHmd_SetFloatArray(ovrHmd hmddesc, + const char* propertyName, + float values[], + unsigned int arraySize) { - HMDState* hmds = (HMDState*)hmd; - if (hmds && hmds->pHMD) + OVR_ASSERT(propertyName); + OVR_ASSERT(hmddesc); + if (hmddesc) { - // For now, just access the profile. - Profile* p = hmds->pHMD->GetProfile(); - - if (p) - return p->GetNumValues(propertyName); + HMDState* hmds = (HMDState*)hmddesc->Handle; + OVR_ASSERT(hmds); + if (hmds) + { + return hmds->setFloatArray(propertyName, values, arraySize) ? 1 : 0; + } } + + // FIXME: Currently it takes a few lines of code to do this, so just not supported for now. return 0; } +OVR_EXPORT const char* ovrHmd_GetString(ovrHmd hmddesc, + const char* propertyName, + const char* defaultVal) +{ + OVR_ASSERT(propertyName); + if (hmddesc) + { + HMDState* hmds = (HMDState*)hmddesc->Handle; + if (hmds) + { + return hmds->getString(propertyName, defaultVal); + } + } -#ifdef __cplusplus -} // extern "C" -#endif + return NetClient::GetInstance()->GetStringValue(InvalidVirtualHmdId, propertyName, defaultVal); +} + +OVR_EXPORT ovrBool ovrHmd_SetString(ovrHmd hmddesc, + const char* propertyName, + const char* value) +{ + OVR_ASSERT(propertyName); + if (hmddesc) + { + HMDState* hmds = (HMDState*)hmddesc->Handle; + if (hmds) + { + return hmds->setString(propertyName, value) ? 1 : 0; + } + } + return NetClient::GetInstance()->SetStringValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0; +} -//------------------------------------------------------------------------------------- -// ****** Special access for VRConfig +// ----------------------------------------------------------------------------------- +// ***** Logging -// Return the sensor fusion object for the purposes of magnetometer calibration. The -// function is private and is only exposed through VRConfig header declarations -OVR::SensorFusion* ovrHmd_GetSensorFusion(ovrHmd hmd) +OVR_EXPORT ovrBool ovrHmd_StartPerfLog(ovrHmd hmd, const char* fileName, const char* userData1) { - HMDState* p = (HMDState*)hmd; - return &p->SFusion; + OVR_ASSERT(fileName && fileName[0]); + + OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle; + + if (pHMDState) + { + ovrBool started = pHMDState->LagStatsCSV.Start(fileName, userData1) ? 1 : 0; + if (started) + pHMDState->LagStats.AddResultsObserver(pHMDState->LagStatsCSV.GetObserver()); + return started; + } + return 0; } +OVR_EXPORT ovrBool ovrHmd_StopPerfLog(ovrHmd hmd) +{ + OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle; + if (pHMDState) + { + return pHMDState->LagStatsCSV.Stop() ? 1 : 0; + } + return false; +} + +#ifdef __cplusplus +} // extern "C" +#endif |