diff options
Diffstat (limited to 'LibOVR/Src/Util')
-rw-r--r-- | LibOVR/Src/Util/Util_ImageWindow.cpp | 46 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_ImageWindow.h | 17 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_Interface.h | 3 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_LatencyTest.cpp | 570 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_LatencyTest.h | 173 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_LatencyTest2.cpp | 191 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_LatencyTest2.h | 238 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_LatencyTest2Reader.cpp | 118 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_LatencyTest2Reader.h | 63 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_LatencyTest2State.h | 96 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_LongPollThread.cpp | 96 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_LongPollThread.h | 72 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_Render_Stereo.cpp | 136 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_Render_Stereo.h | 38 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_Settings.cpp | 312 | ||||
-rw-r--r-- | LibOVR/Src/Util/Util_Settings.h | 83 |
16 files changed, 972 insertions, 1280 deletions
diff --git a/LibOVR/Src/Util/Util_ImageWindow.cpp b/LibOVR/Src/Util/Util_ImageWindow.cpp index cb091c7..d937a25 100644 --- a/LibOVR/Src/Util/Util_ImageWindow.cpp +++ b/LibOVR/Src/Util/Util_ImageWindow.cpp @@ -23,12 +23,13 @@ See the License for the specific language governing permissions and limitations under the License. *************************************************************************************/ -#include "../../Include/OVR.h" +#include "../../Include/OVR_Kernel.h" #include "Util_ImageWindow.h" #if defined(OVR_OS_WIN32) +#define WIN32_LEAN_AND_MEAN #include <Windows.h> #include "DWrite.h" @@ -51,7 +52,14 @@ namespace OVR { namespace Util { ID2D1Factory* ImageWindow::pD2DFactory = NULL; IDWriteFactory* ImageWindow::pDWriteFactory = NULL; -ImageWindow* ImageWindow::globalWindow[4]; + + +// TODO(review): This appears to be (at present) necessary, the global list is accessed by the +// render loop in Samples. In the current version, windows will just be lost when windowCount +// exceeds MaxWindows; I've left that in place, since this is unfamiliar code. I'm not sure what +// thread-safety guarantees this portion of the code needs to satisfy, so I don't want to +// change it to a list or whatever. Asserts added to catch the error. +ImageWindow* ImageWindow::globalWindow[ImageWindow::MaxWindows]; int ImageWindow::windowCount = 0; LRESULT CALLBACK MainWndProc( @@ -115,9 +123,9 @@ ImageWindow::ImageWindow( uint32_t width, uint32_t height ) : writeFactory = (DWriteCreateFactoryFn)GetProcAddress( hInstWrite, "DWriteCreateFactory" ); } - globalWindow[windowCount] = this; - - ++windowCount; + // TODO: see note where globalWindow is declared. + globalWindow[windowCount++ % MaxWindows] = this; + OVR_ASSERT(windowCount < MaxWindows); if( pD2DFactory == NULL && createFactory && writeFactory ) { @@ -155,7 +163,7 @@ ImageWindow::~ImageWindow() globalWindow[i] = NULL; break; } -} + } if( greyBitmap ) greyBitmap->Release(); @@ -240,14 +248,14 @@ void ImageWindow::AssociateSurface( void* surface ) tmpTarget = NULL; } - result = tmpTarget->CreateBitmap( size, colorBitmapProps, &colorBitmap ); - if( result != S_OK ) + if (tmpTarget) { - greyBitmap->Release(); - greyBitmap = NULL; - - tmpTarget->Release(); - tmpTarget = NULL; + result = tmpTarget->CreateBitmap(size, colorBitmapProps, &colorBitmap); + if (result != S_OK) + { + tmpTarget->Release(); + tmpTarget = NULL; + } } pRT = tmpTarget; } @@ -508,4 +516,14 @@ void ImageWindow::addText( float x, float y, float r, float g, float b, OVR::Str }} -#endif //defined(OVR_OS_WIN32)
\ No newline at end of file +#else //defined(OVR_OS_WIN32) + +namespace OVR { namespace Util { + +ImageWindow* ImageWindow::globalWindow[4]; +int ImageWindow::windowCount = 0; + +}} + +#endif //#else //defined(OVR_OS_WIN32) + diff --git a/LibOVR/Src/Util/Util_ImageWindow.h b/LibOVR/Src/Util/Util_ImageWindow.h index 4b88959..901626d 100644 --- a/LibOVR/Src/Util/Util_ImageWindow.h +++ b/LibOVR/Src/Util/Util_ImageWindow.h @@ -28,13 +28,14 @@ limitations under the License. #define UTIL_IMAGEWINDOW_H #if defined(OVR_OS_WIN32) -#define WIN32_LEAN_AND_MEAN 1 -#include <windows.h> +#include <WinSock2.h> +#include <WS2tcpip.h> +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> #include <d2d1.h> #include <dwrite.h> #endif -#include "../../Include/OVR.h" #include "../Kernel/OVR_Hash.h" #include "../Kernel/OVR_Array.h" #include "../Kernel/OVR_Threads.h" @@ -171,16 +172,16 @@ public: void OnPaint() { } void UpdateImage( const uint8_t* imageData, uint32_t width, uint32_t height ) { UpdateImageBW( imageData, width, height ); } - void UpdateImageBW( const uint8_t* imageData, uint32_t width, uint32_t height ) { } - void UpdateImageRGBA( const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch ) { } + void UpdateImageBW( const uint8_t* imageData, uint32_t width, uint32_t height ) { OVR_UNUSED( imageData ); OVR_UNUSED( width ); OVR_UNUSED( height ); } + void UpdateImageRGBA( const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch ) { OVR_UNUSED( imageData ); OVR_UNUSED( width ); OVR_UNUSED( height ); OVR_UNUSED( pitch ); } void Complete() { } void Process() { } - void AssociateSurface( void* surface ) { } + void AssociateSurface( void* surface ) { OVR_UNUSED(surface); } - void addCircle( float x , float y, float radius, float r, float g, float b, bool fill ) { } - void addText( float x, float y, float r, float g, float b, OVR::String text ) { } + void addCircle( float x , float y, float radius, float r, float g, float b, bool fill ) { OVR_UNUSED( x ); OVR_UNUSED( y ); OVR_UNUSED( radius ); OVR_UNUSED( r ); OVR_UNUSED( g ); OVR_UNUSED( b ); OVR_UNUSED( fill ); } + void addText( float x, float y, float r, float g, float b, OVR::String text ) { OVR_UNUSED( x ); OVR_UNUSED( y ); OVR_UNUSED( r ); OVR_UNUSED( g ); OVR_UNUSED( b ); OVR_UNUSED( text ); } static ImageWindow* GlobalWindow( int window ) { return globalWindow[window]; } static int WindowCount() { return windowCount; } diff --git a/LibOVR/Src/Util/Util_Interface.h b/LibOVR/Src/Util/Util_Interface.h index 1bbf638..fbb7d25 100644 --- a/LibOVR/Src/Util/Util_Interface.h +++ b/LibOVR/Src/Util/Util_Interface.h @@ -1,6 +1,5 @@ /************************************************************************************ -PublicHeader: OVR.h Filename : Util_Interface.h Content : Simple interface, utilised by internal demos, with access to wider SDK as needed. @@ -30,7 +29,7 @@ limitations under the License. #ifndef OVR_Util_Interface_h #define OVR_Util_Interface_h -#include "../../Src/OVR_CAPI.h" +#include "../OVR_CAPI.h" //Files left in to ease its possible return...... diff --git a/LibOVR/Src/Util/Util_LatencyTest.cpp b/LibOVR/Src/Util/Util_LatencyTest.cpp deleted file mode 100644 index 3017c72..0000000 --- a/LibOVR/Src/Util/Util_LatencyTest.cpp +++ /dev/null @@ -1,570 +0,0 @@ -/************************************************************************************ - -Filename : Util_LatencyTest.cpp -Content : Wraps the lower level LatencyTester interface and adds functionality. -Created : February 14, 2013 -Authors : Lee Cooper - -Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. - -Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); -you may not use the Oculus VR Rift SDK except in compliance with the License, -which is provided at the time of installation or download, or which -otherwise accompanies this software in either electronic or hard copy form. - -You may obtain a copy of the License at - -http://www.oculusvr.com/licenses/LICENSE-3.1 - -Unless required by applicable law or agreed to in writing, the Oculus VR SDK -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -*************************************************************************************/ - -#include "Util_LatencyTest.h" - -#include "../Kernel/OVR_Log.h" -#include "../Kernel/OVR_Timer.h" - -namespace OVR { namespace Util { - -static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10; -static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10; -static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5; -static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5; -static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions. -static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4; -static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000; -static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000; -static const Color CALIBRATE_BLACK(0, 0, 0); -static const Color CALIBRATE_WHITE(255, 255, 255); -static const Color COLOR1(0, 0, 0); -static const Color COLOR2(255, 255, 255); -static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255); -static const float BIG_FLOAT = 1000000.0f; -static const float SMALL_FLOAT = -1000000.0f; - -//------------------------------------------------------------------------------------- -// ***** LatencyTest - -LatencyTest::LatencyTest(LatencyTestDevice* device) - : Handler(getThis()) -{ - if (device != NULL) - { - SetDevice(device); - } - - reset(); - - srand(Timer::GetTicksMs()); -} - -LatencyTest::~LatencyTest() -{ - clearMeasurementResults(); -} - -bool LatencyTest::SetDevice(LatencyTestDevice* device) -{ - - if (device != Device) - { - Handler.RemoveHandlerFromDevices(); - - Device = device; - - if (Device != NULL) - { - Device->AddMessageHandler(&Handler); - - // Set trigger threshold. - LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming. - Device->SetConfiguration(configuration, true); - - // Set display to initial (3 dashes). - LatencyTestDisplay ltd(2, 0x40400040); - Device->SetDisplay(ltd); - } - } - - return true; -} - -UInt32 LatencyTest::getRandomComponent(UInt32 range) -{ - UInt32 val = rand() % range; - return val; -} - -void LatencyTest::BeginTest() -{ - if (State == State_WaitingForButton) - { - // Set color to black and wait a while. - RenderColor = CALIBRATE_BLACK; - - State = State_WaitingForSettlePreCalibrationColorBlack; - OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack.")); - - setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); - } -} - -void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage) -{ - // For debugging. -/* if (msg.Type == Message_LatencyTestSamples) - { - MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg; - - if (pSamples->Samples.GetSize() > 0) - { - // Just show the first one for now. - Color c = pSamples->Samples[0]; - OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B)); - } - return; - } -*/ - - if (latencyTestMessage == LatencyTest_Timer) - { - if (!Device) - { - reset(); - return; - } - - if (State == State_WaitingForSettlePreCalibrationColorBlack) - { - // Send calibrate message to device and wait a while. - Device->SetCalibrate(CALIBRATE_BLACK); - - State = State_WaitingForSettlePostCalibrationColorBlack; - OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack.")); - - setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); - } - else if (State == State_WaitingForSettlePostCalibrationColorBlack) - { - // Change color to white and wait a while. - RenderColor = CALIBRATE_WHITE; - - State = State_WaitingForSettlePreCalibrationColorWhite; - OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite.")); - - setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); - } - else if (State == State_WaitingForSettlePreCalibrationColorWhite) - { - // Send calibrate message to device and wait a while. - Device->SetCalibrate(CALIBRATE_WHITE); - - State = State_WaitingForSettlePostCalibrationColorWhite; - OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite.")); - - setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); - } - else if (State == State_WaitingForSettlePostCalibrationColorWhite) - { - // Calibration is done. Switch to color 1 and wait for it to settle. - RenderColor = COLOR1; - - State = State_WaitingForSettlePostMeasurement; - OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement.")); - - UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); - setTimer(waitTime); - } - else if (State == State_WaitingForSettlePostMeasurement) - { - // Prepare for next measurement. - - // Create a new result object. - MeasurementResult* pResult = new MeasurementResult(); - Results.PushBack(pResult); - - State = State_WaitingToTakeMeasurement; - OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement.")); - } - else if (State == State_WaitingForTestStarted) - { - // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next. - getActiveResult()->TimedOutWaitingForTestStarted = true; - - State = State_WaitingForSettlePostMeasurement; - OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'.")); - OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement.")); - - UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); - setTimer(waitTime); - } - else if (State == State_WaitingForColorDetected) - { - // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next. - getActiveResult()->TimedOutWaitingForColorDetected = true; - - State = State_WaitingForSettlePostMeasurement; - OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'.")); - OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); - - UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); - setTimer(waitTime); - } - } - else if (latencyTestMessage == LatencyTest_ProcessInputs) - { - if (State == State_WaitingToTakeMeasurement) - { - if (!Device) - { - reset(); - return; - } - - // Send 'StartTest' feature report with opposite target color. - if (RenderColor == COLOR1) - { - RenderColor = COLOR2; - } - else - { - RenderColor = COLOR1; - } - - getActiveResult()->TargetColor = RenderColor; - - // Record time so we can determine usb roundtrip time. - getActiveResult()->StartTestSeconds = Timer::GetSeconds(); - - Device->SetStartTest(RenderColor); - - State = State_WaitingForTestStarted; - OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted.")); - - setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED); - - LatencyTestDisplay ltd(2, 0x40090040); - Device->SetDisplay(ltd); - } - } - else if (msg.Type == Message_LatencyTestButton) - { - BeginTest(); - } - else if (msg.Type == Message_LatencyTestStarted) - { - if (State == State_WaitingForTestStarted) - { - clearTimer(); - - // Record time so we can determine usb roundtrip time. - getActiveResult()->TestStartedSeconds = Timer::GetSeconds(); - - State = State_WaitingForColorDetected; - OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected.")); - - setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED); - } - } - else if (msg.Type == Message_LatencyTestColorDetected) - { - if (State == State_WaitingForColorDetected) - { - // Record time to detect color. - MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg; - UInt16 elapsedTime = pDetected->Elapsed; - OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime)); - - getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime; - - if (areResultsComplete()) - { - // We're done. - processResults(); - reset(); - } - else - { - // Run another measurement. - State = State_WaitingForSettlePostMeasurement; - OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); - - UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); - setTimer(waitTime); - - LatencyTestDisplay ltd(2, 0x40400040); - Device->SetDisplay(ltd); - } - } - } - else if (msg.Type == Message_DeviceRemoved) - { - reset(); - } -} - -LatencyTest::MeasurementResult* LatencyTest::getActiveResult() -{ - OVR_ASSERT(!Results.IsEmpty()); - return Results.GetLast(); -} - -void LatencyTest::setTimer(UInt32 timeMilliS) -{ - ActiveTimerMilliS = timeMilliS; -} - -void LatencyTest::clearTimer() -{ - ActiveTimerMilliS = 0; -} - -void LatencyTest::reset() -{ - clearMeasurementResults(); - State = State_WaitingForButton; - - HaveOldTime = false; - ActiveTimerMilliS = 0; -} - -void LatencyTest::clearMeasurementResults() -{ - while(!Results.IsEmpty()) - { - MeasurementResult* pElem = Results.GetFirst(); - pElem->RemoveNode(); - delete pElem; - } -} - -LatencyTest::LatencyTestHandler::~LatencyTestHandler() -{ - RemoveHandlerFromDevices(); -} - -void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg) -{ - pLatencyTestUtil->handleMessage(msg); -} - -void LatencyTest::ProcessInputs() -{ - updateForTimeouts(); - handleMessage(Message(), LatencyTest_ProcessInputs); -} - -bool LatencyTest::DisplayScreenColor(Color& colorToDisplay) -{ - updateForTimeouts(); - - if (State == State_WaitingForButton) - { - return false; - } - - colorToDisplay = RenderColor; - return true; -} - -const char* LatencyTest::GetResultsString() -{ - if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr()) - { - ReturnedResultString = ResultsString; - return ReturnedResultString.ToCStr(); - } - - return NULL; -} - -bool LatencyTest::areResultsComplete() -{ - UInt32 initialMeasurements = 0; - - UInt32 measurements1to2 = 0; - UInt32 measurements2to1 = 0; - - MeasurementResult* pCurr = Results.GetFirst(); - while(true) - { - // Process. - if (!pCurr->TimedOutWaitingForTestStarted && - !pCurr->TimedOutWaitingForColorDetected) - { - initialMeasurements++; - - if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE) - { - if (pCurr->TargetColor == COLOR2) - { - measurements1to2++; - } - else - { - measurements2to1++; - } - } - } - - if (Results.IsLast(pCurr)) - { - break; - } - pCurr = Results.GetNext(pCurr); - } - - if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && - measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) - { - return true; - } - - return false; -} - -void LatencyTest::processResults() -{ - - UInt32 minTime1To2 = UINT_MAX; - UInt32 maxTime1To2 = 0; - float averageTime1To2 = 0.0f; - UInt32 minTime2To1 = UINT_MAX; - UInt32 maxTime2To1 = 0; - float averageTime2To1 = 0.0f; - - float minUSBTripMilliS = BIG_FLOAT; - float maxUSBTripMilliS = SMALL_FLOAT; - float averageUSBTripMilliS = 0.0f; - UInt32 countUSBTripTime = 0; - - UInt32 measurementsCount = 0; - UInt32 measurements1to2 = 0; - UInt32 measurements2to1 = 0; - - MeasurementResult* pCurr = Results.GetFirst(); - UInt32 count = 0; - while(true) - { - count++; - - if (!pCurr->TimedOutWaitingForTestStarted && - !pCurr->TimedOutWaitingForColorDetected) - { - measurementsCount++; - - if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE) - { - if (pCurr->TargetColor == COLOR2) - { - measurements1to2++; - - if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES) - { - UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; - - minTime1To2 = Alg::Min(elapsed, minTime1To2); - maxTime1To2 = Alg::Max(elapsed, maxTime1To2); - - averageTime1To2 += (float) elapsed; - } - } - else - { - measurements2to1++; - - if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES) - { - UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; - - minTime2To1 = Alg::Min(elapsed, minTime2To1); - maxTime2To1 = Alg::Max(elapsed, maxTime2To1); - - averageTime2To1 += (float) elapsed; - } - } - - float usbRountripElapsedMilliS = Timer::MsPerSecond * (float) (pCurr->TestStartedSeconds - pCurr->StartTestSeconds); - minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS); - maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS); - averageUSBTripMilliS += usbRountripElapsedMilliS; - countUSBTripTime++; - } - } - - if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && - measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) - { - break; - } - - if (Results.IsLast(pCurr)) - { - break; - } - pCurr = Results.GetNext(pCurr); - } - - averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES; - averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES; - - averageUSBTripMilliS /= countUSBTripTime; - - float finalResult = 0.5f * (averageTime1To2 + averageTime2To1); - finalResult += averageUSBTripMilliS; - - ResultsString.Clear(); - ResultsString.AppendFormat("RESULT=%.1f (add half Tracker period) [b->w %d|%.1f|%d] [w->b %d|%.1f|%d] [usb rndtrp %.1f|%.1f|%.1f] [cnt %d] [tmouts %d]", - finalResult, - minTime1To2, averageTime1To2, maxTime1To2, - minTime2To1, averageTime2To1, maxTime2To1, - minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS, - DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount); - - // Display result on latency tester display. - LatencyTestDisplay ltd(1, (int)finalResult); - Device->SetDisplay(ltd); -} - -void LatencyTest::updateForTimeouts() -{ - if (!HaveOldTime) - { - HaveOldTime = true; - OldTime = Timer::GetTicksMs(); - return; - } - - UInt32 newTime = Timer::GetTicksMs(); - UInt32 elapsedMilliS = newTime - OldTime; - if (newTime < OldTime) - { - elapsedMilliS = OldTime - newTime; - elapsedMilliS = UINT_MAX - elapsedMilliS; - } - OldTime = newTime; - - elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often. - - - if (ActiveTimerMilliS == 0) - { - return; - } - - if (elapsedMilliS >= ActiveTimerMilliS) - { - ActiveTimerMilliS = 0; - handleMessage(Message(), LatencyTest_Timer); - return; - } - - ActiveTimerMilliS -= elapsedMilliS; -} - -}} // namespace OVR::Util diff --git a/LibOVR/Src/Util/Util_LatencyTest.h b/LibOVR/Src/Util/Util_LatencyTest.h deleted file mode 100644 index 0844603..0000000 --- a/LibOVR/Src/Util/Util_LatencyTest.h +++ /dev/null @@ -1,173 +0,0 @@ -/************************************************************************************ - -PublicHeader: OVR.h -Filename : Util_LatencyTest.h -Content : Wraps the lower level LatencyTesterDevice and adds functionality. -Created : February 14, 2013 -Authors : Lee Cooper - -Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. - -Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); -you may not use the Oculus VR Rift SDK except in compliance with the License, -which is provided at the time of installation or download, or which -otherwise accompanies this software in either electronic or hard copy form. - -You may obtain a copy of the License at - -http://www.oculusvr.com/licenses/LICENSE-3.1 - -Unless required by applicable law or agreed to in writing, the Oculus VR SDK -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -*************************************************************************************/ - -#ifndef OVR_Util_LatencyTest_h -#define OVR_Util_LatencyTest_h - -#include "../OVR_Device.h" - -#include "../Kernel/OVR_String.h" -#include "../Kernel/OVR_List.h" - -namespace OVR { namespace Util { - - -//------------------------------------------------------------------------------------- -// ***** LatencyTest -// -// LatencyTest utility class wraps the low level LatencyTestDevice and manages the scheduling -// of a latency test. A single test is composed of a series of individual latency measurements -// which are used to derive min, max, and an average latency value. -// -// Developers are required to call the following methods: -// SetDevice - Sets the LatencyTestDevice to be used for the tests. -// ProcessInputs - This should be called at the same place in the code where the game engine -// reads the headset orientation from LibOVR (typically done by calling -// 'GetOrientation' on the SensorFusion object). Calling this at the right time -// enables us to measure the same latency that occurs for headset orientation -// changes. -// DisplayScreenColor - The latency tester works by sensing the color of the pixels directly -// beneath it. The color of these pixels can be set by drawing a small -// quad at the end of the rendering stage. The quad should be small -// such that it doesn't significantly impact the rendering of the scene, -// but large enough to be 'seen' by the sensor. See the SDK -// documentation for more information. -// GetResultsString - Call this to get a string containing the most recent results. -// If the string has already been gotten then NULL will be returned. -// The string pointer will remain valid until the next time this -// method is called. -// - -class LatencyTest : public NewOverrideBase -{ -public: - LatencyTest(LatencyTestDevice* device = NULL); - ~LatencyTest(); - - // Set the Latency Tester device that we'll use to send commands to and receive - // notification messages from. - bool SetDevice(LatencyTestDevice* device); - - // Returns true if this LatencyTestUtil has a Latency Tester device. - bool HasDevice() const - { return Handler.IsHandlerInstalled(); } - - void ProcessInputs(); - bool DisplayScreenColor(Color& colorToDisplay); - const char* GetResultsString(); - - bool IsMeasuringNow() const { return (State != State_WaitingForButton); } - - // Begin test. Equivalent to pressing the button on the latency tester. - void BeginTest(); - -private: - LatencyTest* getThis() { return this; } - - enum LatencyTestMessageType - { - LatencyTest_None, - LatencyTest_Timer, - LatencyTest_ProcessInputs, - }; - - UInt32 getRandomComponent(UInt32 range); - void handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage = LatencyTest_None); - void reset(); - void setTimer(UInt32 timeMilliS); - void clearTimer(); - - class LatencyTestHandler : public MessageHandler - { - LatencyTest* pLatencyTestUtil; - public: - LatencyTestHandler(LatencyTest* latencyTester) : pLatencyTestUtil(latencyTester) { } - ~LatencyTestHandler(); - - virtual void OnMessage(const Message& msg); - }; - - bool areResultsComplete(); - void processResults(); - void updateForTimeouts(); - - Ptr<LatencyTestDevice> Device; - LatencyTestHandler Handler; - - enum TesterState - { - State_WaitingForButton, - State_WaitingForSettlePreCalibrationColorBlack, - State_WaitingForSettlePostCalibrationColorBlack, - State_WaitingForSettlePreCalibrationColorWhite, - State_WaitingForSettlePostCalibrationColorWhite, - State_WaitingToTakeMeasurement, - State_WaitingForTestStarted, - State_WaitingForColorDetected, - State_WaitingForSettlePostMeasurement - }; - TesterState State; - - bool HaveOldTime; - UInt32 OldTime; - UInt32 ActiveTimerMilliS; - - Color RenderColor; - - struct MeasurementResult : public ListNode<MeasurementResult>, public NewOverrideBase - { - MeasurementResult() - : DeviceMeasuredElapsedMilliS(0), - TimedOutWaitingForTestStarted(false), - TimedOutWaitingForColorDetected(false), - StartTestSeconds(0.0), - TestStartedSeconds(0.0) - {} - - Color TargetColor; - - UInt32 DeviceMeasuredElapsedMilliS; - - bool TimedOutWaitingForTestStarted; - bool TimedOutWaitingForColorDetected; - - double StartTestSeconds; - double TestStartedSeconds; - }; - - List<MeasurementResult> Results; - void clearMeasurementResults(); - - MeasurementResult* getActiveResult(); - - StringBuffer ResultsString; - String ReturnedResultString; -}; - -}} // namespace OVR::Util - -#endif // OVR_Util_LatencyTest_h diff --git a/LibOVR/Src/Util/Util_LatencyTest2.cpp b/LibOVR/Src/Util/Util_LatencyTest2.cpp deleted file mode 100644 index 6fc8b1f..0000000 --- a/LibOVR/Src/Util/Util_LatencyTest2.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/************************************************************************************ - -Filename : Util_LatencyTest2.cpp -Content : Wraps the lower level LatencyTester interface for DK2 and adds functionality. -Created : March 10, 2014 -Authors : Volga Aksoy - -Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. - -Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); -you may not use the Oculus VR Rift SDK except in compliance with the License, -which is provided at the time of installation or download, or which -otherwise accompanies this software in either electronic or hard copy form. - -You may obtain a copy of the License at - -http://www.oculusvr.com/licenses/LICENSE-3.1 - -Unless required by applicable law or agreed to in writing, the Oculus VR SDK -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -*************************************************************************************/ - -#include "Util_LatencyTest2.h" - -#include "../OVR_CAPI.h" -#include "../Kernel/OVR_Log.h" -#include "../Kernel/OVR_Timer.h" - - -namespace OVR { namespace Util { - -//------------------------------------------------------------------------------------- -// ***** LatencyTest2 - -LatencyTest2::LatencyTest2(SensorDevice* device) - : Handler(getThis()) - , TestActive(false) - , StartTiming(-1) - , LatencyMeasuredInSeconds(-1) - , LastPixelReadMsg(NULL) - , RenderColorValue(0) - , NumMsgsBeforeSettle(0) - , NumTestsSuccessful(0) -{ - if (device != NULL) - { - SetSensorDevice(device); - } -} - -LatencyTest2::~LatencyTest2() -{ - HmdDevice = NULL; - LatencyTesterDev = NULL; - - Handler.RemoveHandlerFromDevices(); -} - -bool LatencyTest2::SetSensorDevice(SensorDevice* device) -{ - Lock::Locker devLocker(&TesterLock); - - // Enable/Disable pixel read from HMD - if (device != HmdDevice) - { - Handler.RemoveHandlerFromDevices(); - - HmdDevice = device; - - if (HmdDevice != NULL) - { - HmdDevice->AddMessageHandler(&Handler); - } - } - - return true; -} - -bool LatencyTest2::SetDisplayDevice(LatencyTestDevice* device) -{ - Lock::Locker devLocker(&TesterLock); - - if (device != LatencyTesterDev) - { - LatencyTesterDev = device; - if (LatencyTesterDev != NULL) - { - // Set display to initial (3 dashes). - LatencyTestDisplay ltd(2, 0x40400040); - LatencyTesterDev->SetDisplay(ltd); - } - } - - return true; -} - -void LatencyTest2::BeginTest(double startTime) -{ - Lock::Locker devLocker(&TesterLock); - - if (!TestActive) - { - TestActive = true; - NumMsgsBeforeSettle = 0; - - // Go to next pixel value - //RenderColorValue = (RenderColorValue == 0) ? 255 : 0; - RenderColorValue = (RenderColorValue + LT2_ColorIncrement) % 256; - RawStartTiming = LastPixelReadMsg.RawSensorTime; - - if (startTime > 0.0) - StartTiming = startTime; - else - StartTiming = ovr_GetTimeInSeconds(); - - } -} - -void LatencyTest2::handleMessage(const MessagePixelRead& msg) -{ - Lock::Locker devLocker(&TesterLock); - - // Hold onto the last message as we will use this when we start a new test - LastPixelReadMsg = msg; - - // If color readback index is valid, store it in the lock-less queue. - int readbackIndex = 0; - if (FrameTimeRecord::ColorToReadbackIndex(&readbackIndex, msg.PixelReadValue)) - { - RecentFrameSet.AddValue(readbackIndex, msg.FrameTimeSeconds); - LockessRecords.SetState(RecentFrameSet); - } - - NumMsgsBeforeSettle++; - - if (TestActive) - { - int pixelValueDiff = RenderColorValue - LastPixelReadMsg.PixelReadValue; - int rawTimeDiff = LastPixelReadMsg.RawFrameTime - RawStartTiming; - - if (pixelValueDiff < LT2_PixelTestThreshold && pixelValueDiff > -LT2_PixelTestThreshold) - { - TestActive = false; - - LatencyMeasuredInSeconds = LastPixelReadMsg.FrameTimeSeconds - StartTiming; - RawLatencyMeasured = rawTimeDiff; - //LatencyMeasuredInSeconds = RawLatencyMeasured / 1000000.0; - - if(LatencyTesterDev && (NumTestsSuccessful % 5) == 0) - { - int displayNum = (int)(RawLatencyMeasured / 100.0); - //int displayNum = NumMsgsBeforeSettle; - //int displayNum = (int)(LatencyMeasuredInSeconds * 1000.0); - LatencyTestDisplay ltd(1, displayNum); - LatencyTesterDev->SetDisplay(ltd); - } - - NumTestsSuccessful++; - } - else if (TestActive && (rawTimeDiff / 1000) > LT2_TimeoutWaitingForColorDetected) - { - TestActive = false; - LatencyMeasuredInSeconds = -1; - } - } -} - -LatencyTest2::PixelReadHandler::~PixelReadHandler() -{ - RemoveHandlerFromDevices(); -} - -void LatencyTest2::PixelReadHandler::OnMessage(const Message& msg) -{ - if(msg.Type == Message_PixelRead) - pLatencyTestUtil->handleMessage(static_cast<const MessagePixelRead&>(msg)); -} - -bool LatencyTest2::DisplayScreenColor(Color& colorToDisplay) -{ - Lock::Locker devLocker(&TesterLock); - colorToDisplay = Color(RenderColorValue, RenderColorValue, RenderColorValue, 255); - - return TestActive; -} - -}} // namespace OVR::Util diff --git a/LibOVR/Src/Util/Util_LatencyTest2.h b/LibOVR/Src/Util/Util_LatencyTest2.h deleted file mode 100644 index 61e8477..0000000 --- a/LibOVR/Src/Util/Util_LatencyTest2.h +++ /dev/null @@ -1,238 +0,0 @@ -/************************************************************************************ - -PublicHeader: OVR.h -Filename : Util_LatencyTest2.h -Content : Wraps the lower level LatencyTester interface for DK2 and adds functionality. -Created : March 10, 2014 -Authors : Volga Aksoy - -Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. - -Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); -you may not use the Oculus VR Rift SDK except in compliance with the License, -which is provided at the time of installation or download, or which -otherwise accompanies this software in either electronic or hard copy form. - -You may obtain a copy of the License at - -http://www.oculusvr.com/licenses/LICENSE-3.1 - -Unless required by applicable law or agreed to in writing, the Oculus VR SDK -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -*************************************************************************************/ - -#ifndef OVR_Util_LatencyTest2_h -#define OVR_Util_LatencyTest2_h - -#include "../OVR_Device.h" - -#include "../Kernel/OVR_String.h" -#include "../Kernel/OVR_List.h" -#include "../Kernel/OVR_Lockless.h" - -namespace OVR { namespace Util { - - -enum { - LT2_ColorIncrement = 32, - LT2_PixelTestThreshold = LT2_ColorIncrement / 3, - LT2_IncrementCount = 256 / LT2_ColorIncrement, - LT2_TimeoutWaitingForColorDetected = 1000 // 1 second -}; - -//------------------------------------------------------------------------------------- - -// Describes frame scanout time used for latency testing. -struct FrameTimeRecord -{ - int ReadbackIndex; - double TimeSeconds; - - // Utility functions to convert color to readBack indices and back. - // The purpose of ReadbackIndex is to allow direct comparison by value. - - static bool ColorToReadbackIndex(int *readbackIndex, unsigned char color) - { - int compareColor = color - LT2_ColorIncrement/2; - int index = color / LT2_ColorIncrement; // Use color without subtraction due to rounding. - int delta = compareColor - index * LT2_ColorIncrement; - - if ((delta < LT2_PixelTestThreshold) && (delta > -LT2_PixelTestThreshold)) - { - *readbackIndex = index; - return true; - } - return false; - } - - static unsigned char ReadbackIndexToColor(int readbackIndex) - { - OVR_ASSERT(readbackIndex < LT2_IncrementCount); - return (unsigned char)(readbackIndex * LT2_ColorIncrement + LT2_ColorIncrement/2); - } -}; - -// FrameTimeRecordSet is a container holding multiple consecutive frame timing records -// returned from the lock-less state. Used by FrameTimeManager. - -struct FrameTimeRecordSet -{ - enum { - RecordCount = 4, - RecordMask = RecordCount - 1 - }; - FrameTimeRecord Records[RecordCount]; - int NextWriteIndex; - - FrameTimeRecordSet() - { - NextWriteIndex = 0; - memset(this, 0, sizeof(FrameTimeRecordSet)); - } - - void AddValue(int readValue, double timeSeconds) - { - Records[NextWriteIndex].ReadbackIndex = readValue; - Records[NextWriteIndex].TimeSeconds = timeSeconds; - NextWriteIndex ++; - if (NextWriteIndex == RecordCount) - NextWriteIndex = 0; - } - // Matching should be done starting from NextWrite index - // until wrap-around - - const FrameTimeRecord& operator [] (int i) const - { - return Records[(NextWriteIndex + i) & RecordMask]; - } - - const FrameTimeRecord& GetMostRecentFrame() - { - return Records[(NextWriteIndex - 1) & RecordMask]; - } - - // Advances I to absolute color index - bool FindReadbackIndex(int* i, int readbackIndex) const - { - for (; *i < RecordCount; (*i)++) - { - if ((*this)[*i].ReadbackIndex == readbackIndex) - return true; - } - return false; - } - - bool IsAllZeroes() const - { - for (int i = 0; i < RecordCount; i++) - if (Records[i].ReadbackIndex != 0) - return false; - return true; - } -}; - - -//------------------------------------------------------------------------------------- -// ***** LatencyTest2 -// -// LatencyTest2 utility class wraps the low level SensorDevice and manages the scheduling -// of a latency test. A single test is composed of a series of individual latency measurements -// which are used to derive min, max, and an average latency value. -// -// Developers are required to call the following methods: -// SetDevice - Sets the SensorDevice to be used for the tests. -// ProcessInputs - This should be called at the same place in the code where the game engine -// reads the headset orientation from LibOVR (typically done by calling -// 'GetOrientation' on the SensorFusion object). Calling this at the right time -// enables us to measure the same latency that occurs for headset orientation -// changes. -// DisplayScreenColor - The latency tester works by sensing the color of the pixels directly -// beneath it. The color of these pixels can be set by drawing a small -// quad at the end of the rendering stage. The quad should be small -// such that it doesn't significantly impact the rendering of the scene, -// but large enough to be 'seen' by the sensor. See the SDK -// documentation for more information. -// GetResultsString - Call this to get a string containing the most recent results. -// If the string has already been gotten then NULL will be returned. -// The string pointer will remain valid until the next time this -// method is called. -// - -class LatencyTest2 : public NewOverrideBase -{ -public: - LatencyTest2(SensorDevice* device = NULL); - ~LatencyTest2(); - - // Set the Latency Tester device that we'll use to send commands to and receive - // notification messages from. - bool SetSensorDevice(SensorDevice* device); - bool SetDisplayDevice(LatencyTestDevice* device); - - // Returns true if this LatencyTestUtil has a Latency Tester device. - bool HasDisplayDevice() const { return LatencyTesterDev.GetPtr() != NULL; } - bool HasDevice() const { return Handler.IsHandlerInstalled(); } - - bool DisplayScreenColor(Color& colorToDisplay); - //const char* GetResultsString(); - - // Begin test. Equivalent to pressing the button on the latency tester. - void BeginTest(double startTime = -1.0f); - bool IsMeasuringNow() const { return TestActive; } - double GetMeasuredLatency() const { return LatencyMeasuredInSeconds; } - -// - FrameTimeRecordSet GetLocklessState() { return LockessRecords.GetState(); } - -private: - LatencyTest2* getThis() { return this; } - - enum LatencyTestMessageType - { - LatencyTest_None, - LatencyTest_Timer, - LatencyTest_ProcessInputs, - }; - - void handleMessage(const MessagePixelRead& msg); - - class PixelReadHandler : public MessageHandler - { - LatencyTest2* pLatencyTestUtil; - public: - PixelReadHandler(LatencyTest2* latencyTester) : pLatencyTestUtil(latencyTester) { } - ~PixelReadHandler(); - - virtual void OnMessage(const Message& msg); - }; - PixelReadHandler Handler; - - Ptr<SensorDevice> HmdDevice; - Ptr<LatencyTestDevice> LatencyTesterDev; - - Lock TesterLock; - bool TestActive; - unsigned char RenderColorValue; - MessagePixelRead LastPixelReadMsg; - double StartTiming; - unsigned int RawStartTiming; - UInt32 RawLatencyMeasured; - double LatencyMeasuredInSeconds; - int NumMsgsBeforeSettle; - unsigned int NumTestsSuccessful; - - // MA: - // Frames are added here, then copied into lockess state - FrameTimeRecordSet RecentFrameSet; - LocklessUpdater<FrameTimeRecordSet> LockessRecords; -}; - - - -}} // namespace OVR::Util - -#endif // OVR_Util_LatencyTest2_h diff --git a/LibOVR/Src/Util/Util_LatencyTest2Reader.cpp b/LibOVR/Src/Util/Util_LatencyTest2Reader.cpp new file mode 100644 index 0000000..493f779 --- /dev/null +++ b/LibOVR/Src/Util/Util_LatencyTest2Reader.cpp @@ -0,0 +1,118 @@ +/************************************************************************************ + +Filename : Util_LatencyTest2Reader.cpp +Content : Shared functionality for the DK2 latency tester +Created : July 8, 2014 +Authors : Volga Aksoy, Chris Taylor + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "Util_LatencyTest2Reader.h" + +namespace OVR { namespace Util { + + +//// FrameTimeRecord + +bool FrameTimeRecord::ColorToReadbackIndex(int *readbackIndex, unsigned char color) +{ + int compareColor = color - LT2_ColorIncrement/2; + int index = color / LT2_ColorIncrement; // Use color without subtraction due to rounding. + int delta = compareColor - index * LT2_ColorIncrement; + + if ((delta < LT2_PixelTestThreshold) && (delta > -LT2_PixelTestThreshold)) + { + *readbackIndex = index; + return true; + } + return false; +} + +unsigned char FrameTimeRecord::ReadbackIndexToColor(int readbackIndex) +{ + OVR_ASSERT(readbackIndex < LT2_IncrementCount); + return (unsigned char)(readbackIndex * LT2_ColorIncrement + LT2_ColorIncrement/2); +} + + +//// FrameTimeRecordSet + +FrameTimeRecordSet::FrameTimeRecordSet() +{ + NextWriteIndex = 0; + memset(this, 0, sizeof(FrameTimeRecordSet)); +} + +void FrameTimeRecordSet::AddValue(int readValue, double timeSeconds) +{ + Records[NextWriteIndex].ReadbackIndex = readValue; + Records[NextWriteIndex].TimeSeconds = timeSeconds; + NextWriteIndex++; + if (NextWriteIndex == RecordCount) + NextWriteIndex = 0; +} +// Matching should be done starting from NextWrite index +// until wrap-around + +const FrameTimeRecord& FrameTimeRecordSet::operator [] (int i) const +{ + return Records[(NextWriteIndex + i) & RecordMask]; +} + +const FrameTimeRecord& FrameTimeRecordSet::GetMostRecentFrame() +{ + return Records[(NextWriteIndex - 1) & RecordMask]; +} + +// Advances I to absolute color index +bool FrameTimeRecordSet::FindReadbackIndex(int* i, int readbackIndex) const +{ + for (; *i < RecordCount; (*i)++) + { + if ((*this)[*i].ReadbackIndex == readbackIndex) + return true; + } + return false; +} + +bool FrameTimeRecordSet::IsAllZeroes() const +{ + for (int i = 0; i < RecordCount; i++) + if (Records[i].ReadbackIndex != 0) + return false; + return true; +} + + +//// RecordStateReader + +void RecordStateReader::GetRecordSet(FrameTimeRecordSet& recordset) +{ + if(!Updater) + { + return; + } + + recordset = Updater->SharedLatencyTestState.GetState(); + return; +} + + +}} // namespace OVR::Util diff --git a/LibOVR/Src/Util/Util_LatencyTest2Reader.h b/LibOVR/Src/Util/Util_LatencyTest2Reader.h new file mode 100644 index 0000000..57f70be --- /dev/null +++ b/LibOVR/Src/Util/Util_LatencyTest2Reader.h @@ -0,0 +1,63 @@ +/************************************************************************************ + +Filename : Util_LatencyTest2Reader.h +Content : Shared functionality for the DK2 latency tester +Created : July 8, 2014 +Authors : Volga Aksoy, Chris Taylor + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_Util_LatencyTest2Reader_h +#define OVR_Util_LatencyTest2Reader_h + +#include "../Tracking/Tracking_SensorState.h" +#include "Util_LatencyTest2State.h" + +namespace OVR { namespace Util { + + +//----------------------------------------------------------------------------- +// RecordStateReader + +// User interface to retrieve pose from the sensor fusion subsystem +class RecordStateReader : public NewOverrideBase +{ +protected: + const Tracking::CombinedSharedStateUpdater* Updater; + +public: + RecordStateReader() + : Updater(NULL) + { + } + + // Initialize the updater + void SetUpdater(const Tracking::CombinedSharedStateUpdater *updater) + { + Updater = updater; + } + + void GetRecordSet(FrameTimeRecordSet& recordset); +}; + + +}} // namespace OVR::Util + +#endif // OVR_Util_LatencyTest2Reader_h diff --git a/LibOVR/Src/Util/Util_LatencyTest2State.h b/LibOVR/Src/Util/Util_LatencyTest2State.h new file mode 100644 index 0000000..3ea4e76 --- /dev/null +++ b/LibOVR/Src/Util/Util_LatencyTest2State.h @@ -0,0 +1,96 @@ +/************************************************************************************ + +Filename : Util_LatencyTest2Reader.h +Content : Shared functionality for the DK2 latency tester +Created : July 8, 2014 +Authors : Volga Aksoy, Chris Taylor + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_Util_LatencyTest2_State_h +#define OVR_Util_LatencyTest2_State_h + +#include "../Kernel/OVR_Lockless.h" + +namespace OVR { namespace Util { + + +enum LatencyTester2Constants +{ + LT2_ColorIncrement = 32, + LT2_PixelTestThreshold = LT2_ColorIncrement / 3, + LT2_IncrementCount = 256 / LT2_ColorIncrement, + LT2_TimeoutWaitingForColorDetected = 1000 // 1 second +}; + + +//------------------------------------------------------------------------------------- +// FrameTimeRecord + +// Describes frame scan-out time used for latency testing. +struct FrameTimeRecord +{ + int ReadbackIndex; + double TimeSeconds; + + // Utility functions to convert color to readBack indices and back. + // The purpose of ReadbackIndex is to allow direct comparison by value. + + static bool ColorToReadbackIndex(int *readbackIndex, unsigned char color); + static unsigned char ReadbackIndexToColor(int readbackIndex); +}; + + +//----------------------------------------------------------------------------- +// FrameTimeRecordSet + +// FrameTimeRecordSet is a container holding multiple consecutive frame timing records +// returned from the lock-less state. Used by FrameTimeManager. +struct FrameTimeRecordSet +{ + enum { + RecordCount = 4, + RecordMask = RecordCount - 1 + }; + FrameTimeRecord Records[RecordCount]; + int NextWriteIndex; + + FrameTimeRecordSet(); + + void AddValue(int readValue, double timeSeconds); + // Matching should be done starting from NextWrite index + // until wrap-around + + const FrameTimeRecord& operator [] (int i) const; + + const FrameTimeRecord& GetMostRecentFrame(); + + // Advances I to absolute color index + bool FindReadbackIndex(int* i, int readbackIndex) const; + + bool IsAllZeroes() const; +}; + +typedef LocklessUpdater<FrameTimeRecordSet, FrameTimeRecordSet> LockessRecordUpdater; + + +}} // namespace OVR::Util + +#endif // OVR_Util_LatencyTest2_State_h diff --git a/LibOVR/Src/Util/Util_LongPollThread.cpp b/LibOVR/Src/Util/Util_LongPollThread.cpp new file mode 100644 index 0000000..c7c5c5d --- /dev/null +++ b/LibOVR/Src/Util/Util_LongPollThread.cpp @@ -0,0 +1,96 @@ +/************************************************************************************ + +Filename : Util_LongPollThread.cpp +Content : Allows us to do all long polling tasks from a single thread to minimize deadlock risk +Created : June 30, 2013 +Authors : Chris Taylor + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "Util_LongPollThread.h" +#include "Util_Watchdog.h" + +OVR_DEFINE_SINGLETON(OVR::Util::LongPollThread); + +namespace OVR { namespace Util { + + +void LongPollThread::AddPollFunc(Observer<PollFunc>* func) +{ + func->Observe(PollSubject); +} + +LongPollThread::LongPollThread() : + Terminated(false) +{ + Start(); + + PushDestroyCallbacks(); +} + +LongPollThread::~LongPollThread() +{ + fireTermination(); + + Join(); +} + +void LongPollThread::OnThreadDestroy() +{ + fireTermination(); +} + +void LongPollThread::Wake() +{ + WakeEvent.SetEvent(); +} + +void LongPollThread::fireTermination() +{ + Terminated = true; + Wake(); +} + +void LongPollThread::OnSystemDestroy() +{ + Release(); +} + +int LongPollThread::Run() +{ + SetThreadName("LongPoll"); + WatchDog watchdog("LongPoll"); + + // While not terminated, + do + { + watchdog.Feed(10000); + + PollSubject->Call(); + + WakeEvent.Wait(WakeupInterval); + WakeEvent.ResetEvent(); + } while (!Terminated); + + return 0; +} + + +}} // namespace OVR::Util diff --git a/LibOVR/Src/Util/Util_LongPollThread.h b/LibOVR/Src/Util/Util_LongPollThread.h new file mode 100644 index 0000000..5183e2f --- /dev/null +++ b/LibOVR/Src/Util/Util_LongPollThread.h @@ -0,0 +1,72 @@ +/************************************************************************************ + +Filename : Util_LongPollThread.h +Content : Allows us to do all long polling tasks from a single thread to minimize deadlock risk +Created : June 30, 2013 +Authors : Chris Taylor + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_Util_LongPollThread_h +#define OVR_Util_LongPollThread_h + +#include "../Kernel/OVR_Timer.h" +#include "../Kernel/OVR_Atomic.h" +#include "../Kernel/OVR_Allocator.h" +#include "../Kernel/OVR_String.h" +#include "../Kernel/OVR_System.h" +#include "../Kernel/OVR_Threads.h" +#include "../Kernel/OVR_Observer.h" + +namespace OVR { namespace Util { + + +//----------------------------------------------------------------------------- +// LongPollThread + +// This thread runs long-polling subsystems that wake up every second or so +// The motivation is to reduce the number of threads that are running to minimize the risk of deadlock +class LongPollThread : public Thread, public SystemSingletonBase<LongPollThread> +{ + OVR_DECLARE_SINGLETON(LongPollThread); + virtual void OnThreadDestroy(); + +public: + typedef Delegate0<void> PollFunc; + static const int WakeupInterval = 1000; // milliseconds + + void AddPollFunc(Observer<PollFunc>* func); + + void Wake(); + +protected: + ObserverScope<PollFunc> PollSubject; + + bool Terminated; + Event WakeEvent; + void fireTermination(); + + virtual int Run(); +}; + + +}} // namespace OVR::Util + +#endif // OVR_Util_LongPollThread_h diff --git a/LibOVR/Src/Util/Util_Render_Stereo.cpp b/LibOVR/Src/Util/Util_Render_Stereo.cpp index e84381e..6937141 100644 --- a/LibOVR/Src/Util/Util_Render_Stereo.cpp +++ b/LibOVR/Src/Util/Util_Render_Stereo.cpp @@ -25,10 +25,11 @@ limitations under the License. *************************************************************************************/ #include "Util_Render_Stereo.h" -#include "../OVR_SensorFusion.h" namespace OVR { namespace Util { namespace Render { +using namespace OVR::Tracking; + //----------------------------------------------------------------------------------- // **** Useful debug functions. @@ -442,7 +443,7 @@ const StereoEyeParamsWithOrtho& StereoConfig::GetEyeRenderParams(StereoEye eye) UpdateComputedState(); } - static const UByte eyeParamIndices[3] = { 0, 0, 1 }; + static const uint8_t eyeParamIndices[3] = { 0, 0, 1 }; OVR_ASSERT(eye < sizeof(eyeParamIndices)); return EyeRenderParams[eyeParamIndices[eye]]; @@ -787,13 +788,13 @@ static const int DMA_NumTrisPerEye = (DMA_GridSize)*(DMA_GridSize)*2; -void DistortionMeshDestroy ( DistortionMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices ) +void DistortionMeshDestroy ( DistortionMeshVertexData *pVertices, uint16_t *pTriangleMeshIndices ) { OVR_FREE ( pVertices ); OVR_FREE ( pTriangleMeshIndices ); } -void DistortionMeshCreate ( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, +void DistortionMeshCreate ( DistortionMeshVertexData **ppVertices, uint16_t **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo ) { @@ -811,7 +812,7 @@ void DistortionMeshCreate ( DistortionMeshVertexData **ppVertices, UInt16 **ppTr // Generate distortion mesh for a eye. -void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, +void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, uint16_t **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, bool rightEye, const HmdRenderInfo &hmdRenderInfo, @@ -822,7 +823,7 @@ void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTri *ppVertices = (DistortionMeshVertexData*) OVR_ALLOC( sizeof(DistortionMeshVertexData) * (*pNumVertices) ); - *ppTriangleListIndices = (UInt16*) OVR_ALLOC( sizeof(UInt16) * (*pNumTriangles) * 3 ); + *ppTriangleListIndices = (uint16_t*) OVR_ALLOC( sizeof(uint16_t) * (*pNumTriangles) * 3 ); if (!*ppVertices || !*ppTriangleListIndices) { @@ -870,11 +871,16 @@ void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTri sourceCoordNDC.y = 2.0f * ( (float)y / (float)DMA_GridSize ) - 1.0f; Vector2f tanEyeAngle = TransformRendertargetNDCToTanFovSpace ( eyeToSourceNDC, sourceCoordNDC ); - // This is the function that does the really heavy lifting. + // Find a corresponding screen position. + // Note - this function does not have to be precise - we're just trying to match the mesh tessellation + // with the shape of the distortion to minimise the number of trianlges needed. Vector2f screenNDC = TransformTanFovSpaceToScreenNDC ( distortion, tanEyeAngle, false ); + // ...but don't let verts overlap to the other eye. + screenNDC.x = Alg::Max ( -1.0f, Alg::Min ( screenNDC.x, 1.0f ) ); + screenNDC.y = Alg::Max ( -1.0f, Alg::Min ( screenNDC.y, 1.0f ) ); - // We then need RGB UVs. Since chromatic aberration is generated from screen coords, not - // directly from texture NDCs, we can't just use tanEyeAngle, we need to go the long way round. + // From those screen positions, we then need (effectively) RGB UVs. + // This is the function that actually matters when doing the distortion calculation. Vector2f tanEyeAnglesR, tanEyeAnglesG, tanEyeAnglesB; TransformScreenNDCToTanFovSpaceChroma ( &tanEyeAnglesR, &tanEyeAnglesG, &tanEyeAnglesB, distortion, screenNDC ); @@ -883,7 +889,6 @@ void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTri pcurVert->TanEyeAnglesG = tanEyeAnglesG; pcurVert->TanEyeAnglesB = tanEyeAnglesB; - HmdShutterTypeEnum shutterType = hmdRenderInfo.Shutter.Type; switch ( shutterType ) { @@ -914,17 +919,15 @@ void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTri } // Fade out at texture edges. + // The furthest out will be the blue channel, because of chromatic aberration (true of any standard lens) + Vector2f sourceTexCoordBlueNDC = TransformTanFovSpaceToRendertargetNDC ( eyeToSourceNDC, tanEyeAnglesB ); float edgeFadeIn = ( 1.0f / fadeOutBorderFraction ) * - ( 1.0f - Alg::Max ( Alg::Abs ( sourceCoordNDC.x ), Alg::Abs ( sourceCoordNDC.y ) ) ); + ( 1.0f - Alg::Max ( Alg::Abs ( sourceTexCoordBlueNDC.x ), Alg::Abs ( sourceTexCoordBlueNDC.y ) ) ); // Also fade out at screen edges. float edgeFadeInScreen = ( 2.0f / fadeOutBorderFraction ) * ( 1.0f - Alg::Max ( Alg::Abs ( screenNDC.x ), Alg::Abs ( screenNDC.y ) ) ); edgeFadeIn = Alg::Min ( edgeFadeInScreen, edgeFadeIn ); - // Don't let verts overlap to the other eye. - screenNDC.x = Alg::Max ( -1.0f, Alg::Min ( screenNDC.x, 1.0f ) ); - screenNDC.y = Alg::Max ( -1.0f, Alg::Min ( screenNDC.y, 1.0f ) ); - pcurVert->Shade = Alg::Max ( 0.0f, Alg::Min ( edgeFadeIn, 1.0f ) ); pcurVert->ScreenPosNDC.x = 0.5f * screenNDC.x - 0.5f + xOffset; pcurVert->ScreenPosNDC.y = -screenNDC.y; @@ -935,7 +938,7 @@ void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTri // Populate index buffer info - UInt16 *pcurIndex = *ppTriangleListIndices; + uint16_t *pcurIndex = *ppTriangleListIndices; for ( int triNum = 0; triNum < DMA_GridSize * DMA_GridSize; triNum++ ) { @@ -982,23 +985,23 @@ void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTri // so linear interpolation works better & we can use fewer tris. if ( ( x < DMA_GridSize/2 ) != ( y < DMA_GridSize/2 ) ) // != is logical XOR { - *pcurIndex++ = (UInt16)FirstVertex; - *pcurIndex++ = (UInt16)FirstVertex+1; - *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1)+1; + *pcurIndex++ = (uint16_t)FirstVertex; + *pcurIndex++ = (uint16_t)FirstVertex+1; + *pcurIndex++ = (uint16_t)FirstVertex+(DMA_GridSize+1)+1; - *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1)+1; - *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1); - *pcurIndex++ = (UInt16)FirstVertex; + *pcurIndex++ = (uint16_t)FirstVertex+(DMA_GridSize+1)+1; + *pcurIndex++ = (uint16_t)FirstVertex+(DMA_GridSize+1); + *pcurIndex++ = (uint16_t)FirstVertex; } else { - *pcurIndex++ = (UInt16)FirstVertex; - *pcurIndex++ = (UInt16)FirstVertex+1; - *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1); + *pcurIndex++ = (uint16_t)FirstVertex; + *pcurIndex++ = (uint16_t)FirstVertex+1; + *pcurIndex++ = (uint16_t)FirstVertex+(DMA_GridSize+1); - *pcurIndex++ = (UInt16)FirstVertex+1; - *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1)+1; - *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1); + *pcurIndex++ = (uint16_t)FirstVertex+1; + *pcurIndex++ = (uint16_t)FirstVertex+(DMA_GridSize+1)+1; + *pcurIndex++ = (uint16_t)FirstVertex+(DMA_GridSize+1); } } } @@ -1013,13 +1016,13 @@ static const int HMA_NumVertsPerEye = (HMA_GridSize+1)*(HMA_GridSize+1); static const int HMA_NumTrisPerEye = (HMA_GridSize)*(HMA_GridSize)*2; -void HeightmapMeshDestroy ( HeightmapMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices ) +void HeightmapMeshDestroy ( HeightmapMeshVertexData *pVertices, uint16_t *pTriangleMeshIndices ) { OVR_FREE ( pVertices ); OVR_FREE ( pTriangleMeshIndices ); } -void HeightmapMeshCreate ( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, +void HeightmapMeshCreate ( HeightmapMeshVertexData **ppVertices, uint16_t **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo ) { @@ -1037,7 +1040,7 @@ void HeightmapMeshCreate ( HeightmapMeshVertexData **ppVertices, UInt16 **ppTria // Generate heightmap mesh for one eye. -void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, +void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, uint16_t **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, bool rightEye, const HmdRenderInfo &hmdRenderInfo, const ScaleAndOffset2D &eyeToSourceNDC ) @@ -1046,7 +1049,7 @@ void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, UInt16 **ppTrian *pNumTriangles = HMA_NumTrisPerEye; *ppVertices = (HeightmapMeshVertexData*) OVR_ALLOC( sizeof(HeightmapMeshVertexData) * (*pNumVertices) ); - *ppTriangleListIndices = (UInt16*) OVR_ALLOC( sizeof(UInt16) * (*pNumTriangles) * 3 ); + *ppTriangleListIndices = (uint16_t*) OVR_ALLOC( sizeof(uint16_t) * (*pNumTriangles) * 3 ); if (!*ppVertices || !*ppTriangleListIndices) { @@ -1133,7 +1136,7 @@ void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, UInt16 **ppTrian // Populate index buffer info - UInt16 *pcurIndex = *ppTriangleListIndices; + uint16_t *pcurIndex = *ppTriangleListIndices; for ( int triNum = 0; triNum < HMA_GridSize * HMA_GridSize; triNum++ ) { @@ -1180,23 +1183,23 @@ void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, UInt16 **ppTrian // so linear interpolation works better & we can use fewer tris. if ( ( x < HMA_GridSize/2 ) != ( y < HMA_GridSize/2 ) ) // != is logical XOR { - *pcurIndex++ = (UInt16)FirstVertex; - *pcurIndex++ = (UInt16)FirstVertex+1; - *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1)+1; + *pcurIndex++ = (uint16_t)FirstVertex; + *pcurIndex++ = (uint16_t)FirstVertex+1; + *pcurIndex++ = (uint16_t)FirstVertex+(HMA_GridSize+1)+1; - *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1)+1; - *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1); - *pcurIndex++ = (UInt16)FirstVertex; + *pcurIndex++ = (uint16_t)FirstVertex+(HMA_GridSize+1)+1; + *pcurIndex++ = (uint16_t)FirstVertex+(HMA_GridSize+1); + *pcurIndex++ = (uint16_t)FirstVertex; } else { - *pcurIndex++ = (UInt16)FirstVertex; - *pcurIndex++ = (UInt16)FirstVertex+1; - *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1); + *pcurIndex++ = (uint16_t)FirstVertex; + *pcurIndex++ = (uint16_t)FirstVertex+1; + *pcurIndex++ = (uint16_t)FirstVertex+(HMA_GridSize+1); - *pcurIndex++ = (UInt16)FirstVertex+1; - *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1)+1; - *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1); + *pcurIndex++ = (uint16_t)FirstVertex+1; + *pcurIndex++ = (uint16_t)FirstVertex+(HMA_GridSize+1)+1; + *pcurIndex++ = (uint16_t)FirstVertex+(HMA_GridSize+1); } } } @@ -1318,7 +1321,7 @@ TimewarpMachine::TimewarpMachine() { for ( int i = 0; i < 2; i++ ) { - EyeRenderPoses[i] = Transformf(); + EyeRenderPoses[i] = Posef(); } DistortionTimeCount = 0; VsyncEnabled = false; @@ -1349,10 +1352,9 @@ double TimewarpMachine::GetViewRenderPredictionTime() return NextFramePresentFlushTime + CurrentPredictionValues.PresentFlushToRenderedScene; } -Transformf TimewarpMachine::GetViewRenderPredictionPose(SensorFusion &sfusion) +bool TimewarpMachine::GetViewRenderPredictionPose(SensorStateReader* reader, Posef& pose) { - double predictionTime = GetViewRenderPredictionTime(); - return sfusion.GetPoseAtTime(predictionTime); + return reader->GetPoseAtTime(GetViewRenderPredictionTime(), pose); } double TimewarpMachine::GetVisiblePixelTimeStart() @@ -1365,31 +1367,43 @@ double TimewarpMachine::GetVisiblePixelTimeEnd() // Note that PredictionGetDeviceValues() did all the vsync-dependent thinking for us. return NextFramePresentFlushTime + CurrentPredictionValues.PresentFlushToTimewarpEnd; } -Transformf TimewarpMachine::GetPredictedVisiblePixelPoseStart(SensorFusion &sfusion) +bool TimewarpMachine::GetPredictedVisiblePixelPoseStart(SensorStateReader* reader, Posef& pose) { - double predictionTime = GetVisiblePixelTimeStart(); - return sfusion.GetPoseAtTime(predictionTime); + return reader->GetPoseAtTime(GetVisiblePixelTimeStart(), pose); } -Transformf TimewarpMachine::GetPredictedVisiblePixelPoseEnd (SensorFusion &sfusion) +bool TimewarpMachine::GetPredictedVisiblePixelPoseEnd(SensorStateReader* reader, Posef& pose) { - double predictionTime = GetVisiblePixelTimeEnd(); - return sfusion.GetPoseAtTime(predictionTime); + return reader->GetPoseAtTime(GetVisiblePixelTimeEnd(), pose); } -Matrix4f TimewarpMachine::GetTimewarpDeltaStart(SensorFusion &sfusion, Transformf const &renderedPose) +bool TimewarpMachine::GetTimewarpDeltaStart(SensorStateReader* reader, Posef const &renderedPose, Matrix4f& transform) { - Transformf visiblePose = GetPredictedVisiblePixelPoseStart ( sfusion ); + Posef visiblePose; + if (!GetPredictedVisiblePixelPoseStart(reader, visiblePose)) + { + return false; + } + Matrix4f visibleMatrix(visiblePose); Matrix4f renderedMatrix(renderedPose); Matrix4f identity; // doesn't matter for orientation-only timewarp - return TimewarpComputePoseDelta ( renderedMatrix, visibleMatrix, identity ); + transform = TimewarpComputePoseDelta ( renderedMatrix, visibleMatrix, identity ); + + return true; } -Matrix4f TimewarpMachine::GetTimewarpDeltaEnd (SensorFusion &sfusion, Transformf const &renderedPose) +bool TimewarpMachine::GetTimewarpDeltaEnd(SensorStateReader* reader, Posef const &renderedPose, Matrix4f& transform) { - Transformf visiblePose = GetPredictedVisiblePixelPoseEnd ( sfusion ); + Posef visiblePose; + if (!GetPredictedVisiblePixelPoseEnd(reader, visiblePose)) + { + return false; + } + Matrix4f visibleMatrix(visiblePose); Matrix4f renderedMatrix(renderedPose); Matrix4f identity; // doesn't matter for orientation-only timewarp - return TimewarpComputePoseDelta ( renderedMatrix, visibleMatrix, identity ); + transform = TimewarpComputePoseDelta ( renderedMatrix, visibleMatrix, identity ); + + return true; } diff --git a/LibOVR/Src/Util/Util_Render_Stereo.h b/LibOVR/Src/Util/Util_Render_Stereo.h index 326059e..2517c37 100644 --- a/LibOVR/Src/Util/Util_Render_Stereo.h +++ b/LibOVR/Src/Util/Util_Render_Stereo.h @@ -1,6 +1,5 @@ /************************************************************************************ -PublicHeader: OVR.h Filename : Util_Render_Stereo.h Content : Sample stereo rendering configuration classes. Created : October 22, 2012 @@ -29,13 +28,9 @@ limitations under the License. #define OVR_Util_Render_Stereo_h #include "../OVR_Stereo.h" +#include "../Tracking/Tracking_SensorStateReader.h" - -namespace OVR { - -class SensorFusion; - -namespace Util { namespace Render { +namespace OVR { namespace Util { namespace Render { @@ -334,19 +329,19 @@ struct DistortionMeshVertexData }; -void DistortionMeshCreate ( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, +void DistortionMeshCreate ( DistortionMeshVertexData **ppVertices, uint16_t **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo ); // Generate distortion mesh for a eye. This version requires less data then stereoParms, supporting // dynamic change in render target viewport. -void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, +void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, uint16_t **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, bool rightEye, const HmdRenderInfo &hmdRenderInfo, const DistortionRenderDesc &distortion, const ScaleAndOffset2D &eyeToSourceNDC ); -void DistortionMeshDestroy ( DistortionMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices ); +void DistortionMeshDestroy ( DistortionMeshVertexData *pVertices, uint16_t *pTriangleMeshIndices ); //----------------------------------------------------------------------------------- @@ -368,17 +363,17 @@ struct HeightmapMeshVertexData }; -void HeightmapMeshCreate ( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, +void HeightmapMeshCreate ( HeightmapMeshVertexData **ppVertices, uint16_t **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo ); // Generate heightmap mesh for a eye. This version requires less data then stereoParms, supporting // dynamic change in render target viewport. -void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, +void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, uint16_t **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, bool rightEye, const HmdRenderInfo &hmdRenderInfo, const ScaleAndOffset2D &eyeToSourceNDC ); -void HeightmapMeshDestroy ( HeightmapMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices ); +void HeightmapMeshDestroy ( HeightmapMeshVertexData *pVertices, uint16_t *pTriangleMeshIndices ); @@ -434,7 +429,7 @@ public: // and the predicted pose of the HMD at that time. // You usually only need to call one of these functions. double GetViewRenderPredictionTime(); - Transformf GetViewRenderPredictionPose(SensorFusion &sfusion); + bool GetViewRenderPredictionPose(Tracking::SensorStateReader* reader, Posef& transform); // Timewarp prediction functions. You usually only need to call one of these three sets of functions. @@ -443,14 +438,13 @@ public: double GetVisiblePixelTimeStart(); double GetVisiblePixelTimeEnd(); // Predicted poses of the HMD at those first and last pixels. - Transformf GetPredictedVisiblePixelPoseStart(SensorFusion &sfusion); - Transformf GetPredictedVisiblePixelPoseEnd (SensorFusion &sfusion); + bool GetPredictedVisiblePixelPoseStart(Tracking::SensorStateReader* reader, Posef& transform); + bool GetPredictedVisiblePixelPoseEnd(Tracking::SensorStateReader* reader, Posef& transform); // The delta matrices to feed to the timewarp distortion code, // given the pose that was used for rendering. // (usually the one returned by GetViewRenderPredictionPose() earlier) - Matrix4f GetTimewarpDeltaStart(SensorFusion &sfusion, Transformf const &renderedPose); - Matrix4f GetTimewarpDeltaEnd (SensorFusion &sfusion, Transformf const &renderedPose); - + bool GetTimewarpDeltaStart(Tracking::SensorStateReader* reader, Posef const &renderedPose, Matrix4f& transform); + bool GetTimewarpDeltaEnd(Tracking::SensorStateReader* reader, Posef const &renderedPose, Matrix4f& transform); // Just-In-Time distortion aims to delay the second sensor reading & distortion // until the very last moment to improve prediction. However, it is a little scary, @@ -466,9 +460,7 @@ public: void JustInTime_BeforeDistortionTimeMeasurement(double timeNow); void JustInTime_AfterDistortionTimeMeasurement(double timeNow); - private: - bool VsyncEnabled; HmdRenderInfo RenderInfo; PredictionValues CurrentPredictionValues; @@ -480,11 +472,11 @@ private: float DistortionTimeAverage; // Pose at which last time the eye was rendered. - Transformf EyeRenderPoses[2]; + Posef EyeRenderPoses[2]; // Absolute time of the last present+flush double LastFramePresentFlushTime; - // Seconds between presentflushes + // Seconds between present+flushes float PresentFlushToPresentFlushSeconds; // Predicted absolute time of the next present+flush double NextFramePresentFlushTime; diff --git a/LibOVR/Src/Util/Util_Settings.cpp b/LibOVR/Src/Util/Util_Settings.cpp new file mode 100644 index 0000000..d7b3f0d --- /dev/null +++ b/LibOVR/Src/Util/Util_Settings.cpp @@ -0,0 +1,312 @@ +/************************************************************************************ + +Filename : Util_Settings.cpp +Content : Persistent settings subsystem +Created : June 11, 2014 +Author : Chris Taylor + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "Util_Settings.h" +#include "../OVR_Profile.h" + +OVR_DEFINE_SINGLETON(Util::Settings); + +namespace OVR { namespace Util { + + +//// Settings + +Settings::Settings() : + Dirty(false) +{ + // Set up long poll handler + PollObserver.SetHandler(LongPollThread::PollFunc::FromMember<Settings, &Settings::pollDirty>(this)); + LongPollThread::GetInstance()->AddPollFunc(PollObserver); + + PushDestroyCallbacks(); +} + +Settings::~Settings() +{ + PollObserver.ReleaseAll(); + + Lock::Locker locker(&DataLock); + + if (Dirty) + { + updateFile(); + Dirty = false; + } +} + +void Settings::OnSystemDestroy() +{ + delete this; +} + +void Settings::SetFileName(const String& fileName) +{ + Lock::Locker locker(&DataLock); + + OVR_ASSERT(FullFilePath.IsEmpty()); + + if (FullFilePath.IsEmpty()) + { + FullFilePath = GetBaseOVRPath(true) + "/" + fileName; + + loadFile(); + } +} + +void Settings::loadFile() +{ + Root = *JSON::Load(FullFilePath.ToCStr()); + if (!Root) + { + OVR_DEBUG_LOG(("[Settings] Settings file was empty")); + } + else + { + OVR_DEBUG_LOG(("[Settings] Successfully read settings file")); + } +} + +void Settings::updateFile() +{ + OVR_ASSERT(!FullFilePath.IsEmpty()); + + if (Root->Save(FullFilePath.ToCStr())) + { + OVR_DEBUG_LOG(("[Settings] Updated settings file: %s", FullFilePath.ToCStr())); + Dirty = false; + } + else + { + LogError("[Settings] WARNING: Unable to write settings file: %s", FullFilePath.ToCStr()); + OVR_ASSERT(false); + } +} + +void Settings::pollDirty() +{ + // If dirty, + if (Dirty) + { + Lock::Locker locker(&DataLock); + + if (!Dirty) + { + return; + } + + updateFile(); + } +} + +void Settings::SetNumber(const char* key, double value) +{ + Lock::Locker locker(&DataLock); + + Dirty = true; + + if (!Root) + { + Root = *JSON::CreateObject(); + } + + Ptr<JSON> item = Root->GetItemByName(key); + if (!item) + { + Root->AddNumberItem(key, value); + return; + } + + item->Type = JSON_Number; + item->dValue = value; +} + +void Settings::SetInt(const char* key, int value) +{ + Lock::Locker locker(&DataLock); + + if (!Root) + { + Root = *JSON::CreateObject(); + Dirty = true; + } + + Ptr<JSON> item = Root->GetItemByName(key); + if (!item) + { + Root->AddIntItem(key, value); + Dirty = true; + return; + } + + // If the value changed, + if (item->Type != JSON_Number || + (int)item->dValue != value) + { + item->Type = JSON_Number; + item->dValue = value; + Dirty = true; + } +} + +void Settings::SetBool(const char* key, bool value) +{ + Lock::Locker locker(&DataLock); + + if (!Root) + { + Root = *JSON::CreateObject(); + Dirty = true; + } + + Ptr<JSON> item = Root->GetItemByName(key); + if (!item) + { + Root->AddBoolItem(key, value); + Dirty = true; + return; + } + + // If the value changed, + if (item->Type != JSON_Bool || + ((int)item->dValue != 0) != value) + { + item->Type = JSON_Bool; + item->dValue = value ? 1. : 0.; + item->Value = value ? "true" : "false"; + Dirty = true; + } +} + +void Settings::SetString(const char* key, const char* value) +{ + Lock::Locker locker(&DataLock); + + if (!Root) + { + Root = *JSON::CreateObject(); + Dirty = true; + } + + Ptr<JSON> item = Root->GetItemByName(key); + if (!item) + { + Root->AddStringItem(key, value); + Dirty = true; + return; + } + + // If the value changed, + if (item->Type != JSON_String || + item->Value != value) + { + item->Type = JSON_String; + item->Value = value; + Dirty = true; + } +} + +double Settings::GetNumber(const char* key, double defaultValue) +{ + Lock::Locker locker(&DataLock); + + if (!Root) + { + SetNumber(key, defaultValue); + return defaultValue; + } + + Ptr<JSON> item = Root->GetItemByName(key); + if (!item) + { + SetNumber(key, defaultValue); + return defaultValue; + } + + return item->dValue; +} + +int Settings::GetInt(const char* key, int defaultValue) +{ + Lock::Locker locker(&DataLock); + + if (!Root) + { + SetInt(key, defaultValue); + return defaultValue; + } + + Ptr<JSON> item = Root->GetItemByName(key); + if (!item) + { + SetInt(key, defaultValue); + return defaultValue; + } + + return (int)item->dValue; +} + +bool Settings::GetBool(const char* key, bool defaultValue) +{ + Lock::Locker locker(&DataLock); + + if (!Root) + { + SetBool(key, defaultValue); + return defaultValue; + } + + Ptr<JSON> item = Root->GetItemByName(key); + if (!item) + { + SetBool(key, defaultValue); + return defaultValue; + } + + return (int)item->dValue != 0; +} + +String Settings::GetString(const char* key, const char* defaultValue) +{ + Lock::Locker locker(&DataLock); + + if (!Root) + { + SetString(key, defaultValue); + return defaultValue; + } + + Ptr<JSON> item = Root->GetItemByName(key); + if (!item) + { + SetString(key, defaultValue); + return defaultValue; + } + + return item->Value; +} + + +}} // namespace OVR::Util diff --git a/LibOVR/Src/Util/Util_Settings.h b/LibOVR/Src/Util/Util_Settings.h new file mode 100644 index 0000000..79ea172 --- /dev/null +++ b/LibOVR/Src/Util/Util_Settings.h @@ -0,0 +1,83 @@ +/************************************************************************************ + +PublicHeader: n/a +Filename : Util_Settings.h +Content : Persistent settings subsystem +Created : June 11, 2014 +Author : Chris Taylor + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_Settings_h +#define OVR_Settings_h + +#include "../Kernel/OVR_String.h" +#include "../Kernel/OVR_System.h" +#include "../Kernel/OVR_Observer.h" +#include "../OVR_JSON.h" +#include "Util_LongPollThread.h" + +namespace OVR { namespace Util { + + +//----------------------------------------------------------------------------- +// Settings + +class Settings : public NewOverrideBase, public SystemSingletonBase<Settings> +{ + OVR_DECLARE_SINGLETON(Settings); + + ObserverScope<LongPollThread::PollFunc> PollObserver; + void pollDirty(); + + // Helpers (call with lock held) + void loadFile(); + void updateFile(); + + // Synchronization for data access + Lock DataLock; + + // Full path to the JSON settings file + String FullFilePath; + + // Backed by JSON + Ptr<JSON> Root; + + // Dirty flag to capture multiple changes for long poll writes + bool Dirty; + +public: + void SetFileName(const String& fileName); + + void SetNumber(const char* key, double value); + void SetInt(const char* key, int value); + void SetBool(const char* key, bool value); + void SetString(const char* key, const char* value); + + double GetNumber(const char* key, double defaultValue = 0.); + int GetInt(const char* key, int defaultValue = 0); + bool GetBool(const char* key, bool defaultValue = false); + String GetString(const char* key, const char* defaultValue = ""); +}; + + +}} // namespace OVR::Util + +#endif // OVR_Settings_h |