From 4207f9c279e832e3afcb3f5fc6cd8d84cb4cfe4c Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sat, 28 Mar 2015 01:43:35 +0100
Subject: Bump OculusVR RIFT SDK to 0.5.0.1

---
 LibOVR/Src/OVR_CAPI.cpp | 1452 ++++++++++++++++++++++++++---------------------
 1 file changed, 790 insertions(+), 662 deletions(-)
 mode change 100644 => 100755 LibOVR/Src/OVR_CAPI.cpp

(limited to 'LibOVR/Src/OVR_CAPI.cpp')

diff --git a/LibOVR/Src/OVR_CAPI.cpp b/LibOVR/Src/OVR_CAPI.cpp
old mode 100644
new mode 100755
index bfc0527..d451ad8
--- a/LibOVR/Src/OVR_CAPI.cpp
+++ b/LibOVR/Src/OVR_CAPI.cpp
@@ -25,67 +25,84 @@ limitations under the License.
 ************************************************************************************/
 
 #include "OVR_CAPI.h"
+#include "OVR_Version.h"
+
+
 #include "Kernel/OVR_Timer.h"
-#include "Kernel/OVR_Math.h"
 #include "Kernel/OVR_System.h"
+#include "Kernel/OVR_DebugHelp.h"
+#include "Extras/OVR_Math.h"
 #include "OVR_Stereo.h"
 #include "OVR_Profile.h"
-#include "../Include/OVR_Version.h"
 
 #include "CAPI/CAPI_HMDState.h"
-#include "CAPI/CAPI_FrameTimeManager.h"
 
+#include "Net/OVR_Session.h"
 #include "Service/Service_NetClient.h"
-#ifdef OVR_SINGLE_PROCESS
+#ifdef OVR_PRIVATE_FILE
 #include "Service/Service_NetServer.h"
 #endif
 
-#ifdef OVR_OS_WIN32
+#include "Displays/OVR_Display.h"
+
+#if defined(OVR_OS_WIN32)
 #include "Displays/OVR_Win32_ShimFunctions.h"
+#include <VersionHelpers.h>
 #endif
 
+// Forward decl to keep the callback static
+
+// Note: Removed CaptureHmdDescTrace from non-Win32 due to build warning.
+#if !defined(OVR_OS_MAC) && !defined(OVR_OS_LINUX)
+static bool CaptureHmdDescTrace(const OVR::CAPI::HMDState *state);
+#endif
+#define TRACE_STATE_CAPTURE_FUNC OVR::CAPI::HMDState::EnumerateHMDStateList(CaptureHmdDescTrace)
+#include "Tracing/Tracing.h"
+
+#if !defined(OVR_OS_MAC) && !defined(OVR_OS_LINUX)
+// EnumerateHMDStateList callback for tracing state capture
+static bool CaptureHmdDescTrace(const OVR::CAPI::HMDState* state)
+{
+    TraceHmdDesc(*state->pHmdDesc);
+    OVR_UNUSED(state); // Avoid potential compiler warnings.
+    return true;
+}
+#endif
+
+// Produce an invalid tracking state that will not mess up the application too badly.
+static ovrTrackingState GetNullTrackingState()
+{
+    ovrTrackingState nullState = ovrTrackingState();
+    nullState.HeadPose.ThePose.Orientation.w = 1.f; // Provide valid quaternions for head pose.
+    return nullState;
+}
+
+// Produce a null frame timing structure that will not break the calling application.
+static ovrFrameTiming GetNullFrameTiming()
+{
+    ovrFrameTiming nullTiming = ovrFrameTiming();
+    nullTiming.DeltaSeconds = 0.013f; // Provide nominal value
+    return nullTiming;
+}
+
 
 using namespace OVR;
 using namespace OVR::Util::Render;
-using namespace OVR::Tracking;
+using namespace OVR::Vision;
 
 //-------------------------------------------------------------------------------------
 // Math
 namespace OVR {
 
 
-// ***** FovPort
-
-// C-interop support: FovPort <-> ovrFovPort
-FovPort::FovPort(const ovrFovPort &src)
-    : UpTan(src.UpTan), DownTan(src.DownTan), LeftTan(src.LeftTan), RightTan(src.RightTan)
-{ }    
-
-FovPort::operator ovrFovPort () const
-{
-    ovrFovPort result;
-    result.LeftTan  = LeftTan;
-    result.RightTan = RightTan;
-    result.UpTan    = UpTan;
-    result.DownTan  = DownTan;
-    return result;
-}
-
-// Converts Fov Tan angle units to [-1,1] render target NDC space
-Vector2f FovPort::TanAngleToRendertargetNDC(Vector2f const &tanEyeAngle)
-{  
-    ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(*this);
-    return tanEyeAngle * eyeToSourceNDC.Scale + eyeToSourceNDC.Offset;
-}
-
 // ***** SensorDataType
 
 SensorDataType::SensorDataType(const ovrSensorData& s)
 {
-    Acceleration = s.Accelerometer;
-    RotationRate = s.Gyro;
-    MagneticField = s.Magnetometer;
-    Temperature = s.Temperature;
+    Acceleration        = s.Accelerometer;
+    RotationRate        = s.Gyro;
+    MagneticField       = s.Magnetometer;
+    Temperature         = s.Temperature;
     AbsoluteTimeSeconds = s.TimeInSeconds;
 }
 
@@ -93,39 +110,35 @@ SensorDataType::operator ovrSensorData () const
 {
     ovrSensorData result;
     result.Accelerometer = Acceleration;
-    result.Gyro = RotationRate;
-    result.Magnetometer = MagneticField;
-    result.Temperature = Temperature;
-    result.TimeInSeconds = (float) AbsoluteTimeSeconds;
+    result.Gyro          = RotationRate;
+    result.Magnetometer  = MagneticField;
+    result.Temperature   = Temperature;
+    result.TimeInSeconds = (float)AbsoluteTimeSeconds;
     return result;
 }
 
 
+
+
 // ***** SensorState
 
 TrackingState::TrackingState(const ovrTrackingState& s)
 {
-    HeadPose    = s.HeadPose;
-    CameraPose  = s.CameraPose;
+    HeadPose          = s.HeadPose;
+    CameraPose        = s.CameraPose;
     LeveledCameraPose = s.LeveledCameraPose;
-    RawSensorData = s.RawSensorData;
-    StatusFlags = s.StatusFlags;
-    LastVisionProcessingTime = s.LastVisionProcessingTime;
-    LastVisionFrameLatency = s.LastVisionFrameLatency;
-    LastCameraFrameCounter = s.LastCameraFrameCounter;
+    RawSensorData     = s.RawSensorData;
+    StatusFlags       = s.StatusFlags;
 }
 
 TrackingState::operator ovrTrackingState() const
 {
     ovrTrackingState result;
-    result.HeadPose     = HeadPose;
-    result.CameraPose   = CameraPose;
+    result.HeadPose          = HeadPose;
+    result.CameraPose        = CameraPose;
     result.LeveledCameraPose = LeveledCameraPose;
-    result.RawSensorData  = RawSensorData;
-    result.StatusFlags  = StatusFlags;
-    result.LastVisionProcessingTime = LastVisionProcessingTime;
-    result.LastVisionFrameLatency = LastVisionFrameLatency;
-    result.LastCameraFrameCounter = LastCameraFrameCounter;
+    result.RawSensorData     = RawSensorData;
+    result.StatusFlags       = StatusFlags;
     return result;
 }
 
@@ -136,182 +149,210 @@ TrackingState::operator ovrTrackingState() const
 
 using namespace OVR::CAPI;
 
-#ifdef __cplusplus 
-extern "C" {
-#endif
-
 
-// Used to generate projection from ovrEyeDesc::Fov
-OVR_EXPORT ovrMatrix4f ovrMatrix4f_Projection(ovrFovPort fov, float znear, float zfar, ovrBool rightHanded)
+// Helper function to validate the HMD object provided by the API user.
+static HMDState* GetHMDStateFromOvrHmd(ovrHmd hmddesc)
 {
-    return CreateProjection(rightHanded ? true : false, fov, znear, zfar);
+    if (!hmddesc || !hmddesc->Handle)
+        return nullptr;
+
+    return (HMDState*)hmddesc->Handle;
 }
 
 
-OVR_EXPORT ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale,
-                                                      float orthoDistance, float hmdToEyeViewOffsetX)
+OVR_PUBLIC_FUNCTION(double) ovr_GetTimeInSeconds()
 {
+    return Timer::GetSeconds();
+}
 
-    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
-    // the physical [-orthoHalfFov,orthoHalfFov]
-    // Note moving the offset from M[0][2]+M[1][2] to M[0][3]+M[1][3] - this means
-    // we don't have to feed in Z=1 all the time.
-    // The horizontal offset math is a little hinky because the destination is
-    // actually [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]
-    // So we need to first map [-FovPixels/2,FovPixels/2] to
-    //                         [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]:
-    // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset;
-    //    = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset;
-    // 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 +
-    //      orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2];
-    // So in the new projection matrix we need to scale by Projection.M[0][0]*2*orthoHalfFov/FovPixels and
-    // offset by orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2].
-
-    Matrix4f ortho;
-    ortho.M[0][0] = projection.M[0][0] * orthoScale.x;
-    ortho.M[0][1] = 0.0f;
-    ortho.M[0][2] = 0.0f;
-    ortho.M[0][3] = -projection.M[0][2] + ( orthoHorizontalOffset * projection.M[0][0] );
-
-    ortho.M[1][0] = 0.0f;
-    ortho.M[1][1] = -projection.M[1][1] * orthoScale.y;       // Note sign flip (text rendering uses Y=down).
-    ortho.M[1][2] = 0.0f;
-    ortho.M[1][3] = -projection.M[1][2];
-
-    /*
-    if ( fabsf ( zNear - zFar ) < 0.001f )
-    {
-        ortho.M[2][0] = 0.0f;
-        ortho.M[2][1] = 0.0f;
-        ortho.M[2][2] = 0.0f;
-        ortho.M[2][3] = zFar;
-    }
-    else
-    {
-        ortho.M[2][0] = 0.0f;
-        ortho.M[2][1] = 0.0f;
-        ortho.M[2][2] = zFar / (zNear - zFar);
-        ortho.M[2][3] = (zFar * zNear) / (zNear - zFar);
-    }
-    */
 
-    // MA: Undo effect of sign
-    ortho.M[2][0] = 0.0f;
-    ortho.M[2][1] = 0.0f;
-    //ortho.M[2][2] = projection.M[2][2] * projection.M[3][2] * -1.0f; // reverse right-handedness
-    ortho.M[2][2] = 0.0f;
-    ortho.M[2][3] = 0.0f;
-        //projection.M[2][3];
+//-------------------------------------------------------------------------------------
 
-    // No perspective correction for ortho.
-    ortho.M[3][0] = 0.0f;
-    ortho.M[3][1] = 0.0f;
-    ortho.M[3][2] = 0.0f;
-    ortho.M[3][3] = 1.0f;
+// 1. Init/shutdown.
 
-    return ortho;
-}
+static ovrBool CAPI_ovrInitializeCalled = ovrFalse;
+static OVR::Service::NetClient* CAPI_pNetClient = nullptr;
 
 
-OVR_EXPORT double ovr_GetTimeInSeconds()
+ovrBool ovr_InitializeRenderingShim()
 {
-    return Timer::GetSeconds();
+    OVR::Display::Initialize();
+
+    return OVR::Display::GetDirectDisplayInitialized();
 }
 
-// Waits until the specified absolute time.
-OVR_EXPORT double ovr_WaitTillTime(double absTime)
+OVR_PUBLIC_FUNCTION(ovrBool) ovr_InitializeRenderingShimVersion(int requestedMinorVersion)
 {
-    double       initialTime = ovr_GetTimeInSeconds();
-    double       newTime     = initialTime;
+    // We ignore the patch and build versions here, as they aren't relevant to compatibility.
+    // And we don't store them away here, as we do that in ovr_Initialize() instead.
     
-    while(newTime < absTime)
+    if (requestedMinorVersion > OVR_MINOR_VERSION)
+        return ovrFalse;
+
+    return ovr_InitializeRenderingShim();
+}
+
+// Write out to the log where the current running module is located on disk.
+static void LogLocationOfThisModule()
+{
+#if defined (OVR_OS_WIN32)
+    // Log out the DLL file path on startup.
     {
-        for (int j = 0; j < 5; j++)
-            OVR_PROCESSOR_PAUSE();
+        bool success = false;
+
+        HMODULE hModule = nullptr;
+        GetModuleHandleEx(
+            GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+            (LPCTSTR)&ovr_Initialize,
+            &hModule);
+        if (hModule)
+        {
+            wchar_t filename[_MAX_PATH];
+            DWORD len = GetModuleFileNameW(hModule, filename, OVR_ARRAY_COUNT(filename));
+            if (len > 0 && filename[0])
+            {
+                success = true;
+                LogText("[CAPI] LibOVR module is located at %ws\n", filename);
+            }
+        }
 
-        newTime = ovr_GetTimeInSeconds();
+        if (!success)
+        {
+            LogError("[CAPI] WARNING: Unable to find LibOVR module.");
+        }
     }
-
-    // How long we waited
-    return newTime - initialTime;
+#endif // OVR_OS_WIN32
 }
 
+// These defaults are also in OVR_CAPIShim.c
+static const ovrInitParams DefaultParams = {
+    ovrInit_RequestVersion, // Flags
+    OVR_MINOR_VERSION,      // RequestedMinorVersion
+    0,                      // LogCallback
+    0                       // ConnectionTimeoutSeconds
+};
 
-//-------------------------------------------------------------------------------------
+// Precondition: params is not null
+OVR_PUBLIC_FUNCTION(ovrBool) ovr_Initialize(ovrInitParams const* params)
+{
+    // TBD: Should we check if the version requested changed and fail here?
+    if (CAPI_ovrInitializeCalled) // If already initialized...
+        return ovrTrue;
 
-// 1. Init/shutdown.
+    TraceInit();
+    TraceCall(0);
 
-static ovrBool CAPI_SystemInitCalled = 0;
-static ovrBool CAPI_ovrInitializeCalled = 0;
+    if (!params)
+    {
+        params = &DefaultParams;
+    }
 
-static OVR::Service::NetClient* CAPI_pNetClient = 0;
+    bool DebugMode = (params->Flags & ovrInit_Debug) != 0;
 
-OVR_EXPORT ovrBool ovr_InitializeRenderingShim()
-{
-    OVR::System::DirectDisplayInitialize();
-    return OVR::System::DirectDisplayEnabled();
-}
+#if defined(OVR_BUILD_DEBUG)
+    // If no debug setting is provided,
+    if (!(params->Flags & (ovrInit_Debug | ovrInit_ForceNoDebug)))
+    {
+        DebugMode = true;
+    }
+#endif
 
-OVR_EXPORT ovrBool ovr_Initialize()
-{
-    if (CAPI_ovrInitializeCalled)
-        return 1;
+    // We ignore the requested patch version and build version, as they are not currently relevant to
+    // the library compatibility. Our test for minor version compatibility is currently simple: we support
+    // only older or equal minor versions, and don't change our behavior if the requested minor version
+    // is older than than OVR_MINOR_VERSION.
 
-    // We must set up the system for the plugin to work
-    if (!OVR::System::IsInitialized())
+    if ((params->Flags & ovrInit_RequestVersion) != 0 &&
+        (params->RequestedMinorVersion > OVR_MINOR_VERSION))
     {
-        OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All));
-        CAPI_SystemInitCalled = 1;
+        goto Abort;
     }
 
-    if (!OVR::System::DirectDisplayEnabled() && !OVR::Display::InCompatibilityMode(false))
+#if defined(OVR_OS_WIN32)
+    // Older than Windows 7 SP1?
+    if (!IsWindows7SP1OrGreater())
     {
+        MessageBoxA(nullptr, "This software depends on features available starting with \nWindows 7 Service Pack 1, and it cannot start.", "LibOVR: Cannot start", 0);
         OVR_ASSERT(false);
-        return 0;
+        goto Abort;
     }
+#endif // OVR_OS_WIN32
 
-    CAPI_pNetClient = NetClient::GetInstance();
+    OVR::Net::RuntimeSDKVersion.SetCurrent();  // Fill in the constant parts of this struct.
+    OVR::Net::RuntimeSDKVersion.RequestedMinorVersion = (uint16_t)params->RequestedMinorVersion;
 
-#ifdef OVR_SINGLE_PROCESS
+    // Initialize display subsystem regardless of Allocator initialization.
+    OVR::Display::Initialize();
 
-    // If the server could not start running,
-    if (Service::NetServer::GetInstance()->IsInitialized())
+    // We must set up the system for the plugin to work
+    if (!OVR::System::IsInitialized())
     {
-        CAPI_pNetClient->Connect(true);
+        // TBD: Base this on registry setting?
+        Allocator::SetLeakTracking(DebugMode);
+
+        OVR::Log* logger = OVR::Log::ConfigureDefaultLog(OVR::LogMask_All);
+
+        // Set the CAPI logger callback
+        logger->SetCAPICallback(params->LogCallback);
+
+        OVR::System::Init(logger);
     }
-    else
+
+    if (!OVR::Display::GetDirectDisplayInitialized() && !OVR::Display::InCompatibilityMode(true))
     {
-        // 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");
+        OVR_ASSERT(false);
+        goto Abort;
     }
-#else
-    CAPI_pNetClient->Connect(true);
-#endif
 
+    CAPI_pNetClient = NetClient::GetInstance();
+
+    // Store off the initialization parameters from ovr_Initialize()
+    CAPI_pNetClient->ApplyParameters(params);
+
+    // Mark as initialized regardless of whether or not we can connect to the server.
     CAPI_ovrInitializeCalled = 1;
 
-    return 1;
+    // Log the location of the module after most of the bring-up, as the game
+    // could do almost anything in response to a log message callback.
+    LogLocationOfThisModule();
+
+    // If unable to connect to server and we are not in a debug mode,
+    if (!CAPI_pNetClient->Connect(true) && !DebugMode)
+    {
+        // Then it's a failure when the server is unreachable.
+        // This means that a DebugHMD cannot be created unless the ovrInit_Debug flag is set.
+        goto Abort;
+    }
+
+    // everything is okay
+    TraceReturn(0);
+    return ovrTrue;
+
+Abort:
+    // clean up and return failure
+    TraceReturn(0);
+    TraceFini();
+    return ovrFalse;
 }
 
-OVR_EXPORT void ovr_Shutdown()
+
+OVR_PUBLIC_FUNCTION(void) ovr_Shutdown()
 {  
-    // We should clean up the system to be complete
-    if (OVR::System::IsInitialized() && CAPI_SystemInitCalled)
+    if (CAPI_ovrInitializeCalled)
+    {
+        OVR::Display::Shutdown();
+        TraceCall(0);
+        TraceFini();
+        CAPI_ovrInitializeCalled = 0;
+    }
+
+    if (OVR::System::IsInitialized())
     {
         OVR::System::Destroy();
     }
 
-    CAPI_SystemInitCalled = 0;
-    CAPI_ovrInitializeCalled = 0;
+    OVR::Net::RuntimeSDKVersion.Reset();
+    CAPI_pNetClient = nullptr;  // Not strictly necessary, but useful for debugging and cleanliness.
 }
 
 
@@ -324,10 +365,10 @@ OVR_EXPORT void ovr_Shutdown()
 //         probably check this.
 //
 
-OVR_EXPORT int ovrHmd_Detect()
+OVR_PUBLIC_FUNCTION(int) ovrHmd_Detect()
 {
     if (!CAPI_ovrInitializeCalled)
-        return 0;
+        return -2;
 
     return CAPI_pNetClient->Hmd_Detect();
 }
@@ -337,7 +378,7 @@ OVR_EXPORT int ovrHmd_Detect()
 // 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)
+OVR_PUBLIC_FUNCTION(ovrHmd) ovrHmd_Create(int index)
 {
     if (!CAPI_ovrInitializeCalled)
         return 0;
@@ -351,18 +392,35 @@ OVR_EXPORT ovrHmd ovrHmd_Create(int index)
     while (!CAPI_pNetClient->Hmd_Create(index, &netInfo) ||
            netInfo.NetId == InvalidVirtualHmdId)
     {
-        // If two seconds elapse and still no HMD detected,
-        if (Timer::GetSeconds() - t0 > 2.)
+        double waitTime = 2.0; // Default wait time
+
+        if (NetClient::GetInstance()->IsConnected(false, false))
         {
-            if (!NetClient::GetInstance()->IsConnected(false, false))
+            NetClient::GetInstance()->SetLastError("No HMD Detected");
+
+            // If in single process mode,
+            if (Net::Session::IsSingleProcess())
             {
-                NetClient::GetInstance()->SetLastError("Not connected to service");
+                // Wait 8 seconds for HMD to be detected, as this is a single process
+                // build and we expect that the operator has the system set up properly.
+                waitTime = 8.0;
             }
             else
             {
-                NetClient::GetInstance()->SetLastError("No HMD Detected");
+                // Wait 1/2 second for HMD to be detected.
+                waitTime = 0.5;
             }
+        }
+        else
+        {
+            NetClient::GetInstance()->SetLastError("Not connected to service");
 
+            // Wait the default amount of time for the service to start up.
+        }
+
+        // If two seconds elapse and still no HMD detected,
+        if (Timer::GetSeconds() - t0 > waitTime)
+        {
             return 0;
         }
     }
@@ -380,24 +438,26 @@ OVR_EXPORT ovrHmd ovrHmd_Create(int index)
     // Reset frame timing so that FrameTimeManager values are properly initialized in AppRendered mode.
     ovrHmd_ResetFrameTiming(hmds->pHmdDesc, 0);
 
+    TraceHmdDesc(*hmds->pHmdDesc);
+
     return hmds->pHmdDesc;
 }
 
 
-OVR_EXPORT ovrBool ovrHmd_AttachToWindow( ovrHmd hmd, void* window,
-                                         const ovrRecti* destMirrorRect,
-                                         const ovrRecti* sourceRenderTargetRect )
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_AttachToWindow(ovrHmd hmddesc, void* window,
+                                                   const ovrRecti* destMirrorRect,
+                                                   const ovrRecti* sourceRenderTargetRect)
 {
-    OVR_UNUSED( destMirrorRect );
-    OVR_UNUSED( sourceRenderTargetRect );
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return ovrFalse;
+
+    OVR_UNUSED3(destMirrorRect, sourceRenderTargetRect, hmds);
 
     if (!CAPI_ovrInitializeCalled)
-        return false;
+        return ovrFalse;
 
-    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
@@ -408,27 +468,27 @@ OVR_EXPORT ovrBool ovrHmd_AttachToWindow( ovrHmd hmd, void* window,
     OVR_UNUSED(window);
 #endif
 
-    return true;
+    return ovrTrue;
 }
 
-OVR_EXPORT ovrHmd ovrHmd_CreateDebug(ovrHmdType type)
+OVR_PUBLIC_FUNCTION(ovrHmd) ovrHmd_CreateDebug(ovrHmdType type)
 {
     if (!CAPI_ovrInitializeCalled)
         return 0;
 
-    HMDState* hmds = HMDState::CreateHMDState(type);
+    HMDState* hmds = HMDState::CreateDebugHMDState(type);
+    if (!hmds)
+        return nullptr;
 
     return hmds->pHmdDesc;
 }
 
-OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmddesc)
+OVR_PUBLIC_FUNCTION(void) ovrHmd_Destroy(ovrHmd hmddesc)
 {
-    if (!hmddesc || !hmddesc->Handle)
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
         return;
-    
-    // TBD: Any extra shutdown?
-    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");
@@ -448,20 +508,16 @@ OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmddesc)
 }
 
 
-OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmddesc)
+OVR_PUBLIC_FUNCTION(const char*) ovrHmd_GetLastError(ovrHmd hmddesc)
 {
     if (!CAPI_ovrInitializeCalled)
-    {
         return "System initialize not called";
-    }
 
     VirtualHmdId netId = InvalidVirtualHmdId;
 
-    if (hmddesc && hmddesc->Handle)
-    {
-        HMDState* p = (HMDState*)hmddesc->Handle;
-        netId = p->GetNetId();
-    }
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (hmds)
+        netId = hmds->GetNetId();
 
     return CAPI_pNetClient->Hmd_GetLastError(netId);
 }
@@ -470,7 +526,7 @@ OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmddesc)
 
 // Returns version string representing libOVR version. Static, so
 // string remains valid for app lifespan
-OVR_EXPORT const char* ovr_GetVersionString()
+OVR_PUBLIC_FUNCTION(const char*) ovr_GetVersionString()
 {
 	static const char* version = OVR_VERSION_LIBOVR_PFX OVR_VERSION_STRING;
     return version + sizeof(OVR_VERSION_LIBOVR_PFX) - 1;
@@ -483,21 +539,24 @@ OVR_EXPORT const char* ovr_GetVersionString()
 // 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 hmddesc)
+OVR_PUBLIC_FUNCTION(unsigned int) ovrHmd_GetEnabledCaps(ovrHmd hmddesc)
 {
-    HMDState* p = (HMDState*)hmddesc->Handle;
-    return p ? p->EnabledHmdCaps : 0;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return 0;
+
+    return hmds->EnabledHmdCaps;
 }
 
 // Modifies capability bits described by ovrHmdCapBits that can be modified,
 // such as ovrHmdCap_LowPersistance.
-OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmddesc, unsigned int capsBits)
+OVR_PUBLIC_FUNCTION(void) ovrHmd_SetEnabledCaps(ovrHmd hmddesc, unsigned int capsBits)
 {
-    HMDState* p = (HMDState*)hmddesc->Handle;
-    if (p)
-    {
-        p->SetEnabledHmdCaps(capsBits);
-    }
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return;
+
+    hmds->SetEnabledHmdCaps(capsBits);
 }
 
 
@@ -516,62 +575,50 @@ OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmddesc, unsigned int capsBits)
 //    functions that have different rules (all frame access functions
 //    must be on render thread)
 
-OVR_EXPORT ovrBool ovrHmd_ConfigureTracking(ovrHmd hmddesc, unsigned int supportedCaps,
-                                                            unsigned int requiredCaps)
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_ConfigureTracking(ovrHmd hmddesc, unsigned int supportedCaps,
+                                                      unsigned int requiredCaps)
 {
-    if (hmddesc)
-    {
-        HMDState* p = (HMDState*)hmddesc->Handle;
-        return p->ConfigureTracking(supportedCaps, requiredCaps);
-    }
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return ovrFalse;
 
-    return 0;
+    return hmds->ConfigureTracking(supportedCaps, requiredCaps) ? ovrTrue : ovrFalse;
 }
 
-OVR_EXPORT void ovrHmd_RecenterPose(ovrHmd hmddesc)
+
+OVR_PUBLIC_FUNCTION(void) ovrHmd_RecenterPose(ovrHmd hmddesc)
 {
-    if (hmddesc)
-    {
-        HMDState* p = (HMDState*)hmddesc->Handle;
-        p->TheSensorStateReader.RecenterPose();
-    }
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return;
+
+    hmds->RecenterPose();
 }
 
-OVR_EXPORT ovrTrackingState ovrHmd_GetTrackingState(ovrHmd hmddesc, double absTime)
+OVR_PUBLIC_FUNCTION(ovrTrackingState) ovrHmd_GetTrackingState(ovrHmd hmddesc, double absTime)
 {
-    ovrTrackingState result;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return GetNullTrackingState();
 
-    if (hmddesc)
-    {
-        HMDState* p = (HMDState*)hmddesc->Handle;
-        result = p->PredictedTrackingState(absTime);
+    return hmds->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
 
-    return result;
-}
 
 
 //-------------------------------------------------------------------------------------
 // *** General Setup
 
 // Per HMD -> calculateIdealPixelSize
-OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmddesc, ovrEyeType eye, ovrFovPort fov,
-                                             float pixelsPerDisplayPixel)
+OVR_PUBLIC_FUNCTION(ovrSizei) ovrHmd_GetFovTextureSize(ovrHmd hmddesc, ovrEyeType eye,
+                                                       ovrFovPort fov, float pixelsPerDisplayPixel)
 {
-    ovrHmdStruct *  hmd = hmddesc->Handle;
-    if (!hmd) return Sizei(0);
-    
-    HMDState* hmds = (HMDState*)hmd;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return Sizei(0);
+
     return hmds->RenderState.GetFOVTextureSize(eye, fov, pixelsPerDisplayPixel);
 }
 
@@ -579,73 +626,69 @@ OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmddesc, ovrEyeType eye, ovr
 //-------------------------------------------------------------------------------------
 
 
-OVR_EXPORT 
-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);
-}
-
-
-
-// TBD: MA - Deprecated, need alternative
-void ovrHmd_SetVsync(ovrHmd hmddesc, ovrBool vsync)
+OVR_PUBLIC_FUNCTION(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;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return ovrFalse;
 
-    return ((HMDState*)hmd)->TimeManager.SetVsync(vsync? true : false);
+    return hmds->ConfigureRendering(eyeRenderDescOut, eyeFovIn,
+                                    apiConfig, distortionCaps);
 }
 
+OVR_PUBLIC_FUNCTION(ovrFrameTiming) ovrHmd_BeginFrame(ovrHmd hmddesc, unsigned int frameIndex)
+{
+    // NOTE: frameIndex == 0 is handled inside ovrHmd_BeginFrameTiming()
 
-OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmddesc, unsigned int frameIndex)
-{           
-    HMDState* hmds = (HMDState*)hmddesc->Handle;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
     if (!hmds)
-    {
-        ovrFrameTiming f;
-        memset(&f, 0, sizeof(f));
-        return f;
-    }
+        return GetNullFrameTiming();
+
+    TraceCall(frameIndex);
 
     // Check: Proper configure and threading state for the call.
     hmds->checkRenderingConfigured("ovrHmd_BeginFrame");
-	OVR_DEBUG_LOG_COND(hmds->BeginFrameCalled, ("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(hmddesc, frameIndex);
+    ovrFrameTiming timing = ovrHmd_BeginFrameTiming(hmddesc, frameIndex);
+
+    TraceReturn(frameIndex);
+
+    return timing;
 }
 
 
 // Renders textures to frame buffer
-OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmddesc,
-                                const ovrPosef renderPose[2],
-                                const ovrTexture eyeTexture[2])
+OVR_PUBLIC_FUNCTION(void) ovrHmd_EndFrame(ovrHmd hmddesc,
+                                          const ovrPosef renderPose[2],
+                                          const ovrTexture eyeTexture[2])
 {
-    HMDState* hmds = (HMDState*)hmddesc->Handle;
-    if (!hmds) return;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return;
 
-    // Instrument when the EndFrame() call started
-    hmds->LagStats.InstrumentEndFrameStart(ovr_GetTimeInSeconds());
+    const ovrTexture*        eyeDepthTexture = nullptr;
+    ovrPositionTimewarpDesc* posTimewarpDesc = nullptr;
 
-    hmds->SubmitEyeTextures(renderPose, eyeTexture);
+    TraceCall(hmds->BeginFrameIndex);
+
+    hmds->SubmitEyeTextures(renderPose, eyeTexture, eyeDepthTexture);
 
     // 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);
+    hmds->pRenderer->SetLatencyTestColor(hmds->LatencyTestActive ? hmds->LatencyTestDrawColor : nullptr);
 
-    ovrHmd_GetLatencyTest2DrawColor(hmddesc, NULL); // We don't actually need to draw color, so send NULL
+    ovrHmd_GetLatencyTest2DrawColor(hmddesc, nullptr); // We don't actually need to draw color, so send nullptr
     
     if (hmds->pRenderer)
     {
@@ -664,35 +707,32 @@ OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmddesc,
             }
         }
 
-        hmds->pRenderer->EndFrame(true);
+        if (posTimewarpDesc)
+            hmds->pRenderer->SetPositionTimewarpDesc(*posTimewarpDesc);
+
+        hmds->pRenderer->EndFrame(hmds->BeginFrameIndex, true);
         hmds->pRenderer->RestoreGraphicsState();
     }
 
     // Call after present
     ovrHmd_EndFrameTiming(hmddesc);
 
-    // Instrument latency tester
-    hmds->LagStats.InstrumentLatencyTimings(hmds->TimeManager);
-
-    // Instrument when the EndFrame() call ended
-    hmds->LagStats.InstrumentEndFrameEnd(ovr_GetTimeInSeconds());
+    TraceReturn(hmds->BeginFrameIndex);
 
     // Out of BeginFrame
     hmds->BeginFrameThreadId = 0;
     hmds->BeginFrameCalled   = false;
+    hmds->BeginFrameIndex++; // Set frame index to the next value in case 0 is passed.
 }
 
-
 // Not exposed as part of public API
-OVR_EXPORT void ovrHmd_RegisterPostDistortionCallback(ovrHmd hmddesc, PostDistortionCallback callback)
+OVR_PUBLIC_FUNCTION(void) ovrHmd_RegisterPostDistortionCallback(ovrHmd hmddesc, PostDistortionCallback callback)
 {
-    HMDState* hmds = (HMDState*)hmddesc->Handle;
-    if (!hmds) return;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds || !hmds->pRenderer)
+        return;
 
-    if (hmds->pRenderer)
-    {
-        hmds->pRenderer->RegisterPostDistortionCallback(callback);
-    }
+    hmds->pRenderer->RegisterPostDistortionCallback(callback);
 }
 
 
@@ -700,245 +740,262 @@ OVR_EXPORT void ovrHmd_RegisterPostDistortionCallback(ovrHmd hmddesc, PostDistor
 //-------------------------------------------------------------------------------------
 // ***** Frame Timing logic
 
-
-OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
+OVR_PUBLIC_FUNCTION(ovrFrameTiming) ovrHmd_GetFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
 {
-    ovrHmdStruct *  hmd = hmddesc->Handle;
-    ovrFrameTiming f;
-    memset(&f, 0, sizeof(f));
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return GetNullFrameTiming();
 
-    HMDState* hmds = (HMDState*)hmd;
-    if (hmds)
+    // If no frame index is provided,
+    if (frameIndex == 0)
     {
-        FrameTimeManager::Timing frameTiming = hmds->TimeManager.GetFrameTiming(frameIndex);
-
-        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];
-
-         // Compute DeltaSeconds.
-        f.DeltaSeconds = (hmds->LastGetFrameTimeSeconds == 0.0f) ? 0.0f :
-                         (float) (f.ThisFrameSeconds - hmds->LastFrameTimeSeconds);    
-        hmds->LastGetFrameTimeSeconds = f.ThisFrameSeconds;
-        if (f.DeltaSeconds > 1.0f)
-            f.DeltaSeconds = 1.0f;
+        // Use the next one in the series.
+        frameIndex = hmds->BeginFrameIndex;
     }
-        
-    return f;
+
+    return hmds->GetFrameTiming(frameIndex);
 }
 
-OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
+OVR_PUBLIC_FUNCTION(ovrFrameTiming) ovrHmd_BeginFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
 {
-    ovrHmdStruct *  hmd = hmddesc->Handle;
-    ovrFrameTiming f;
-    memset(&f, 0, sizeof(f));
-
-    HMDState* hmds = (HMDState*)hmd;
-    if (!hmds) return f;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return GetNullFrameTiming();
 
     // Check: Proper state for the call.    
     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();
+    // If a frame index is specified,
+    if (frameIndex != 0)
+    {
+        // Use the next one after the last BeginFrame() index.
+        hmds->BeginFrameIndex = (uint32_t)frameIndex;
+    }
+    else
+    {
+        frameIndex = hmds->BeginFrameIndex;
+    }
 
-    f.ThisFrameSeconds      = 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];
+    // Update latency tester once per frame
+    hmds->LatencyTestActive = hmds->ProcessLatencyTest(hmds->LatencyTestDrawColor);
 
-    // Compute DeltaSeconds.
-    f.DeltaSeconds = (hmds->LastFrameTimeSeconds == 0.0f) ? 0.0f :
-                     (float) (thisFrameTime - hmds->LastFrameTimeSeconds);
-    hmds->LastFrameTimeSeconds = thisFrameTime;
-    if (f.DeltaSeconds > 1.0f)
-        f.DeltaSeconds = 1.0f;
+    if (!hmds->BeginFrameCalled)
+    {
+        hmds->TimewarpTimer.CalculateTimewarpTiming(frameIndex);
+    }
 
-    return f;
+    return hmds->GetFrameTiming(frameIndex);
 }
 
-
-OVR_EXPORT void ovrHmd_EndFrameTiming(ovrHmd hmddesc)
+OVR_PUBLIC_FUNCTION(void) ovrHmd_EndFrameTiming(ovrHmd hmddesc)
 {
-    HMDState* hmds = (HMDState*)hmddesc->Handle;
-    if (!hmds) return;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return;
 
     // Debug state checks: Must be in BeginFrameTiming, on the same thread.
     hmds->checkBeginFrameTimingScope("ovrHmd_EndTiming");
    // 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);
-    }
+    hmds->endFrameRenderTiming();
 }
 
-
-OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmddesc,  unsigned int frameIndex) 
+OVR_PUBLIC_FUNCTION(void) ovrHmd_ResetFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
 {
-    HMDState* hmds = (HMDState*)hmddesc->Handle;
-    if (!hmds) return;
-    
-    hmds->TimeManager.ResetFrameTiming(frameIndex, 
-                                       false,
-                                       hmds->RenderingConfigured);
-    hmds->LastFrameTimeSeconds    = 0.0;
-    hmds->LastGetFrameTimeSeconds = 0.0;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return;
+
+    OVR_UNUSED(frameIndex);
+
+    // Clear timing-related state.
+    hmds->BeginFrameIndex = frameIndex;
+    hmds->TimewarpTimer.Reset();
 }
 
-OVR_EXPORT void ovrHmd_GetEyePoses(ovrHmd hmd, unsigned int frameIndex, ovrVector3f hmdToEyeViewOffset[2],
-                                   ovrPosef outEyePoses[2], ovrTrackingState* outHmdTrackingState)
+OVR_PUBLIC_FUNCTION(void) ovrHmd_GetEyePoses(ovrHmd hmddesc, unsigned int frameIndex, const ovrVector3f hmdToEyeViewOffset[2],
+                                             ovrPosef outEyePoses[2], ovrTrackingState* outHmdTrackingState)
 {
-    HMDState* hmds = (HMDState*)hmd->Handle;
-    if (!hmds) return;
+    if (!hmdToEyeViewOffset || !outEyePoses)
+    {
+        OVR_ASSERT(false);
+        return;
+    }
 
-    hmds->LatencyTestActive = hmds->ProcessLatencyTest(hmds->LatencyTestDrawColor);
-    
-    ovrTrackingState hmdTrackingState = hmds->TimeManager.GetEyePredictionTracking(hmd, ovrEye_Count, frameIndex);
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+    {
+        outEyePoses[0] = ovrPosef(); outEyePoses[0].Orientation.w = 1;
+        outEyePoses[1] = ovrPosef(); outEyePoses[1].Orientation.w = 1;
+        if (outHmdTrackingState)
+            *outHmdTrackingState = GetNullTrackingState();
+        return;
+    }
+
+    // If no frame index is provided,
+    if (frameIndex == 0)
+    {
+        // Use the next one in the series.
+        frameIndex = hmds->BeginFrameIndex;
+    }
+
+    ovrTrackingState hmdTrackingState = hmds->GetMidpointPredictionTracking(frameIndex);
+    TraceTrackingState(hmdTrackingState);
     ovrPosef hmdPose = hmdTrackingState.HeadPose.ThePose;
 
     // caller passed in a valid pointer, so copy to output
-    if(outHmdTrackingState)
+    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);
 }
 
-ovrPosef ovrHmd_GetHmdPosePerEye(ovrHmd hmd, ovrEyeType eye)
+OVR_PUBLIC_FUNCTION(ovrPosef) ovrHmd_GetHmdPosePerEye(ovrHmd hmddesc, ovrEyeType eye)
 {
-    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)
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
     {
-        hmds->LatencyTestActive = hmds->ProcessLatencyTest(hmds->LatencyTestDrawColor);
+        ovrPosef nullPose = ovrPosef();
+        nullPose.Orientation.w = 1.0f; // Return a proper quaternion.
+        return nullPose;
     }
 
     hmds->checkBeginFrameTimingScope("ovrHmd_GetEyePose");
-    return hmds->TimeManager.GetEyePredictionPose(hmd, eye);
+
+    return hmds->GetEyePredictionPose(eye);
 }
 
-OVR_EXPORT void ovrHmd_AddDistortionTimeMeasurement(ovrHmd hmddesc, double distortionTimeSeconds)
+OVR_PUBLIC_FUNCTION(void) ovrHmd_AddDistortionTimeMeasurement(ovrHmd hmddesc, double distortionTimeSeconds)
 {
-    if (!hmddesc)
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
         return;
-    HMDState* hmds = (HMDState*)hmddesc->Handle;
 
     hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices");   
-    hmds->TimeManager.AddDistortionTimeMeasurement(distortionTimeSeconds);
-}
-
 
+    hmds->TimewarpTimer.AddDistortionTimeMeasurement(distortionTimeSeconds);
+}
 
-OVR_EXPORT void ovrHmd_GetEyeTimewarpMatricesDebug(ovrHmd hmddesc, ovrEyeType eye,
-                                              ovrPosef renderPose, ovrMatrix4f twmOut[2],double debugTimingOffsetInSeconds)
+OVR_PUBLIC_FUNCTION(void) ovrHmd_GetEyeTimewarpMatricesDebug(ovrHmd hmddesc, ovrEyeType eye, ovrPosef renderPose,
+                                                             ovrQuatf playerTorsoMotion, ovrMatrix4f twmOut[2],
+                                                             double debugTimingOffsetInSeconds)
 {
-    if (!hmddesc)
+    if (!twmOut)
+    {
+        OVR_ASSERT(false);
+        return;
+    }
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
         return;
-    HMDState* hmds = (HMDState*)hmddesc->Handle;
 
     // Debug checks: BeginFrame was called, on the same thread.
     hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices");   
 
-    hmds->TimeManager.GetTimewarpMatrices(hmddesc, eye, renderPose, twmOut, debugTimingOffsetInSeconds);
+    // TODO: Position input disabled for now
+    bool calcPosition = false;
+    ovrVector3f* hmdToEyeViewOffset = nullptr;
 
-    /*
-    // MA: Took this out because new latency test approach just sames
-    //     the sample times in FrameTimeManager.
-    // TODO: if no timewarp, then test latency in begin eye render
-    if (eye == 0)
-    {        
-        hmds->ProcessLatencyTest2(hmds->LatencyTest2DrawColor, -1.0f);
-    }
-    */
+    // playerTorsoMotion can be fed in by the app to indicate player rotation,
+    // i.e. renderPose is in torso space, and playerTorsoMotion says that torso space changed.
+    Quatf playerTorsoMotionInv = Quatf(playerTorsoMotion).Inverted();
+    Posef renderPoseTorso = (Posef)renderPose * Posef(playerTorsoMotionInv, Vector3f::Zero());
+    hmds->GetTimewarpMatricesEx(eye, renderPoseTorso, calcPosition, hmdToEyeViewOffset, twmOut,
+                                debugTimingOffsetInSeconds);
 }
 
 
-OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmddesc, ovrEyeType eye,
-                                              ovrPosef renderPose, ovrMatrix4f twmOut[2])
+OVR_PUBLIC_FUNCTION(void) ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmddesc, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2])
 {
-    return(ovrHmd_GetEyeTimewarpMatricesDebug(hmddesc, eye, renderPose, twmOut, 0.0));
-}
+    if (!twmOut)
+    {
+        OVR_ASSERT(false);
+        return;
+    }
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return;
 
+    // Shortcut to doing orientation-only timewarp.
+
+    hmds->GetTimewarpMatrices(eye, renderPose, twmOut);
+}
 
 
-OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmddesc,
-                                                 ovrEyeType eyeType, ovrFovPort fov)
+OVR_PUBLIC_FUNCTION(ovrEyeRenderDesc) ovrHmd_GetRenderDesc(ovrHmd hmddesc,
+                                                           ovrEyeType eyeType, ovrFovPort fov)
 {
-    HMDState* hmds = (HMDState*)hmddesc->Handle;
-    ovrEyeRenderDesc erd;
-   
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
     if (!hmds)
-    {
-        memset(&erd, 0, sizeof(erd));
-        return erd;
-    }
+        return ovrEyeRenderDesc();
 
     return hmds->RenderState.CalcRenderDesc(eyeType, fov);
 }
 
 
-
 #define OVR_OFFSET_OF(s, field) ((size_t)&((s*)0)->field)
 
 
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_CreateDistortionMeshDebug(ovrHmd hmddesc,
+                                                              ovrEyeType eyeType, ovrFovPort fov,
+                                                              unsigned int distortionCaps,
+                                                              ovrDistortionMesh *meshData,
+												              float debugEyeReliefOverrideInMetres)
+{
+    if (!meshData)
+    {
+        OVR_ASSERT(false);
+        return ovrFalse;
+    }
 
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return ovrFalse;
 
-OVR_EXPORT ovrBool ovrHmd_CreateDistortionMeshDebug( ovrHmd hmddesc,
-                                                ovrEyeType eyeType, ovrFovPort fov,
-                                                unsigned int distortionCaps,
-                                                ovrDistortionMesh *meshData,
-												float debugEyeReliefOverrideInMetres)
-{
-    // 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));
-
-}
-OVR_EXPORT ovrBool ovrHmd_CreateDistortionMesh( ovrHmd hmddesc,
-                                                ovrEyeType eyeType, ovrFovPort fov,
-                                                unsigned int distortionCaps,
-                                                ovrDistortionMesh *meshData)
+    return hmds->CreateDistortionMesh(eyeType, fov,
+                                      distortionCaps,
+                                      meshData,
+                                      debugEyeReliefOverrideInMetres)
+           ? ovrTrue : ovrFalse;
+}
+
+
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_CreateDistortionMesh(ovrHmd hmddesc,
+                                                         ovrEyeType eyeType, ovrFovPort fov,
+                                                         unsigned int distortionCaps,
+                                                         ovrDistortionMesh *meshData)
 {
-    return(ovrHmd_CreateDistortionMeshDebug( hmddesc, eyeType, fov, distortionCaps,meshData, 0));
+    if (!meshData)
+    {
+        OVR_ASSERT(false);
+        return ovrFalse;
+    }
+
+    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)
+OVR_PUBLIC_FUNCTION(void) ovrHmd_DestroyDistortionMesh(ovrDistortionMesh* meshData)
 {
+    if (!meshData)
+    {
+        OVR_ASSERT(false);
+        return;
+    }
+
     if (meshData->pVertexData)
         DistortionMeshDestroy((DistortionMeshVertexData*)meshData->pVertexData,
                               meshData->pIndexData);
@@ -952,10 +1009,16 @@ OVR_EXPORT void ovrHmd_DestroyDistortionMesh(ovrDistortionMesh* meshData)
 
 // Computes updated 'uvScaleOffsetOut' to be used with a distortion if render target size or
 // viewport changes after the fact. This can be used to adjust render size every frame, if desired.
-OVR_EXPORT void ovrHmd_GetRenderScaleAndOffset( ovrFovPort fov,
-                                                ovrSizei textureSize, ovrRecti renderViewport,
-                                                ovrVector2f uvScaleOffsetOut[2] )
+OVR_PUBLIC_FUNCTION(void) ovrHmd_GetRenderScaleAndOffset(ovrFovPort fov,
+                                                         ovrSizei textureSize, ovrRecti renderViewport,
+                                                         ovrVector2f uvScaleOffsetOut[2] )
 {        
+    if (!uvScaleOffsetOut)
+    {
+        OVR_ASSERT(false);
+        return;
+    }
+
     // Find the mapping from TanAngle space to target NDC space.
     ScaleAndOffset2D  eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(fov);
     // Find the mapping from TanAngle space to textureUV space.
@@ -963,73 +1026,95 @@ OVR_EXPORT void ovrHmd_GetRenderScaleAndOffset( ovrFovPort fov,
                                          eyeToSourceNDC,
                                          renderViewport, textureSize );
 
-    uvScaleOffsetOut[0] = eyeToSourceUV.Scale;
-    uvScaleOffsetOut[1] = eyeToSourceUV.Offset;
+    if (uvScaleOffsetOut)
+    {
+        uvScaleOffsetOut[0] = eyeToSourceUV.Scale;
+        uvScaleOffsetOut[1] = eyeToSourceUV.Offset;
+    }
 }
 
 
 //-------------------------------------------------------------------------------------
 // ***** Latency Test interface
 
-OVR_EXPORT ovrBool ovrHmd_GetLatencyTestDrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3])
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_GetLatencyTestDrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3])
 {
-    HMDState* p = (HMDState*)hmddesc->Handle;
-    rgbColorOut[0] = p->LatencyTestDrawColor[0];
-    rgbColorOut[1] = p->LatencyTestDrawColor[1];
-    rgbColorOut[2] = p->LatencyTestDrawColor[2];
-    return p->LatencyTestActive;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return ovrFalse;
+
+    if (rgbColorOut)
+    {
+        rgbColorOut[0] = hmds->LatencyTestDrawColor[0];
+        rgbColorOut[1] = hmds->LatencyTestDrawColor[1];
+        rgbColorOut[2] = hmds->LatencyTestDrawColor[2];
+    }
+
+    return hmds->LatencyTestActive;
 }
 
-OVR_EXPORT ovrBool ovrHmd_ProcessLatencyTest(ovrHmd hmddesc, unsigned char rgbColorOut[3])
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_ProcessLatencyTest(ovrHmd hmddesc, unsigned char rgbColorOut[3])
 {
     OVR_UNUSED(hmddesc);
+
+    if (!rgbColorOut)
+    {
+        OVR_ASSERT(false);
+        return ovrFalse;
+    }
+
     return NetClient::GetInstance()->LatencyUtil_ProcessInputs(Timer::GetSeconds(), rgbColorOut);
 }
 
-OVR_EXPORT const char*  ovrHmd_GetLatencyTestResult(ovrHmd hmddesc)
+OVR_PUBLIC_FUNCTION(const char*) ovrHmd_GetLatencyTestResult(ovrHmd hmddesc)
 {
     OVR_UNUSED(hmddesc);
+
     return NetClient::GetInstance()->LatencyUtil_GetResultsString();
 }
 
-OVR_EXPORT ovrBool ovrHmd_GetLatencyTest2DrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3])
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_GetLatencyTest2DrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3])
 {
-    HMDState* hmds = (HMDState*)hmddesc->Handle;
-    if (!hmds) return false;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return ovrFalse;
 
     // TBD: Move directly into renderer
     bool dk2LatencyTest = (hmds->EnabledHmdCaps & ovrHmdCap_DynamicPrediction) != 0;
     if (dk2LatencyTest)
     {
-        hmds->TimeManager.GetFrameLatencyTestDrawColor(hmds->LatencyTest2DrawColor);
-        if(rgbColorOut != NULL)
+        hmds->LatencyTest2DrawColor[0] = hmds->ScreenLatencyTracker.GetNextDrawColor();
+        hmds->LatencyTest2DrawColor[1] = hmds->ScreenLatencyTracker.IsLatencyTimingAvailable() ? 255 : 0;
+        hmds->LatencyTest2DrawColor[2] = hmds->ScreenLatencyTracker.IsLatencyTimingAvailable() ? 0 : 255;
+
+        if (rgbColorOut)
         {
             rgbColorOut[0] = hmds->LatencyTest2DrawColor[0];
             rgbColorOut[1] = hmds->LatencyTest2DrawColor[1];
             rgbColorOut[2] = hmds->LatencyTest2DrawColor[2];
         }
 
-        if(hmds->pRenderer != NULL)
+        if (hmds->pRenderer)
             hmds->pRenderer->SetLatencyTest2Color(hmds->LatencyTest2DrawColor);
     }
     else
     {
-        if(hmds->pRenderer != NULL)
-            hmds->pRenderer->SetLatencyTest2Color(NULL);
+        if (hmds->pRenderer)
+            hmds->pRenderer->SetLatencyTest2Color(nullptr);
     }
 
-    return dk2LatencyTest;
+    return dk2LatencyTest ? ovrTrue : ovrFalse;
 }
 
 
-OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmddesc)
+OVR_PUBLIC_FUNCTION(double) ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmddesc)
 {
-    HMDState* p = (HMDState*)hmddesc->Handle;
-
-    // MA Test
-    float latencyRender, latencyTimewarp, latencyPostPresent;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return 0.;
 
-    p->TimeManager.GetLatencyTimings(latencyRender, latencyTimewarp, latencyPostPresent);
+    double latencyPostPresent = 0.;
+    hmds->ScreenLatencyTracker.GetVsyncToScanout(latencyPostPresent);
     return latencyPostPresent;
 }
 
@@ -1038,252 +1123,295 @@ OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmddesc)
 // ***** Health and Safety Warning Display interface
 //
 
-OVR_EXPORT void ovrHmd_GetHSWDisplayState(ovrHmd hmd, ovrHSWDisplayState *hswDisplayState)
+OVR_PUBLIC_FUNCTION(void) ovrHmd_GetHSWDisplayState(ovrHmd hmddesc, ovrHSWDisplayState *hswDisplayState)
 {
-    OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
-
-    if (pHMDState)
+    if (!hswDisplayState)
     {
-        OVR::CAPI::HSWDisplay* pHSWDisplay = pHMDState->pHSWDisplay;
-
-        if(pHSWDisplay)
-            pHSWDisplay->TickState(hswDisplayState); // This may internally call HSWDisplay::Display.
+        OVR_ASSERT(false);
+        return;
     }
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return;
+
+    OVR::CAPI::HSWDisplay* pHSWDisplay = hmds->pHSWDisplay;
+
+    if (pHSWDisplay)
+        pHSWDisplay->TickState(hswDisplayState); // This may internally call HSWDisplay::Display.
 }
 
-OVR_EXPORT ovrBool ovrHmd_DismissHSWDisplay(ovrHmd hmd)
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_DismissHSWDisplay(ovrHmd hmddesc)
 {
-    OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds)
+        return ovrFalse;
 
-    if (pHMDState)
-    {
-        OVR::CAPI::HSWDisplay* pHSWDisplay = pHMDState->pHSWDisplay;
+    OVR::CAPI::HSWDisplay* pHSWDisplay = hmds->pHSWDisplay;
 
-        if(pHSWDisplay)
-            return (pHSWDisplay->Dismiss() ? 1 : 0);
-    }
+    if (pHSWDisplay && pHSWDisplay->Dismiss())
+        return ovrTrue;
 
-    return false;
+    return ovrFalse;
 }
 
 
 // -----------------------------------------------------------------------------------
 // ***** Property Access
-OVR_EXPORT ovrBool ovrHmd_GetBool(ovrHmd hmddesc,
-                                  const char* propertyName,
-                                  ovrBool defaultVal)
+
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_GetBool(ovrHmd hmddesc,
+                                            const char* propertyName,
+                                            ovrBool defaultVal)
 {
-    OVR_ASSERT(propertyName);
-    if (hmddesc)
+    OVR_ASSERT(propertyName != nullptr);
+    if (!propertyName)
+        return ovrFalse;
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    bool success = false;
+    if (hmds)
     {
-        HMDState* hmds = (HMDState*)hmddesc->Handle;
-        OVR_ASSERT(hmds);
-        if (hmds)
-        {
-            return hmds->getBoolValue(propertyName, (defaultVal != 0));
-        }
+        success = hmds->getBoolValue(propertyName, (defaultVal != ovrFalse));
     }
-
-    return NetClient::GetInstance()->GetBoolValue(InvalidVirtualHmdId, propertyName, (defaultVal != 0)) ? 1 : 0;
-}
-
-OVR_EXPORT ovrBool ovrHmd_SetBool(ovrHmd hmddesc,
-                                  const char* propertyName,
-                                  ovrBool value)
-{
-    OVR_ASSERT(propertyName);
-    if (hmddesc)
+    else
     {
-        HMDState* hmds = (HMDState*)hmddesc->Handle;
-        OVR_ASSERT(hmds);
-        if (hmds)
-        {
-            return hmds->setBoolValue(propertyName, value != 0) ? 1 : 0;
-        }
+        success = NetClient::GetInstance()->GetBoolValue(InvalidVirtualHmdId, propertyName, (defaultVal != ovrFalse));
     }
-
-    return NetClient::GetInstance()->SetBoolValue(InvalidVirtualHmdId, propertyName, (value != 0)) ? 1 : 0;
+    return success ? ovrTrue : ovrFalse;
 }
 
-OVR_EXPORT int ovrHmd_GetInt(ovrHmd hmddesc,
-                             const char* propertyName,
-                             int defaultVal)
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetBool(ovrHmd hmddesc,
+                                            const char* propertyName,
+                                            ovrBool value)
 {
-    OVR_ASSERT(propertyName);
-    if (hmddesc)
+    OVR_ASSERT(propertyName != nullptr);
+    if (!propertyName)
+        return ovrFalse;
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    bool retval = false;
+    if (hmds)
     {
-        HMDState* hmds = (HMDState*)hmddesc->Handle;
-        OVR_ASSERT(hmds);
-        if (hmds)
-        {
-            return hmds->getIntValue(propertyName, defaultVal);
-        }
+        retval = hmds->setBoolValue(propertyName, value != ovrFalse);
     }
-
-    return NetClient::GetInstance()->GetIntValue(InvalidVirtualHmdId, propertyName, defaultVal);
+    else
+    {
+        retval = NetClient::GetInstance()->SetBoolValue(InvalidVirtualHmdId, propertyName, (value != ovrFalse));
+    }
+    return retval ? ovrTrue : ovrFalse;
 }
 
-OVR_EXPORT ovrBool ovrHmd_SetInt(ovrHmd hmddesc,
-                                 const char* propertyName,
-                                 int value)
+OVR_PUBLIC_FUNCTION(int) ovrHmd_GetInt(ovrHmd hmddesc,
+                                       const char* propertyName,
+                                       int defaultVal)
 {
-    OVR_ASSERT(propertyName);
-    if (hmddesc)
+    OVR_ASSERT(propertyName != nullptr);
+    if (!propertyName)
+        return ovrFalse;
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (hmds)
     {
-        HMDState* hmds = (HMDState*)hmddesc->Handle;
-        OVR_ASSERT(hmds);
-        if (hmds)
-        {
-            return hmds->setIntValue(propertyName, value) ? 1 : 0;
-        }
+        return hmds->getIntValue(propertyName, defaultVal);
+    }
+    else
+    {
+        return NetClient::GetInstance()->GetIntValue(InvalidVirtualHmdId, propertyName, defaultVal);
     }
-
-    return NetClient::GetInstance()->SetIntValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0;
 }
 
-OVR_EXPORT float ovrHmd_GetFloat(ovrHmd hmddesc,
-                                 const char* propertyName,
-                                 float defaultVal)
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetInt(ovrHmd hmddesc,
+                                           const char* propertyName,
+                                           int value)
 {
-    OVR_ASSERT(propertyName);
-    if (hmddesc)
+    OVR_ASSERT(propertyName != nullptr);
+    if (!propertyName)
+        return ovrFalse;
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    bool success = false;
+    if (hmds)
     {
-        HMDState* hmds = (HMDState*)hmddesc->Handle;
-        OVR_ASSERT(hmds);
-        if (hmds)
-        {
-            return hmds->getFloatValue(propertyName, defaultVal);
-        }
+        success = hmds->setIntValue(propertyName, value);
     }
-
-    return (float)NetClient::GetInstance()->GetNumberValue(InvalidVirtualHmdId, propertyName, defaultVal);
+    else
+    {
+        success = NetClient::GetInstance()->SetIntValue(InvalidVirtualHmdId, propertyName, value);
+    }
+    return success ? ovrTrue : ovrFalse;
 }
 
-OVR_EXPORT ovrBool ovrHmd_SetFloat(ovrHmd hmddesc,
-                                   const char* propertyName,
-                                   float value)
+OVR_PUBLIC_FUNCTION(float) ovrHmd_GetFloat(ovrHmd hmddesc,
+                                           const char* propertyName,
+                                           float defaultVal)
 {
-    OVR_ASSERT(propertyName);
-    if (hmddesc)
+    OVR_ASSERT(propertyName != nullptr);
+    if (!propertyName)
+        return ovrFalse;
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (hmds)
     {
-        HMDState* hmds = (HMDState*)hmddesc->Handle;
-        OVR_ASSERT(hmds);
-        if (hmds)
-        {
-            return hmds->setFloatValue(propertyName, value) ? 1 : 0;
-        }
+        return hmds->getFloatValue(propertyName, defaultVal);
+    }
+    else
+    {
+        return (float)NetClient::GetInstance()->GetNumberValue(InvalidVirtualHmdId, propertyName, defaultVal);
     }
-
-    return NetClient::GetInstance()->SetNumberValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0;
 }
 
-OVR_EXPORT unsigned int ovrHmd_GetFloatArray(ovrHmd hmddesc,
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetFloat(ovrHmd hmddesc,
                                              const char* propertyName,
-                                             float values[],
-                                             unsigned int arraySize)
+                                             float value)
 {
-    OVR_ASSERT(propertyName);
-    OVR_ASSERT(hmddesc);
-    if (hmddesc)
+    OVR_ASSERT(propertyName != nullptr);
+    if (!propertyName)
+        return ovrFalse;
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    bool success = false;
+    if (hmds)
     {
-        HMDState* hmds = (HMDState*)hmddesc->Handle;
-        OVR_ASSERT(hmds);
-        if (hmds)
-        {
-            return hmds->getFloatArray(propertyName, values, arraySize);
-        }
+        success = hmds->setFloatValue(propertyName, value);
+    }
+    else
+    {
+        success = NetClient::GetInstance()->SetNumberValue(InvalidVirtualHmdId, propertyName, value);
     }
+    return success ? ovrTrue : ovrFalse;
+}
+
+OVR_PUBLIC_FUNCTION(unsigned int) ovrHmd_GetFloatArray(ovrHmd hmddesc,
+                                                       const char* propertyName,
+                                                       float values[],
+                                                       unsigned int arraySize)
+{
+    OVR_ASSERT(propertyName != nullptr && values != nullptr);
+    if (!propertyName || !values)
+        return 0;
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds) return 0;
 
-    // FIXME: Currently it takes a few lines of code to do this, so just not supported for now.
-    return 0;
+    return hmds->getFloatArray(propertyName, values, arraySize);
 }
 
 // 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)
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetFloatArray(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)
-        {
-            return hmds->setFloatArray(propertyName, values, arraySize) ? 1 : 0;
-        }
-    }
+    OVR_ASSERT(propertyName != nullptr && values != nullptr);
+    if (!propertyName || !values)
+        return ovrFalse;
 
-    // FIXME: Currently it takes a few lines of code to do this, so just not supported for now.
-    return 0;
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (!hmds) return ovrFalse;
+
+    return hmds->setFloatArray(propertyName, values, arraySize) ? ovrTrue : ovrFalse;
 }
 
-OVR_EXPORT const char* ovrHmd_GetString(ovrHmd hmddesc,
-                                        const char* propertyName,
-                                        const char* defaultVal)
+OVR_PUBLIC_FUNCTION(const char*) ovrHmd_GetString(ovrHmd hmddesc,
+                                                  const char* propertyName,
+                                                  const char* defaultVal)
 {
-    OVR_ASSERT(propertyName);
-    if (hmddesc)
+    OVR_ASSERT(propertyName != nullptr);
+    if (!propertyName)
+        return "";
+
+    // Replace null default with empty string
+    if (!defaultVal)
+        defaultVal = "";
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    if (hmds)
     {
-        HMDState* hmds = (HMDState*)hmddesc->Handle;
-        if (hmds)
-        {
-            return hmds->getString(propertyName, defaultVal);
-        }
+        return hmds->getString(propertyName, defaultVal);
+    }
+    else
+    {
+        return NetClient::GetInstance()->GetStringValue(InvalidVirtualHmdId, propertyName, defaultVal);
     }
-
-    return NetClient::GetInstance()->GetStringValue(InvalidVirtualHmdId, propertyName, defaultVal);
 }
- 
-OVR_EXPORT ovrBool ovrHmd_SetString(ovrHmd hmddesc,
-                                    const char* propertyName,
-                                    const char* value)
+
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetString(ovrHmd hmddesc,
+                                              const char* propertyName,
+                                              const char* value)
 {
-    OVR_ASSERT(propertyName);
-    if (hmddesc)
+    OVR_ASSERT(propertyName != nullptr);
+    if (!propertyName)
+        return ovrFalse;
+
+    // Replace null value with empty string
+    if (!value)
+        value = "";
+
+    HMDState* hmds = GetHMDStateFromOvrHmd(hmddesc);
+    bool success = false;
+    if (hmds)
     {
-        HMDState* hmds = (HMDState*)hmddesc->Handle;
-        if (hmds)
-        {
-            return hmds->setString(propertyName, value) ? 1 : 0;
-        }
+        success = hmds->setString(propertyName, value);
     }
-
-    return NetClient::GetInstance()->SetStringValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0;
+    else
+    {
+        success = NetClient::GetInstance()->SetStringValue(InvalidVirtualHmdId, propertyName, value);
+    }
+    return success ? ovrTrue : ovrFalse;
 }
 
+
 // -----------------------------------------------------------------------------------
 // ***** Logging
 
-OVR_EXPORT ovrBool ovrHmd_StartPerfLog(ovrHmd hmd, const char* fileName, const char* userData1)
-{
-    OVR_ASSERT(fileName && fileName[0]);
+// make sure OVR_Log.h's enum matches CAPI's
+static_assert(
+    ((ovrLogLevel_Debug == ovrLogLevel(LogLevel_Debug)) &&
+    (ovrLogLevel_Info   == ovrLogLevel(LogLevel_Info)) &&
+    (ovrLogLevel_Error  == ovrLogLevel(LogLevel_Error))),
+    "mismatched LogLevel enums"
+);
 
-    OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
+#define OVR_TRACEMSG_MAX_LEN 1024 /* in chars */
 
-    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_PUBLIC_FUNCTION(int) ovr_TraceMessage(int level, const char* message)
 {
-    OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
+    OVR_ASSERT(message != nullptr);
+    if (!message)
+        return -1;
+
+    // Keep traced messages to some reasonable maximum length
+    int len = (int)strnlen(message, OVR_TRACEMSG_MAX_LEN);
+    if (len >= OVR_TRACEMSG_MAX_LEN)
+        return -1;
 
-    if (pHMDState)
+    switch (level)
     {
-        return pHMDState->LagStatsCSV.Stop() ? 1 : 0;
+    case ovrLogLevel_Debug:
+        TraceLogDebug(message);
+        break;
+    case ovrLogLevel_Info:
+    default:
+        TraceLogInfo(message);
+        break;
+    case ovrLogLevel_Error:
+        TraceLogError(message);
+        break;
     }
-    return false;
+
+    return len;
 }
 
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_StartPerfLog(ovrHmd hmddesc, const char* fileName, const char* userData1)
+{
+    // DEPRECATED
+    OVR_UNUSED3(hmddesc, fileName, userData1);
+    return ovrFalse;
+}
 
-#ifdef __cplusplus 
-} // extern "C"
-#endif
+OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_StopPerfLog(ovrHmd hmddesc)
+{
+    // DEPRECATED
+    OVR_UNUSED(hmddesc);
+    return ovrFalse;
+}
-- 
cgit v1.2.3