diff options
Diffstat (limited to 'LibOVR/Src/CAPI/CAPI_HMDState.cpp')
-rwxr-xr-x[-rw-r--r--] | LibOVR/Src/CAPI/CAPI_HMDState.cpp | 782 |
1 files changed, 523 insertions, 259 deletions
diff --git a/LibOVR/Src/CAPI/CAPI_HMDState.cpp b/LibOVR/Src/CAPI/CAPI_HMDState.cpp index 923abe0..7a00bf8 100644..100755 --- a/LibOVR/Src/CAPI/CAPI_HMDState.cpp +++ b/LibOVR/Src/CAPI/CAPI_HMDState.cpp @@ -27,122 +27,84 @@ limitations under the License. #include "CAPI_HMDState.h" #include "../OVR_Profile.h" #include "../Service/Service_NetClient.h" + #if !defined(HEADLESS_APP) #ifdef OVR_OS_WIN32 -#include "../Displays/OVR_Win32_ShimFunctions.h" + #include "../Displays/OVR_Win32_ShimFunctions.h" + + // For auto-detection of window handle for direct mode: + #include <OVR_CAPI_D3D.h> + #include <GL/CAPI_GLE.h> + #include <OVR_CAPI_GL.h> + +#elif defined(OVR_OS_LINUX) + + #include "../Displays/OVR_Linux_SDKWindow.h" // For screen rotation + #endif #endif /* !defined(HEADLESS_APP) */ +#include "Tracing/Tracing.h" + namespace OVR { namespace CAPI { -// Accessed via HMDState::GetHMDStateList() +// Accessed via HMDState::EnumerateHMDStateList() +static OVR::Lock hmdStateListLock; static OVR::List<HMDState> hmdStateList; // List of all created HMDStates. //------------------------------------------------------------------------------------- // ***** HMDState -HMDState::HMDState(const OVR::Service::HMDNetworkInfo& netInfo, - const OVR::HMDInfo& hmdInfo, - Profile* profile, - Service::NetClient* client) : +HMDState::HMDState(HMDInfo const & hmdInfo, + Profile* profile, + Service::HMDNetworkInfo const * netInfo, + Service::NetClient* client) : + TimewarpTimer(), + RenderTimer(), + RenderIMUTimeSeconds(0.), pProfile(profile), pHmdDesc(0), pWindow(0), pClient(client), - NetId(netInfo.NetId), - NetInfo(netInfo), - OurHMDInfo(hmdInfo), - pLastError(NULL), - EnabledHmdCaps(0), - EnabledServiceHmdCaps(0), - SharedStateReader(), - TheSensorStateReader(), - TheLatencyTestStateReader(), - LatencyTestActive(false), - //LatencyTestDrawColor(), - LatencyTest2Active(false), - //LatencyTest2DrawColor(), - TimeManager(true), - RenderState(), - pRenderer(), -#if !defined(HEADLESS_APP) - pHSWDisplay(), -#endif /* !defined(HEADLESS_APP) */ - LastFrameTimeSeconds(0.), - LastGetFrameTimeSeconds(0.), - //LastGetStringValue(), - RenderingConfigured(false), - BeginFrameCalled(false), - BeginFrameThreadId(), - RenderAPIThreadChecker(), - BeginFrameTimingCalled(false) -{ - sharedInit(profile); - hmdStateList.PushBack(this); -} - - -HMDState::HMDState(const OVR::HMDInfo& hmdInfo, Profile* profile) : - pProfile(profile), - pHmdDesc(0), - pWindow(0), - pClient(0), NetId(InvalidVirtualHmdId), NetInfo(), OurHMDInfo(hmdInfo), - pLastError(NULL), + pLastError(nullptr), EnabledHmdCaps(0), EnabledServiceHmdCaps(0), - SharedStateReader(), - TheSensorStateReader(), + CombinedHmdReader(), + TheTrackingStateReader(), TheLatencyTestStateReader(), LatencyTestActive(false), //LatencyTestDrawColor(), LatencyTest2Active(false), //LatencyTest2DrawColor(), - TimeManager(true), + ScreenLatencyTracker(), RenderState(), pRenderer(), #if !defined(HEADLESS_APP) pHSWDisplay(), #endif /* !defined(HEADLESS_APP) */ - LastFrameTimeSeconds(0.), - LastGetFrameTimeSeconds(0.), //LastGetStringValue(), RenderingConfigured(false), BeginFrameCalled(false), BeginFrameThreadId(), + BeginFrameIndex(0), RenderAPIThreadChecker(), BeginFrameTimingCalled(false) { - sharedInit(profile); - hmdStateList.PushBack(this); -} - -HMDState::~HMDState() -{ - hmdStateList.Remove(this); - - if (pClient) + if (netInfo) { - pClient->Hmd_Release(NetId); - pClient = 0; + NetId = netInfo->NetId; + NetInfo = *netInfo; } - ConfigureRendering(0,0,0,0); + // Hook up the app timing lockless updater + RenderTimer.SetUpdater(TimewarpTimer.GetUpdater()); - if (pHmdDesc) - { - OVR_FREE(pHmdDesc); - pHmdDesc = NULL; - } -} - -void HMDState::sharedInit(Profile* profile) -{ // TBD: We should probably be looking up the default profile for the given // device type + user if profile == 0. pLastError = 0; @@ -160,10 +122,12 @@ void HMDState::sharedInit(Profile* profile) RenderState.ClearColor[1] = 0.0f; RenderState.ClearColor[2] = 0.0f; RenderState.ClearColor[3] = 0.0f; - RenderState.EnabledHmdCaps = 0; - TimeManager.Init(RenderState.RenderInfo); + if (!TimewarpTimer.Initialize(&RenderState, &ScreenLatencyTracker)) + { + OVR_ASSERT(false); + } /* LatencyTestDrawColor[0] = 0; @@ -171,9 +135,9 @@ void HMDState::sharedInit(Profile* profile) LatencyTestDrawColor[2] = 0; */ - RenderingConfigured = false; - BeginFrameCalled = false; - BeginFrameThreadId = 0; + RenderingConfigured = false; + BeginFrameCalled = false; + BeginFrameThreadId = 0; BeginFrameTimingCalled = false; #if !defined(HEADLESS_APP) @@ -181,11 +145,52 @@ void HMDState::sharedInit(Profile* profile) if(!pHSWDisplay) { pHSWDisplay = *OVR::CAPI::HSWDisplay::Factory(ovrRenderAPI_None, pHmdDesc, RenderState); - pHSWDisplay->Enable(pProfile->GetBoolValue("HSW", true)); + } + + RenderIMUTimeSeconds = 0.; + + hmdStateListLock.DoLock(); + hmdStateList.PushBack(this); + hmdStateListLock.Unlock(); +} + +HMDState::~HMDState() +{ + hmdStateListLock.DoLock(); + hmdStateList.Remove(this); + hmdStateListLock.Unlock(); + + if (pClient) + { + pClient->Hmd_Release(NetId); + pClient = 0; + } + + ConfigureRendering(0,0,0,0); + + if (pHmdDesc) + { + OVR_FREE(pHmdDesc); + pHmdDesc = nullptr; } #endif /* !defined(HEADLESS_APP) */ } +bool HMDState::InitializeSharedState() +{ + if (!CombinedHmdReader.Open(NetInfo.SharedMemoryName.Hmd.ToCStr()) || + !CameraReader.Open(NetInfo.SharedMemoryName.Camera.ToCStr())) + { + return false; + } + + TheTrackingStateReader.SetUpdaters(CombinedHmdReader.Get(), CameraReader.Get()); + TheLatencyTestStateReader.SetUpdater(CombinedHmdReader.Get()); + + + return true; +} + static Vector3f GetNeckModelFromProfile(Profile* profile) { OVR_ASSERT(profile); @@ -228,7 +233,9 @@ static float GetCenterPupilDepthFromRenderInfo(HmdRenderInfo* hmdRenderInfo) void HMDState::UpdateRenderProfile(Profile* profile) { // Apply the given profile to generate a render context - RenderState.RenderInfo = GenerateHmdRenderInfoFromHmdInfo(RenderState.OurHMDInfo, profile); + RenderState.OurProfileRenderInfo = GenerateProfileRenderInfoFromProfile(RenderState.OurHMDInfo, profile); + RenderState.RenderInfo = GenerateHmdRenderInfoFromHmdInfo(RenderState.OurHMDInfo, RenderState.OurProfileRenderInfo); + RenderState.Distortion[0] = CalculateDistortionRenderDesc(StereoEye_Left, RenderState.RenderInfo, 0); RenderState.Distortion[1] = CalculateDistortionRenderDesc(StereoEye_Right, RenderState.RenderInfo, 0); @@ -247,19 +254,25 @@ void HMDState::UpdateRenderProfile(Profile* profile) }; pClient->SetNumberValues(GetNetId(), "NeckModelVector3f", neckModelArray, 3); - double camerastate[7]; - if (profile->GetDoubleValues(OVR_KEY_CAMERA_POSITION, camerastate, 7) == 0) + // Camera position + + // OVR_KEY_CAMERA_POSITION is actually the *inverse* of a camera position. + Posed centeredFromWorld; + + double values[7]; + if (profile->GetDoubleValues(OVR_KEY_CAMERA_POSITION, values, 7) == 7) { - //there is no value, so we load the default - for (int i = 0; i < 7; i++) camerastate[i] = 0; - camerastate[3] = 1;//no offset. by default, give the quaternion w component value 1 + centeredFromWorld = Posed::FromArray(values); } else + { + centeredFromWorld = TheTrackingStateReader.GetDefaultCenteredFromWorld(); + } - TheSensorStateReader.setCenteredFromWorld(OVR::Posed::FromArray(camerastate)); + // ComputeCenteredFromWorld wants a worldFromCpf pose, so invert it. + // FIXME: The stored centeredFromWorld doesn't have a neck model offset applied, but probably should. + TheTrackingStateReader.ComputeCenteredFromWorld(centeredFromWorld.Inverted(), Vector3d(0, 0, 0)); } - - } HMDState* HMDState::CreateHMDState(NetClient* client, const HMDNetworkInfo& netInfo) @@ -269,12 +282,12 @@ HMDState* HMDState::CreateHMDState(NetClient* client, const HMDNetworkInfo& netI if (!client->Hmd_GetHmdInfo(netInfo.NetId, &hinfo)) { OVR_DEBUG_LOG(("[HMDState] Unable to get HMD info")); - return NULL; + return nullptr; } #if !defined(HEADLESS_APP) #ifdef OVR_OS_WIN32 - OVR_DEBUG_LOG(("Setting up display shim")); + OVR_DEBUG_LOG(("[HMDState] Setting up display shim")); // Initialize the display shim before reporting the display to the user code // so that this will happen before the D3D display object is created. @@ -283,23 +296,20 @@ HMDState* HMDState::CreateHMDState(NetClient* client, const HMDNetworkInfo& netI #endif /* !defined(HEADLESS_APP) */ Ptr<Profile> pDefaultProfile = *ProfileManager::GetInstance()->GetDefaultUserProfile(&hinfo); - OVR_DEBUG_LOG(("Using profile %s", pDefaultProfile->GetValue(OVR_KEY_USER))); + OVR_DEBUG_LOG(("[HMDState] Using profile %s", pDefaultProfile->GetValue(OVR_KEY_USER))); - HMDState* hmds = new HMDState(netInfo, hinfo, pDefaultProfile, client); + HMDState* hmds = new HMDState(hinfo, pDefaultProfile, &netInfo, client); - if (!hmds->SharedStateReader.Open(netInfo.SharedMemoryName.ToCStr())) + if (!hmds->InitializeSharedState()) { delete hmds; - return NULL; + return nullptr; } - hmds->TheSensorStateReader.SetUpdater(hmds->SharedStateReader.Get()); - hmds->TheLatencyTestStateReader.SetUpdater(hmds->SharedStateReader.Get()); - return hmds; } -HMDState* HMDState::CreateHMDState(ovrHmdType hmdType) +HMDState* HMDState::CreateDebugHMDState(ovrHmdType hmdType) { HmdTypeEnum t = HmdType_None; if (hmdType == ovrHmd_DK1) @@ -309,41 +319,51 @@ HMDState* HMDState::CreateHMDState(ovrHmdType hmdType) // FIXME: This does not actually grab the right user.. Ptr<Profile> pDefaultProfile = *ProfileManager::GetInstance()->GetDefaultProfile(t); - + return new HMDState(CreateDebugHMDInfo(t), pDefaultProfile); } - -const OVR::List<HMDState>& HMDState::GetHMDStateList() +// Enumerate each open HMD +unsigned HMDState::EnumerateHMDStateList(bool (*callback)(const HMDState *state)) { - return hmdStateList; + unsigned count = 0; + hmdStateListLock.DoLock(); + for (const HMDState *hmds = hmdStateList.GetFirst(); !hmdStateList.IsNull(hmds); hmds = hmdStateList.GetNext(hmds)) + { + if (callback && !callback(hmds)) + break; + ++count; + } + hmdStateListLock.Unlock(); + return count; } - //------------------------------------------------------------------------------------- // *** Sensor bool HMDState::ConfigureTracking(unsigned supportedCaps, unsigned requiredCaps) { - return pClient ? pClient->Hmd_ConfigureTracking(NetId, supportedCaps, requiredCaps) : true; + return pClient ? pClient->Hmd_ConfigureTracking(NetId, supportedCaps, requiredCaps) : true; } -void HMDState::ResetTracking() +void HMDState::ResetTracking(bool visionReset) { - if (pClient) pClient->Hmd_ResetTracking(NetId); + if (pClient) pClient->Hmd_ResetTracking(NetId, visionReset); } // Re-center the orientation. void HMDState::RecenterPose() { - TheSensorStateReader.RecenterPose(); + float hnm[3]; + getFloatArray("NeckModelVector3f", hnm, 3); + TheTrackingStateReader.RecenterPose(Vector3d(hnm[0], hnm[1], hnm[2])); } // Returns prediction for time. -ovrTrackingState HMDState::PredictedTrackingState(double absTime) -{ - Tracking::TrackingState ss; - TheSensorStateReader.GetSensorStateAtTime(absTime, ss); +ovrTrackingState HMDState::PredictedTrackingState(double absTime, void*) +{ + Vision::TrackingState ss; + TheTrackingStateReader.GetTrackingStateAtTime(absTime, ss); // Zero out the status flags if (!pClient || !pClient->IsConnected(false, false)) @@ -351,6 +371,12 @@ ovrTrackingState HMDState::PredictedTrackingState(double absTime) ss.StatusFlags = 0; } +#ifdef OVR_OS_WIN32 + // Set up display code for Windows + Win32::DisplayShim::GetInstance().Active = (ss.StatusFlags & ovrStatus_HmdConnected) != 0; +#endif + + return ss; } @@ -360,31 +386,11 @@ void HMDState::SetEnabledHmdCaps(unsigned hmdCaps) { // disable low persistence and pentile. hmdCaps &= ~ovrHmdCap_LowPersistence; - hmdCaps &= ~ovrHmdCap_DirectPentile; // disable dynamic prediction using the internal latency tester hmdCaps &= ~ovrHmdCap_DynamicPrediction; } - if (OurHMDInfo.HmdType >= HmdType_DK2) - { - if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_DynamicPrediction) - { - // DynamicPrediction change - TimeManager.ResetFrameTiming(TimeManager.GetFrameTiming().FrameIndex, - (hmdCaps & ovrHmdCap_DynamicPrediction) ? true : false, - RenderingConfigured); - } - } - - // Pentile unsupported on everything right now. - hmdCaps &= ~ovrHmdCap_DirectPentile; - - if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_NoVSync) - { - TimeManager.SetVsync((hmdCaps & ovrHmdCap_NoVSync) ? false : true); - } - if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_NoMirrorToWindow) { #if !defined(HEADLESS_APP) @@ -410,7 +416,7 @@ void HMDState::SetEnabledHmdCaps(unsigned hmdCaps) unsigned newServiceCaps = hmdCaps & ovrHmdCap_Writable_Mask & ovrHmdCap_Service_Mask; if (prevServiceCaps ^ newServiceCaps) - { + { EnabledServiceHmdCaps = pClient ? pClient->Hmd_SetEnabledCaps(NetId, newServiceCaps) : newServiceCaps; } @@ -419,7 +425,7 @@ void HMDState::SetEnabledHmdCaps(unsigned hmdCaps) unsigned HMDState::SetEnabledHmdCaps() { - unsigned serviceCaps = pClient ? pClient->Hmd_GetEnabledCaps(NetId) : + unsigned serviceCaps = pClient ? pClient->Hmd_GetEnabledCaps(NetId) : EnabledServiceHmdCaps; return serviceCaps & ((~ovrHmdCap_Service_Mask) | EnabledHmdCaps); @@ -448,12 +454,12 @@ bool HMDState::getBoolValue(const char* propertyName, bool defaultVal) bool HMDState::setBoolValue(const char* propertyName, bool value) { - if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetBoolValue, propertyName)) - { - return NetClient::GetInstance()->SetBoolValue(GetNetId(), propertyName, value); - } + if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetBoolValue, propertyName)) + { + return NetClient::GetInstance()->SetBoolValue(GetNetId(), propertyName, value); + } - return false; + return false; } int HMDState::getIntValue(const char* propertyName, int defaultVal) @@ -471,12 +477,12 @@ int HMDState::getIntValue(const char* propertyName, int defaultVal) bool HMDState::setIntValue(const char* propertyName, int value) { - if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetIntValue, propertyName)) - { - return NetClient::GetInstance()->SetIntValue(GetNetId(), propertyName, value); - } + if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetIntValue, propertyName)) + { + return NetClient::GetInstance()->SetIntValue(GetNetId(), propertyName, value); + } - return false; + return false; } float HMDState::getFloatValue(const char* propertyName, float defaultVal) @@ -507,12 +513,12 @@ float HMDState::getFloatValue(const char* propertyName, float defaultVal) bool HMDState::setFloatValue(const char* propertyName, float value) { - if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetNumberValue, propertyName)) - { - return NetClient::GetInstance()->SetNumberValue(GetNetId(), propertyName, value); - } + if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetNumberValue, propertyName)) + { + return NetClient::GetInstance()->SetNumberValue(GetNetId(), propertyName, value); + } - return false; + return false; } static unsigned CopyFloatArrayWithLimit(float dest[], unsigned destSize, @@ -526,37 +532,70 @@ static unsigned CopyFloatArrayWithLimit(float dest[], unsigned destSize, unsigned HMDState::getFloatArray(const char* propertyName, float values[], unsigned arraySize) { - if (arraySize) - { - if (OVR_strcmp(propertyName, "ScreenSize") == 0) - { - float data[2] = { OurHMDInfo.ScreenSizeInMeters.w, OurHMDInfo.ScreenSizeInMeters.h }; + if (arraySize) + { + if (OVR_strcmp(propertyName, "ScreenSize") == 0) + { + float data[2] = { OurHMDInfo.ScreenSizeInMeters.w, OurHMDInfo.ScreenSizeInMeters.h }; return CopyFloatArrayWithLimit(values, arraySize, data, 2); - } + } else if (OVR_strcmp(propertyName, "DistortionClearColor") == 0) { return CopyFloatArrayWithLimit(values, arraySize, RenderState.ClearColor, 4); } else if (OVR_strcmp(propertyName, "DK2Latency") == 0) { - if (OurHMDInfo.HmdType != HmdType_DK2) + if (OurHMDInfo.HmdType < HmdType_DK2) { return 0; } - union { - struct X { - float latencyRender, latencyTimewarp, latencyPostPresent; - } x; - float data[3]; - } m; + OutputLatencyTimings timings; + ScreenLatencyTracker.GetLatencyTimings(timings); + + if (arraySize > 0) + { + switch (arraySize) + { + default: values[4] = (float)timings.ErrorTimewarp; // Fall-thru + case 4: values[3] = (float)timings.ErrorRender; // Fall-thru + case 3: values[2] = (float)timings.LatencyPostPresent; // Fall-thru + case 2: values[1] = (float)timings.LatencyTimewarp; // Fall-thru + case 1: values[0] = (float)timings.LatencyRender; + } + } + + return arraySize > 5 ? 5 : arraySize; + } + else if (OVR_strcmp(propertyName, "NeckModelVector3f") == 0) + { + // Query the service to grab the HNM. + double hnm[3] = {}; + int count = NetClient::GetInstance()->GetNumberValues(GetNetId(), propertyName, hnm, (int)arraySize); - static_assert(sizeof(m.x)==sizeof(m.data), "sizeof(struct X) failure"); + // If the service is unavailable or returns zero data, + if (count < 3 || + (hnm[0] == 0.0 && hnm[1] == 0.0 && hnm[2] == 0.0)) + { + // These are the default values used if the server does not return any data, due to not + // being reachable or other errors. + OVR_ASSERT(pProfile.GetPtr()); + if (pProfile.GetPtr()) + { + Vector3f neckModel = GetNeckModelFromProfile(pProfile); + hnm[0] = neckModel.x; + hnm[1] = neckModel.y; + hnm[2] = neckModel.z; + } + } - TimeManager.GetLatencyTimings(m.x.latencyRender, m.x.latencyTimewarp, m.x.latencyPostPresent); + for (unsigned i = 0; i < 3 && i < arraySize; ++i) + { + values[i] = (float)hnm[i]; + } - return CopyFloatArrayWithLimit(values, arraySize, m.data, 3); + return arraySize > 3 ? 3 : arraySize; } else if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetNumberValues, propertyName)) { @@ -578,16 +617,16 @@ unsigned HMDState::getFloatArray(const char* propertyName, float values[], unsig return count; } - else if (pProfile) - { - // TBD: Not quite right. Should update profile interface, so that - // we can return 0 in all conditions if property doesn't exist. - + else if (pProfile) + { + // TBD: Not quite right. Should update profile interface, so that + // we can return 0 in all conditions if property doesn't exist. + return pProfile->GetFloatValues(propertyName, values, arraySize); - } - } + } + } - return 0; + return 0; } bool HMDState::setFloatArray(const char* propertyName, float values[], unsigned arraySize) @@ -603,22 +642,22 @@ bool HMDState::setFloatArray(const char* propertyName, float values[], unsigned return true; } - if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetNumberValues, propertyName)) - { - double* da = new double[arraySize]; - for (int i = 0; i < (int)arraySize; ++i) - { - da[i] = values[i]; - } + if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetNumberValues, propertyName)) + { + double* da = new double[arraySize]; + for (int i = 0; i < (int)arraySize; ++i) + { + da[i] = values[i]; + } - bool result = NetClient::GetInstance()->SetNumberValues(GetNetId(), propertyName, da, arraySize); + bool result = NetClient::GetInstance()->SetNumberValues(GetNetId(), propertyName, da, arraySize); - delete[] da; + delete[] da; - return result; - } + return result; + } - return false; + return false; } const char* HMDState::getString(const char* propertyName, const char* defaultVal) @@ -628,26 +667,26 @@ const char* HMDState::getString(const char* propertyName, const char* defaultVal return NetClient::GetInstance()->GetStringValue(GetNetId(), propertyName, defaultVal); } - if (pProfile) - { - LastGetStringValue[0] = 0; - if (pProfile->GetValue(propertyName, LastGetStringValue, sizeof(LastGetStringValue))) - { - return LastGetStringValue; - } - } + if (pProfile) + { + LastGetStringValue[0] = 0; + if (pProfile->GetValue(propertyName, LastGetStringValue, sizeof(LastGetStringValue))) + { + return LastGetStringValue; + } + } - return defaultVal; + return defaultVal; } bool HMDState::setString(const char* propertyName, const char* value) { - if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetStringValue, propertyName)) - { - return NetClient::GetInstance()->SetStringValue(GetNetId(), propertyName, value); - } + if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetStringValue, propertyName)) + { + return NetClient::GetInstance()->SetStringValue(GetNetId(), propertyName, value); + } - return false; + return false; } @@ -659,6 +698,200 @@ bool HMDState::ProcessLatencyTest(unsigned char rgbColorOut[3]) return NetClient::GetInstance()->LatencyUtil_ProcessInputs(Timer::GetSeconds(), rgbColorOut); } + +//------------------------------------------------------------------------------------- +// *** Timewarp + +AppTiming HMDState::GetAppTiming(uint32_t frameIndex) +{ + // Get prediction time for the requested frame index + AppTiming timing; + const bool VsyncOn = ((RenderState.EnabledHmdCaps & ovrHmdCap_NoVSync) == 0); + RenderTimer.GetAppTimingForIndex(timing, VsyncOn, frameIndex); + + // Update the predicted scanout time for this frame index + TimingHistory.SetScanoutTimeForFrame(frameIndex, timing.ScanoutStartTime); + + return timing; +} + +ovrFrameTiming HMDState::GetFrameTiming(uint32_t frameIndex) +{ + AppTiming timing = GetAppTiming(frameIndex); + + // Calculate eye render times based on shutter type + double eyePhotonsTimes[2]; + CalculateEyeRenderTimes(timing.VisibleMidpointTime, timing.FrameInterval, + RenderState.RenderInfo.Shutter.Type, + eyePhotonsTimes[0], eyePhotonsTimes[1]); + + RenderIMUTimeSeconds = Timer::GetSeconds(); // RenderPrediction.RawSensorData.TimeInSeconds; + + // Construct a ovrFrameTiming object from the base app timing information + ovrFrameTiming result; + result.DeltaSeconds = (float)timing.FrameInterval; + result.EyeScanoutSeconds[0] = eyePhotonsTimes[0]; + result.EyeScanoutSeconds[1] = eyePhotonsTimes[1]; + result.ScanoutMidpointSeconds = timing.VisibleMidpointTime; + result.ThisFrameSeconds = timing.ScanoutStartTime - timing.FrameInterval; + result.NextFrameSeconds = timing.ScanoutStartTime; + // Deprecated: This should be queried after render work completes. Please delete me from CAPI. + result.TimewarpPointSeconds = 0.; + return result; +} + +ovrTrackingState HMDState::GetMidpointPredictionTracking(uint32_t frameIndex) +{ + AppTiming timing = GetAppTiming(frameIndex); + RenderIMUTimeSeconds = Timer::GetSeconds(); // RenderPrediction.RawSensorData.TimeInSeconds; + return PredictedTrackingState(timing.VisibleMidpointTime); +} + +Posef HMDState::GetEyePredictionPose(ovrEyeType eye) +{ + // Note that this function does not get the frame index parameter and depends + // on whichever value is passed into the BeginFrame() function. + ovrTrackingState ts = GetMidpointPredictionTracking(BeginFrameIndex); + TraceTrackingState(ts); + Posef const & hmdPose = ts.HeadPose.ThePose; + + // Currently HmdToEyeViewOffset is only a 3D vector + // (Negate HmdToEyeViewOffset because offset is a view matrix offset and not a camera offset) + if (eye == ovrEye_Left) + { + return Posef(hmdPose.Rotation, ((Posef)hmdPose).Apply(-((Vector3f)RenderState.EyeRenderDesc[0].HmdToEyeViewOffset))); + } + else + { + return Posef(hmdPose.Rotation, ((Posef)hmdPose).Apply(-((Vector3f)RenderState.EyeRenderDesc[1].HmdToEyeViewOffset))); + } +} + +void HMDState::endFrameRenderTiming() +{ + TimewarpTimer.SetLastPresentTime(); // Record approximate vsync time + + bool dk2LatencyTest = (EnabledHmdCaps & ovrHmdCap_DynamicPrediction) != 0; + if (dk2LatencyTest) + { + Util::FrameTimeRecordSet recordSet; + TheLatencyTestStateReader.GetRecordSet(recordSet); + + FrameLatencyData data; + data.DrawColor = LatencyTest2DrawColor[0]; + data.RenderIMUTime = RenderIMUTimeSeconds; + data.RenderPredictedScanoutTime = TimingHistory.LookupScanoutTime(BeginFrameIndex); + data.PresentTime = TimewarpTimer.GetLatencyTesterPresentTime(); + data.TimewarpPredictedScanoutTime = TimewarpTimer.GetTimewarpTiming()->ScanoutTime; + data.TimewarpIMUTime = TimewarpTimer.GetTimewarpIMUTime(); + + //OVR_ASSERT(data.TimewarpIMUTime == 0. || data.TimewarpIMUTime >= data.RenderIMUTime); + + ScreenLatencyTracker.SaveDrawColor(data); + ScreenLatencyTracker.MatchRecord(recordSet); + } +} + +void HMDState::getTimewarpStartEnd(ovrEyeType eyeId, double timewarpStartEnd[2]) +{ + // Get eye start/end scanout times + TimewarpTiming const* timewarpTiming = TimewarpTimer.GetTimewarpTiming(); + + for (int i = 0; i < 2; ++i) + { + timewarpStartEnd[i] = timewarpTiming->EyeStartEndTimes[eyeId][i]; + } +} + +void HMDState::GetTimewarpMatricesEx(ovrEyeType eyeId, + ovrPosef renderPose, + bool calcPosition, const ovrVector3f hmdToEyeViewOffset[2], + ovrMatrix4f twmOut[2], double debugTimingOffsetInSeconds) +{ + // Get timewarp start/end timing + double timewarpStartEnd[2]; + getTimewarpStartEnd(eyeId, timewarpStartEnd); + + //TPH, to vary timing, to allow developers to debug, to shunt the predicted time forward + //and back, and see if the SDK is truly delivering the correct time. Also to allow + //illustration of the detrimental effects when this is not done right. + timewarpStartEnd[0] += debugTimingOffsetInSeconds; + timewarpStartEnd[1] += debugTimingOffsetInSeconds; + + ovrTrackingState startState = PredictedTrackingState(timewarpStartEnd[0]); + ovrTrackingState endState = PredictedTrackingState(timewarpStartEnd[1]); + + ovrPosef startHmdPose = startState.HeadPose.ThePose; + ovrPosef endHmdPose = endState.HeadPose.ThePose; + Vector3f eyeOffset = Vector3f(0.0f, 0.0f, 0.0f); + Matrix4f timewarpStart, timewarpEnd; + if (calcPosition) + { + if (!hmdToEyeViewOffset) + { + OVR_ASSERT(false); + LogError("{ERR-102} [FrameTime] No hmdToEyeViewOffset provided even though calcPosition is true."); + + // disable position to avoid positional issues + renderPose.Position = Vector3f::Zero(); + startHmdPose.Position = Vector3f::Zero(); + endHmdPose.Position = Vector3f::Zero(); + } + else if (hmdToEyeViewOffset[eyeId].x >= MATH_FLOAT_MAXVALUE) + { + OVR_ASSERT(false); + LogError("{ERR-103} [FrameTime] Invalid hmdToEyeViewOffset provided by client."); + + // disable position to avoid positional issues + renderPose.Position = Vector3f::Zero(); + startHmdPose.Position = Vector3f::Zero(); + endHmdPose.Position = Vector3f::Zero(); + } + else + { + // Currently HmdToEyeViewOffset is only a 3D vector + // (Negate HmdToEyeViewOffset because offset is a view matrix offset and not a camera offset) + eyeOffset = ((Posef)startHmdPose).Apply(-((Vector3f)hmdToEyeViewOffset[eyeId])); + } + + Posef fromEye = Posef(renderPose).Inverted(); // because we need the view matrix, not the camera matrix + CalculatePositionalTimewarpMatrix(fromEye, startHmdPose, eyeOffset, timewarpStart); + CalculatePositionalTimewarpMatrix(fromEye, endHmdPose, eyeOffset, timewarpEnd); + } + else + { + Quatf fromEye = Quatf(renderPose.Orientation).Inverted(); // because we need the view matrix, not the camera matrix + CalculateOrientationTimewarpMatrix(fromEye, startHmdPose.Orientation, timewarpStart); + CalculateOrientationTimewarpMatrix(fromEye, endHmdPose.Orientation, timewarpEnd); + } + twmOut[0] = timewarpStart; + twmOut[1] = timewarpEnd; +} + +void HMDState::GetTimewarpMatrices(ovrEyeType eyeId, ovrPosef renderPose, + ovrMatrix4f twmOut[2]) +{ + // Get timewarp start/end timing + double timewarpStartEnd[2]; + getTimewarpStartEnd(eyeId, timewarpStartEnd); + + ovrTrackingState startState = PredictedTrackingState(timewarpStartEnd[0]); + ovrTrackingState endState = PredictedTrackingState(timewarpStartEnd[1]); + + Quatf quatFromEye = Quatf(renderPose.Orientation); + quatFromEye.Invert(); // because we need the view matrix, not the camera matrix + + Matrix4f timewarpStart, timewarpEnd; + CalculateOrientationTimewarpMatrix( + quatFromEye, startState.HeadPose.ThePose.Orientation, timewarpStart); + CalculateOrientationTimewarpMatrix( + quatFromEye, endState.HeadPose.ThePose.Orientation, timewarpEnd); + + twmOut[0] = timewarpStart; + twmOut[1] = timewarpEnd; +} + + //------------------------------------------------------------------------------------- // *** Rendering @@ -702,7 +935,7 @@ bool HMDState::ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2], pRenderer.Clear(); } - distortionCaps = distortionCaps & pHmdDesc->DistortionCaps; + distortionCaps = distortionCaps & pHmdDesc->DistortionCaps; // Step 1: do basic setup configuration RenderState.EnabledHmdCaps = EnabledHmdCaps; // This is a copy... Any cleaner way? @@ -712,27 +945,22 @@ bool HMDState::ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2], eyeRenderDescOut[0] = RenderState.EyeRenderDesc[0]; eyeRenderDescOut[1] = RenderState.EyeRenderDesc[1]; - TimeManager.ResetFrameTiming(0, - (EnabledHmdCaps & ovrHmdCap_DynamicPrediction) ? true : false, - true); - - LastFrameTimeSeconds = 0.0f; - // Set RenderingConfigured early to avoid ASSERTs in renderer initialization. RenderingConfigured = true; if (!pRenderer) { pRenderer = *DistortionRenderer::APICreateRegistry - [apiConfig->Header.API](pHmdDesc, TimeManager, RenderState); + [apiConfig->Header.API](); } if (!pRenderer || - !pRenderer->Initialize(apiConfig)) + !pRenderer->Initialize(apiConfig, &TheTrackingStateReader, + &TimewarpTimer, &RenderState)) { RenderingConfigured = false; return false; - } + } #if !defined(HEADLESS_APP) // Setup the Health and Safety Warning display system. @@ -745,11 +973,72 @@ bool HMDState::ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2], if(!pHSWDisplay) // Use * below because that for of operator= causes it to inherit the refcount the factory gave the object. { pHSWDisplay = *OVR::CAPI::HSWDisplay::Factory(apiConfig->Header.API, pHmdDesc, RenderState); - pHSWDisplay->Enable(pProfile->GetBoolValue("HSW", true)); } if (pHSWDisplay) + { pHSWDisplay->Initialize(apiConfig); // This is potentially re-initializing it with a new config. + } + +#ifdef OVR_OS_WIN32 + if (!pWindow) + { + // We can automatically populate the window to attach to by + // pulling that information off the swap chain that the + // application provides. If the application later calls the + // ovrHmd_AttachToWindow() function these will get harmlessly + // overwritten. The check above verifies that the window is + // not set yet, and it insures that this default doesn't + // overwrite the application setting. + + if (apiConfig->Header.API == ovrRenderAPI_D3D11) + { + ovrD3D11Config* d3d11Config = (ovrD3D11Config*)apiConfig; + if (d3d11Config->D3D11.pSwapChain) + { + DXGI_SWAP_CHAIN_DESC desc = {}; + HRESULT hr = d3d11Config->D3D11.pSwapChain->GetDesc(&desc); + if (SUCCEEDED(hr)) + { + pWindow = (void*)desc.OutputWindow; + } + } + } + else if (apiConfig->Header.API == ovrRenderAPI_OpenGL) + { + ovrGLConfig* glConfig = (ovrGLConfig*)apiConfig; + pWindow = (void*)glConfig->OGL.Window; + } +OVR_DISABLE_MSVC_WARNING(4996) // Disable deprecation warning + else if (apiConfig->Header.API == ovrRenderAPI_D3D9) + { + ovrD3D9Config* dx9Config = (ovrD3D9Config*)apiConfig; + if (dx9Config->D3D9.pDevice) + { + D3DDEVICE_CREATION_PARAMETERS params = {}; + HRESULT hr = dx9Config->D3D9.pDevice->GetCreationParameters(¶ms); + if (SUCCEEDED(hr)) + { + pWindow = (void*)params.hFocusWindow; + } + } + } +OVR_RESTORE_MSVC_WARNING() + + // If a window handle was implied by render configuration, + if (pWindow) + { + // This is the same logic as ovrHmd_AttachToWindow() on Windows: + if (pClient) + pClient->Hmd_AttachToWindow(GetNetId(), pWindow); + Win32::DisplayShim::GetInstance().hWindow = (HWND)pWindow; + // On the server side it is updating the association of connection + // to window handle. This is perfectly safe to update later to + // a new window handle (verified). Also verified that if this + // handle is garbage that it doesn't crash anything. + } + } +#endif #endif /* !defined(HEADLESS_APP) */ return true; @@ -757,72 +1046,48 @@ bool HMDState::ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2], void HMDState::SubmitEyeTextures(const ovrPosef renderPose[2], - const ovrTexture eyeTexture[2]) + const ovrTexture eyeTexture[2], + const ovrTexture eyeDepthTexture[2]) { RenderState.EyeRenderPoses[0] = renderPose[0]; RenderState.EyeRenderPoses[1] = renderPose[1]; if (pRenderer) { + if(eyeDepthTexture) + { + pRenderer->SubmitEyeWithDepth(0, &eyeTexture[0], &eyeDepthTexture[0]); + pRenderer->SubmitEyeWithDepth(1, &eyeTexture[1], &eyeDepthTexture[1]); + } + else + { + //OVR_ASSERT(!(RenderState.DistortionCaps & ovrDistortionCap_DepthProjectedTimeWarp)); + //LogError("{ERR-104} [HMDState] Even though ovrDistortionCap_DepthProjectedTimeWarp is enabled, no depth buffer was provided."); + pRenderer->SubmitEye(0, &eyeTexture[0]); pRenderer->SubmitEye(1, &eyeTexture[1]); } } +} - -// I appreciate this is not an idea place for this function, but it didn't seem to be -// being linked properly when in OVR_CAPI.cpp. -// Please relocate if you know of a better place -ovrBool ovrHmd_CreateDistortionMeshInternal( ovrHmdStruct * hmd, - ovrEyeType eyeType, ovrFovPort fov, - unsigned int distortionCaps, - ovrDistortionMesh *meshData, - float overrideEyeReliefIfNonZero ) +bool HMDState::CreateDistortionMesh(ovrEyeType eyeType, ovrFovPort fov, + unsigned int distortionCaps, + ovrDistortionMesh *meshData, + float overrideEyeReliefIfNonZero) { - 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_CC_MSVC) - static_assert(sizeof(DistortionMeshVertexData) == sizeof(ovrDistortionVertex), "DistortionMeshVertexData size mismatch"); -#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 = RenderState.RenderInfo; - const HmdRenderInfo& hmdri = hmds->RenderState.RenderInfo; - StereoEye stereoEye = (eyeType == ovrEye_Left) ? StereoEye_Left : StereoEye_Right; - - DistortionRenderDesc& distortion = hmds->RenderState.Distortion[eyeType]; - if (overrideEyeReliefIfNonZero) - { - distortion.Lens = GenerateLensConfigFromEyeRelief(overrideEyeReliefIfNonZero,hmdri); - } - - // 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_t**)&meshData->pIndexData, - &vertexCount, &triangleCount, - (stereoEye == StereoEye_Right), - hmdri, distortion, eyeToSourceNDC); + DistortionRenderDesc& distortion = RenderState.Distortion[eyeType]; + if (overrideEyeReliefIfNonZero) + { + distortion.Lens = GenerateLensConfigFromEyeRelief(overrideEyeReliefIfNonZero, hmdri); + } - if (meshData->pVertexData) + if (CalculateDistortionMeshFromFOV( + hmdri, distortion, + (eyeType == ovrEye_Left ? StereoEye_Left : StereoEye_Right), + fov, distortionCaps, meshData)) { - // Convert to index - meshData->IndexCount = triangleCount * 3; - meshData->VertexCount = vertexCount; return 1; } @@ -830,5 +1095,4 @@ ovrBool ovrHmd_CreateDistortionMeshInternal( ovrHmdStruct * hmd, } - }} // namespace OVR::CAPI |