oculus1
diff libovr/Src/Util/Util_LatencyTest.cpp @ 3:b069a5c27388
added a couple more stuff, fixed all the LibOVR line endings
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 15 Sep 2013 04:10:05 +0300 |
parents | e2f9e4603129 |
children |
line diff
1.1 --- a/libovr/Src/Util/Util_LatencyTest.cpp Sat Sep 14 17:51:03 2013 +0300 1.2 +++ b/libovr/Src/Util/Util_LatencyTest.cpp Sun Sep 15 04:10:05 2013 +0300 1.3 @@ -1,1 +1,571 @@ 1.4 -/************************************************************************************ 1.5 1.6 Filename : Util_LatencyTest.cpp 1.7 Content : Wraps the lower level LatencyTester interface and adds functionality. 1.8 Created : February 14, 2013 1.9 Authors : Lee Cooper 1.10 1.11 Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. 1.12 1.13 Use of this software is subject to the terms of the Oculus license 1.14 agreement provided at the time of installation or download, or which 1.15 otherwise accompanies this software in either electronic or hard copy form. 1.16 1.17 *************************************************************************************/ 1.18 1.19 #include "Util_LatencyTest.h" 1.20 1.21 #include "../Kernel/OVR_Log.h" 1.22 #include "../Kernel/OVR_Timer.h" 1.23 1.24 namespace OVR { namespace Util { 1.25 1.26 static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10; 1.27 static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10; 1.28 static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5; 1.29 static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5; 1.30 static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions. 1.31 static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4; 1.32 static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000; 1.33 static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000; 1.34 static const Color CALIBRATE_BLACK(0, 0, 0); 1.35 static const Color CALIBRATE_WHITE(255, 255, 255); 1.36 static const Color COLOR1(0, 0, 0); 1.37 static const Color COLOR2(255, 255, 255); 1.38 static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255); 1.39 static const float BIG_FLOAT = 1000000.0f; 1.40 static const float SMALL_FLOAT = -1000000.0f; 1.41 1.42 //------------------------------------------------------------------------------------- 1.43 // ***** LatencyTest 1.44 1.45 LatencyTest::LatencyTest(LatencyTestDevice* device) 1.46 : Handler(getThis()) 1.47 { 1.48 if (device != NULL) 1.49 { 1.50 SetDevice(device); 1.51 } 1.52 1.53 reset(); 1.54 1.55 srand(Timer::GetTicksMs()); 1.56 } 1.57 1.58 LatencyTest::~LatencyTest() 1.59 { 1.60 clearMeasurementResults(); 1.61 } 1.62 1.63 bool LatencyTest::SetDevice(LatencyTestDevice* device) 1.64 { 1.65 1.66 if (device != Device) 1.67 { 1.68 if (device != NULL) 1.69 { 1.70 if (device->GetMessageHandler() != NULL) 1.71 { 1.72 OVR_DEBUG_LOG( 1.73 ("LatencyTest::AttachToDevice failed - device %p already has handler", device)); 1.74 return false; 1.75 } 1.76 } 1.77 1.78 if (Device != NULL) 1.79 { 1.80 Device->SetMessageHandler(0); 1.81 } 1.82 Device = device; 1.83 1.84 if (Device != NULL) 1.85 { 1.86 Device->SetMessageHandler(&Handler); 1.87 1.88 // Set trigger threshold. 1.89 LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming. 1.90 Device->SetConfiguration(configuration, true); 1.91 1.92 // Set display to intial (3 dashes). 1.93 LatencyTestDisplay ltd(2, 0x40400040); 1.94 Device->SetDisplay(ltd); 1.95 } 1.96 } 1.97 1.98 return true; 1.99 } 1.100 1.101 UInt32 LatencyTest::getRandomComponent(UInt32 range) 1.102 { 1.103 UInt32 val = rand() % range; 1.104 return val; 1.105 } 1.106 1.107 void LatencyTest::BeginTest() 1.108 { 1.109 if (State == State_WaitingForButton) 1.110 { 1.111 // Set color to black and wait a while. 1.112 RenderColor = CALIBRATE_BLACK; 1.113 1.114 State = State_WaitingForSettlePreCalibrationColorBlack; 1.115 OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack.")); 1.116 1.117 setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); 1.118 } 1.119 } 1.120 1.121 void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage) 1.122 { 1.123 // For debugging. 1.124 /* if (msg.Type == Message_LatencyTestSamples) 1.125 { 1.126 MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg; 1.127 1.128 if (pSamples->Samples.GetSize() > 0) 1.129 { 1.130 // Just show the first one for now. 1.131 Color c = pSamples->Samples[0]; 1.132 OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B)); 1.133 } 1.134 return; 1.135 } 1.136 */ 1.137 1.138 if (latencyTestMessage == LatencyTest_Timer) 1.139 { 1.140 if (!Device) 1.141 { 1.142 reset(); 1.143 return; 1.144 } 1.145 1.146 if (State == State_WaitingForSettlePreCalibrationColorBlack) 1.147 { 1.148 // Send calibrate message to device and wait a while. 1.149 Device->SetCalibrate(CALIBRATE_BLACK); 1.150 1.151 State = State_WaitingForSettlePostCalibrationColorBlack; 1.152 OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack.")); 1.153 1.154 setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); 1.155 } 1.156 else if (State == State_WaitingForSettlePostCalibrationColorBlack) 1.157 { 1.158 // Change color to white and wait a while. 1.159 RenderColor = CALIBRATE_WHITE; 1.160 1.161 State = State_WaitingForSettlePreCalibrationColorWhite; 1.162 OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite.")); 1.163 1.164 setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); 1.165 } 1.166 else if (State == State_WaitingForSettlePreCalibrationColorWhite) 1.167 { 1.168 // Send calibrate message to device and wait a while. 1.169 Device->SetCalibrate(CALIBRATE_WHITE); 1.170 1.171 State = State_WaitingForSettlePostCalibrationColorWhite; 1.172 OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite.")); 1.173 1.174 setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); 1.175 } 1.176 else if (State == State_WaitingForSettlePostCalibrationColorWhite) 1.177 { 1.178 // Calibration is done. Switch to color 1 and wait for it to settle. 1.179 RenderColor = COLOR1; 1.180 1.181 State = State_WaitingForSettlePostMeasurement; 1.182 OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement.")); 1.183 1.184 UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); 1.185 setTimer(waitTime); 1.186 } 1.187 else if (State == State_WaitingForSettlePostMeasurement) 1.188 { 1.189 // Prepare for next measurement. 1.190 1.191 // Create a new result object. 1.192 MeasurementResult* pResult = new MeasurementResult(); 1.193 Results.PushBack(pResult); 1.194 1.195 State = State_WaitingToTakeMeasurement; 1.196 OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement.")); 1.197 } 1.198 else if (State == State_WaitingForTestStarted) 1.199 { 1.200 // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next. 1.201 getActiveResult()->TimedOutWaitingForTestStarted = true; 1.202 1.203 State = State_WaitingForSettlePostMeasurement; 1.204 OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'.")); 1.205 OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement.")); 1.206 1.207 UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); 1.208 setTimer(waitTime); 1.209 } 1.210 else if (State == State_WaitingForColorDetected) 1.211 { 1.212 // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next. 1.213 getActiveResult()->TimedOutWaitingForColorDetected = true; 1.214 1.215 State = State_WaitingForSettlePostMeasurement; 1.216 OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'.")); 1.217 OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); 1.218 1.219 UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); 1.220 setTimer(waitTime); 1.221 } 1.222 } 1.223 else if (latencyTestMessage == LatencyTest_ProcessInputs) 1.224 { 1.225 if (State == State_WaitingToTakeMeasurement) 1.226 { 1.227 if (!Device) 1.228 { 1.229 reset(); 1.230 return; 1.231 } 1.232 1.233 // Send 'StartTest' feature report with opposite target color. 1.234 if (RenderColor == COLOR1) 1.235 { 1.236 RenderColor = COLOR2; 1.237 } 1.238 else 1.239 { 1.240 RenderColor = COLOR1; 1.241 } 1.242 1.243 getActiveResult()->TargetColor = RenderColor; 1.244 1.245 // Record time so we can determine usb roundtrip time. 1.246 getActiveResult()->StartTestTicksMicroS = Timer::GetTicks(); 1.247 1.248 Device->SetStartTest(RenderColor); 1.249 1.250 State = State_WaitingForTestStarted; 1.251 OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted.")); 1.252 1.253 setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED); 1.254 1.255 LatencyTestDisplay ltd(2, 0x40090040); 1.256 Device->SetDisplay(ltd); 1.257 } 1.258 } 1.259 else if (msg.Type == Message_LatencyTestButton) 1.260 { 1.261 BeginTest(); 1.262 } 1.263 else if (msg.Type == Message_LatencyTestStarted) 1.264 { 1.265 if (State == State_WaitingForTestStarted) 1.266 { 1.267 clearTimer(); 1.268 1.269 // Record time so we can determine usb roundtrip time. 1.270 getActiveResult()->TestStartedTicksMicroS = Timer::GetTicks(); 1.271 1.272 State = State_WaitingForColorDetected; 1.273 OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected.")); 1.274 1.275 setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED); 1.276 } 1.277 } 1.278 else if (msg.Type == Message_LatencyTestColorDetected) 1.279 { 1.280 if (State == State_WaitingForColorDetected) 1.281 { 1.282 // Record time to detect color. 1.283 MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg; 1.284 UInt16 elapsedTime = pDetected->Elapsed; 1.285 OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime)); 1.286 1.287 getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime; 1.288 1.289 if (areResultsComplete()) 1.290 { 1.291 // We're done. 1.292 processResults(); 1.293 reset(); 1.294 } 1.295 else 1.296 { 1.297 // Run another measurement. 1.298 State = State_WaitingForSettlePostMeasurement; 1.299 OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); 1.300 1.301 UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); 1.302 setTimer(waitTime); 1.303 1.304 LatencyTestDisplay ltd(2, 0x40400040); 1.305 Device->SetDisplay(ltd); 1.306 } 1.307 } 1.308 } 1.309 else if (msg.Type == Message_DeviceRemoved) 1.310 { 1.311 reset(); 1.312 } 1.313 } 1.314 1.315 LatencyTest::MeasurementResult* LatencyTest::getActiveResult() 1.316 { 1.317 OVR_ASSERT(!Results.IsEmpty()); 1.318 return Results.GetLast(); 1.319 } 1.320 1.321 void LatencyTest::setTimer(UInt32 timeMilliS) 1.322 { 1.323 ActiveTimerMilliS = timeMilliS; 1.324 } 1.325 1.326 void LatencyTest::clearTimer() 1.327 { 1.328 ActiveTimerMilliS = 0; 1.329 } 1.330 1.331 void LatencyTest::reset() 1.332 { 1.333 clearMeasurementResults(); 1.334 State = State_WaitingForButton; 1.335 1.336 HaveOldTime = false; 1.337 ActiveTimerMilliS = 0; 1.338 } 1.339 1.340 void LatencyTest::clearMeasurementResults() 1.341 { 1.342 while(!Results.IsEmpty()) 1.343 { 1.344 MeasurementResult* pElem = Results.GetFirst(); 1.345 pElem->RemoveNode(); 1.346 delete pElem; 1.347 } 1.348 } 1.349 1.350 LatencyTest::LatencyTestHandler::~LatencyTestHandler() 1.351 { 1.352 RemoveHandlerFromDevices(); 1.353 } 1.354 1.355 void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg) 1.356 { 1.357 pLatencyTestUtil->handleMessage(msg); 1.358 } 1.359 1.360 void LatencyTest::ProcessInputs() 1.361 { 1.362 updateForTimeouts(); 1.363 handleMessage(Message(), LatencyTest_ProcessInputs); 1.364 } 1.365 1.366 bool LatencyTest::DisplayScreenColor(Color& colorToDisplay) 1.367 { 1.368 updateForTimeouts(); 1.369 1.370 if (State == State_WaitingForButton) 1.371 { 1.372 return false; 1.373 } 1.374 1.375 colorToDisplay = RenderColor; 1.376 return true; 1.377 } 1.378 1.379 const char* LatencyTest::GetResultsString() 1.380 { 1.381 if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr()) 1.382 { 1.383 ReturnedResultString = ResultsString; 1.384 return ReturnedResultString.ToCStr(); 1.385 } 1.386 1.387 return NULL; 1.388 } 1.389 1.390 bool LatencyTest::areResultsComplete() 1.391 { 1.392 UInt32 initialMeasurements = 0; 1.393 1.394 UInt32 measurements1to2 = 0; 1.395 UInt32 measurements2to1 = 0; 1.396 1.397 MeasurementResult* pCurr = Results.GetFirst(); 1.398 while(true) 1.399 { 1.400 // Process. 1.401 if (!pCurr->TimedOutWaitingForTestStarted && 1.402 !pCurr->TimedOutWaitingForColorDetected) 1.403 { 1.404 initialMeasurements++; 1.405 1.406 if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE) 1.407 { 1.408 if (pCurr->TargetColor == COLOR2) 1.409 { 1.410 measurements1to2++; 1.411 } 1.412 else 1.413 { 1.414 measurements2to1++; 1.415 } 1.416 } 1.417 } 1.418 1.419 if (Results.IsLast(pCurr)) 1.420 { 1.421 break; 1.422 } 1.423 pCurr = Results.GetNext(pCurr); 1.424 } 1.425 1.426 if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && 1.427 measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) 1.428 { 1.429 return true; 1.430 } 1.431 1.432 return false; 1.433 } 1.434 1.435 void LatencyTest::processResults() 1.436 { 1.437 1.438 UInt32 minTime1To2 = UINT_MAX; 1.439 UInt32 maxTime1To2 = 0; 1.440 float averageTime1To2 = 0.0f; 1.441 UInt32 minTime2To1 = UINT_MAX; 1.442 UInt32 maxTime2To1 = 0; 1.443 float averageTime2To1 = 0.0f; 1.444 1.445 float minUSBTripMilliS = BIG_FLOAT; 1.446 float maxUSBTripMilliS = SMALL_FLOAT; 1.447 float averageUSBTripMilliS = 0.0f; 1.448 UInt32 countUSBTripTime = 0; 1.449 1.450 UInt32 measurementsCount = 0; 1.451 UInt32 measurements1to2 = 0; 1.452 UInt32 measurements2to1 = 0; 1.453 1.454 MeasurementResult* pCurr = Results.GetFirst(); 1.455 UInt32 count = 0; 1.456 while(true) 1.457 { 1.458 count++; 1.459 1.460 if (!pCurr->TimedOutWaitingForTestStarted && 1.461 !pCurr->TimedOutWaitingForColorDetected) 1.462 { 1.463 measurementsCount++; 1.464 1.465 if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE) 1.466 { 1.467 if (pCurr->TargetColor == COLOR2) 1.468 { 1.469 measurements1to2++; 1.470 1.471 if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES) 1.472 { 1.473 UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; 1.474 1.475 minTime1To2 = Alg::Min(elapsed, minTime1To2); 1.476 maxTime1To2 = Alg::Max(elapsed, maxTime1To2); 1.477 1.478 averageTime1To2 += (float) elapsed; 1.479 } 1.480 } 1.481 else 1.482 { 1.483 measurements2to1++; 1.484 1.485 if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES) 1.486 { 1.487 UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; 1.488 1.489 minTime2To1 = Alg::Min(elapsed, minTime2To1); 1.490 maxTime2To1 = Alg::Max(elapsed, maxTime2To1); 1.491 1.492 averageTime2To1 += (float) elapsed; 1.493 } 1.494 } 1.495 1.496 float usbRountripElapsedMilliS = 0.001f * (float) (pCurr->TestStartedTicksMicroS - pCurr->StartTestTicksMicroS); 1.497 minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS); 1.498 maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS); 1.499 averageUSBTripMilliS += usbRountripElapsedMilliS; 1.500 countUSBTripTime++; 1.501 } 1.502 } 1.503 1.504 if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && 1.505 measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) 1.506 { 1.507 break; 1.508 } 1.509 1.510 if (Results.IsLast(pCurr)) 1.511 { 1.512 break; 1.513 } 1.514 pCurr = Results.GetNext(pCurr); 1.515 } 1.516 1.517 averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES; 1.518 averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES; 1.519 1.520 averageUSBTripMilliS /= countUSBTripTime; 1.521 1.522 float finalResult = 0.5f * (averageTime1To2 + averageTime2To1); 1.523 finalResult += averageUSBTripMilliS; 1.524 1.525 ResultsString.Clear(); 1.526 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]", 1.527 finalResult, 1.528 minTime1To2, averageTime1To2, maxTime1To2, 1.529 minTime2To1, averageTime2To1, maxTime2To1, 1.530 minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS, 1.531 DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount); 1.532 1.533 // Display result on latency tester display. 1.534 LatencyTestDisplay ltd(1, (int)finalResult); 1.535 Device->SetDisplay(ltd); 1.536 } 1.537 1.538 void LatencyTest::updateForTimeouts() 1.539 { 1.540 if (!HaveOldTime) 1.541 { 1.542 HaveOldTime = true; 1.543 OldTime = Timer::GetTicksMs(); 1.544 return; 1.545 } 1.546 1.547 UInt32 newTime = Timer::GetTicksMs(); 1.548 UInt32 elapsedMilliS = newTime - OldTime; 1.549 if (newTime < OldTime) 1.550 { 1.551 elapsedMilliS = OldTime - newTime; 1.552 elapsedMilliS = UINT_MAX - elapsedMilliS; 1.553 } 1.554 OldTime = newTime; 1.555 1.556 elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often. 1.557 1.558 1.559 if (ActiveTimerMilliS == 0) 1.560 { 1.561 return; 1.562 } 1.563 1.564 if (elapsedMilliS >= ActiveTimerMilliS) 1.565 { 1.566 ActiveTimerMilliS = 0; 1.567 handleMessage(Message(), LatencyTest_Timer); 1.568 return; 1.569 } 1.570 1.571 ActiveTimerMilliS -= elapsedMilliS; 1.572 } 1.573 1.574 }} // namespace OVR::Util 1.575 \ No newline at end of file 1.576 +/************************************************************************************ 1.577 + 1.578 +Filename : Util_LatencyTest.cpp 1.579 +Content : Wraps the lower level LatencyTester interface and adds functionality. 1.580 +Created : February 14, 2013 1.581 +Authors : Lee Cooper 1.582 + 1.583 +Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. 1.584 + 1.585 +Use of this software is subject to the terms of the Oculus license 1.586 +agreement provided at the time of installation or download, or which 1.587 +otherwise accompanies this software in either electronic or hard copy form. 1.588 + 1.589 +*************************************************************************************/ 1.590 + 1.591 +#include "Util_LatencyTest.h" 1.592 + 1.593 +#include "../Kernel/OVR_Log.h" 1.594 +#include "../Kernel/OVR_Timer.h" 1.595 + 1.596 +namespace OVR { namespace Util { 1.597 + 1.598 +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10; 1.599 +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10; 1.600 +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5; 1.601 +static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5; 1.602 +static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions. 1.603 +static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4; 1.604 +static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000; 1.605 +static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000; 1.606 +static const Color CALIBRATE_BLACK(0, 0, 0); 1.607 +static const Color CALIBRATE_WHITE(255, 255, 255); 1.608 +static const Color COLOR1(0, 0, 0); 1.609 +static const Color COLOR2(255, 255, 255); 1.610 +static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255); 1.611 +static const float BIG_FLOAT = 1000000.0f; 1.612 +static const float SMALL_FLOAT = -1000000.0f; 1.613 + 1.614 +//------------------------------------------------------------------------------------- 1.615 +// ***** LatencyTest 1.616 + 1.617 +LatencyTest::LatencyTest(LatencyTestDevice* device) 1.618 + : Handler(getThis()) 1.619 +{ 1.620 + if (device != NULL) 1.621 + { 1.622 + SetDevice(device); 1.623 + } 1.624 + 1.625 + reset(); 1.626 + 1.627 + srand(Timer::GetTicksMs()); 1.628 +} 1.629 + 1.630 +LatencyTest::~LatencyTest() 1.631 +{ 1.632 + clearMeasurementResults(); 1.633 +} 1.634 + 1.635 +bool LatencyTest::SetDevice(LatencyTestDevice* device) 1.636 +{ 1.637 + 1.638 + if (device != Device) 1.639 + { 1.640 + if (device != NULL) 1.641 + { 1.642 + if (device->GetMessageHandler() != NULL) 1.643 + { 1.644 + OVR_DEBUG_LOG( 1.645 + ("LatencyTest::AttachToDevice failed - device %p already has handler", device)); 1.646 + return false; 1.647 + } 1.648 + } 1.649 + 1.650 + if (Device != NULL) 1.651 + { 1.652 + Device->SetMessageHandler(0); 1.653 + } 1.654 + Device = device; 1.655 + 1.656 + if (Device != NULL) 1.657 + { 1.658 + Device->SetMessageHandler(&Handler); 1.659 + 1.660 + // Set trigger threshold. 1.661 + LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming. 1.662 + Device->SetConfiguration(configuration, true); 1.663 + 1.664 + // Set display to intial (3 dashes). 1.665 + LatencyTestDisplay ltd(2, 0x40400040); 1.666 + Device->SetDisplay(ltd); 1.667 + } 1.668 + } 1.669 + 1.670 + return true; 1.671 +} 1.672 + 1.673 +UInt32 LatencyTest::getRandomComponent(UInt32 range) 1.674 +{ 1.675 + UInt32 val = rand() % range; 1.676 + return val; 1.677 +} 1.678 + 1.679 +void LatencyTest::BeginTest() 1.680 +{ 1.681 + if (State == State_WaitingForButton) 1.682 + { 1.683 + // Set color to black and wait a while. 1.684 + RenderColor = CALIBRATE_BLACK; 1.685 + 1.686 + State = State_WaitingForSettlePreCalibrationColorBlack; 1.687 + OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack.")); 1.688 + 1.689 + setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); 1.690 + } 1.691 +} 1.692 + 1.693 +void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage) 1.694 +{ 1.695 + // For debugging. 1.696 +/* if (msg.Type == Message_LatencyTestSamples) 1.697 + { 1.698 + MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg; 1.699 + 1.700 + if (pSamples->Samples.GetSize() > 0) 1.701 + { 1.702 + // Just show the first one for now. 1.703 + Color c = pSamples->Samples[0]; 1.704 + OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B)); 1.705 + } 1.706 + return; 1.707 + } 1.708 +*/ 1.709 + 1.710 + if (latencyTestMessage == LatencyTest_Timer) 1.711 + { 1.712 + if (!Device) 1.713 + { 1.714 + reset(); 1.715 + return; 1.716 + } 1.717 + 1.718 + if (State == State_WaitingForSettlePreCalibrationColorBlack) 1.719 + { 1.720 + // Send calibrate message to device and wait a while. 1.721 + Device->SetCalibrate(CALIBRATE_BLACK); 1.722 + 1.723 + State = State_WaitingForSettlePostCalibrationColorBlack; 1.724 + OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack.")); 1.725 + 1.726 + setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); 1.727 + } 1.728 + else if (State == State_WaitingForSettlePostCalibrationColorBlack) 1.729 + { 1.730 + // Change color to white and wait a while. 1.731 + RenderColor = CALIBRATE_WHITE; 1.732 + 1.733 + State = State_WaitingForSettlePreCalibrationColorWhite; 1.734 + OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite.")); 1.735 + 1.736 + setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); 1.737 + } 1.738 + else if (State == State_WaitingForSettlePreCalibrationColorWhite) 1.739 + { 1.740 + // Send calibrate message to device and wait a while. 1.741 + Device->SetCalibrate(CALIBRATE_WHITE); 1.742 + 1.743 + State = State_WaitingForSettlePostCalibrationColorWhite; 1.744 + OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite.")); 1.745 + 1.746 + setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); 1.747 + } 1.748 + else if (State == State_WaitingForSettlePostCalibrationColorWhite) 1.749 + { 1.750 + // Calibration is done. Switch to color 1 and wait for it to settle. 1.751 + RenderColor = COLOR1; 1.752 + 1.753 + State = State_WaitingForSettlePostMeasurement; 1.754 + OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement.")); 1.755 + 1.756 + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); 1.757 + setTimer(waitTime); 1.758 + } 1.759 + else if (State == State_WaitingForSettlePostMeasurement) 1.760 + { 1.761 + // Prepare for next measurement. 1.762 + 1.763 + // Create a new result object. 1.764 + MeasurementResult* pResult = new MeasurementResult(); 1.765 + Results.PushBack(pResult); 1.766 + 1.767 + State = State_WaitingToTakeMeasurement; 1.768 + OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement.")); 1.769 + } 1.770 + else if (State == State_WaitingForTestStarted) 1.771 + { 1.772 + // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next. 1.773 + getActiveResult()->TimedOutWaitingForTestStarted = true; 1.774 + 1.775 + State = State_WaitingForSettlePostMeasurement; 1.776 + OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'.")); 1.777 + OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement.")); 1.778 + 1.779 + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); 1.780 + setTimer(waitTime); 1.781 + } 1.782 + else if (State == State_WaitingForColorDetected) 1.783 + { 1.784 + // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next. 1.785 + getActiveResult()->TimedOutWaitingForColorDetected = true; 1.786 + 1.787 + State = State_WaitingForSettlePostMeasurement; 1.788 + OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'.")); 1.789 + OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); 1.790 + 1.791 + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); 1.792 + setTimer(waitTime); 1.793 + } 1.794 + } 1.795 + else if (latencyTestMessage == LatencyTest_ProcessInputs) 1.796 + { 1.797 + if (State == State_WaitingToTakeMeasurement) 1.798 + { 1.799 + if (!Device) 1.800 + { 1.801 + reset(); 1.802 + return; 1.803 + } 1.804 + 1.805 + // Send 'StartTest' feature report with opposite target color. 1.806 + if (RenderColor == COLOR1) 1.807 + { 1.808 + RenderColor = COLOR2; 1.809 + } 1.810 + else 1.811 + { 1.812 + RenderColor = COLOR1; 1.813 + } 1.814 + 1.815 + getActiveResult()->TargetColor = RenderColor; 1.816 + 1.817 + // Record time so we can determine usb roundtrip time. 1.818 + getActiveResult()->StartTestTicksMicroS = Timer::GetTicks(); 1.819 + 1.820 + Device->SetStartTest(RenderColor); 1.821 + 1.822 + State = State_WaitingForTestStarted; 1.823 + OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted.")); 1.824 + 1.825 + setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED); 1.826 + 1.827 + LatencyTestDisplay ltd(2, 0x40090040); 1.828 + Device->SetDisplay(ltd); 1.829 + } 1.830 + } 1.831 + else if (msg.Type == Message_LatencyTestButton) 1.832 + { 1.833 + BeginTest(); 1.834 + } 1.835 + else if (msg.Type == Message_LatencyTestStarted) 1.836 + { 1.837 + if (State == State_WaitingForTestStarted) 1.838 + { 1.839 + clearTimer(); 1.840 + 1.841 + // Record time so we can determine usb roundtrip time. 1.842 + getActiveResult()->TestStartedTicksMicroS = Timer::GetTicks(); 1.843 + 1.844 + State = State_WaitingForColorDetected; 1.845 + OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected.")); 1.846 + 1.847 + setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED); 1.848 + } 1.849 + } 1.850 + else if (msg.Type == Message_LatencyTestColorDetected) 1.851 + { 1.852 + if (State == State_WaitingForColorDetected) 1.853 + { 1.854 + // Record time to detect color. 1.855 + MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg; 1.856 + UInt16 elapsedTime = pDetected->Elapsed; 1.857 + OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime)); 1.858 + 1.859 + getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime; 1.860 + 1.861 + if (areResultsComplete()) 1.862 + { 1.863 + // We're done. 1.864 + processResults(); 1.865 + reset(); 1.866 + } 1.867 + else 1.868 + { 1.869 + // Run another measurement. 1.870 + State = State_WaitingForSettlePostMeasurement; 1.871 + OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); 1.872 + 1.873 + UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); 1.874 + setTimer(waitTime); 1.875 + 1.876 + LatencyTestDisplay ltd(2, 0x40400040); 1.877 + Device->SetDisplay(ltd); 1.878 + } 1.879 + } 1.880 + } 1.881 + else if (msg.Type == Message_DeviceRemoved) 1.882 + { 1.883 + reset(); 1.884 + } 1.885 +} 1.886 + 1.887 +LatencyTest::MeasurementResult* LatencyTest::getActiveResult() 1.888 +{ 1.889 + OVR_ASSERT(!Results.IsEmpty()); 1.890 + return Results.GetLast(); 1.891 +} 1.892 + 1.893 +void LatencyTest::setTimer(UInt32 timeMilliS) 1.894 +{ 1.895 + ActiveTimerMilliS = timeMilliS; 1.896 +} 1.897 + 1.898 +void LatencyTest::clearTimer() 1.899 +{ 1.900 + ActiveTimerMilliS = 0; 1.901 +} 1.902 + 1.903 +void LatencyTest::reset() 1.904 +{ 1.905 + clearMeasurementResults(); 1.906 + State = State_WaitingForButton; 1.907 + 1.908 + HaveOldTime = false; 1.909 + ActiveTimerMilliS = 0; 1.910 +} 1.911 + 1.912 +void LatencyTest::clearMeasurementResults() 1.913 +{ 1.914 + while(!Results.IsEmpty()) 1.915 + { 1.916 + MeasurementResult* pElem = Results.GetFirst(); 1.917 + pElem->RemoveNode(); 1.918 + delete pElem; 1.919 + } 1.920 +} 1.921 + 1.922 +LatencyTest::LatencyTestHandler::~LatencyTestHandler() 1.923 +{ 1.924 + RemoveHandlerFromDevices(); 1.925 +} 1.926 + 1.927 +void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg) 1.928 +{ 1.929 + pLatencyTestUtil->handleMessage(msg); 1.930 +} 1.931 + 1.932 +void LatencyTest::ProcessInputs() 1.933 +{ 1.934 + updateForTimeouts(); 1.935 + handleMessage(Message(), LatencyTest_ProcessInputs); 1.936 +} 1.937 + 1.938 +bool LatencyTest::DisplayScreenColor(Color& colorToDisplay) 1.939 +{ 1.940 + updateForTimeouts(); 1.941 + 1.942 + if (State == State_WaitingForButton) 1.943 + { 1.944 + return false; 1.945 + } 1.946 + 1.947 + colorToDisplay = RenderColor; 1.948 + return true; 1.949 +} 1.950 + 1.951 +const char* LatencyTest::GetResultsString() 1.952 +{ 1.953 + if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr()) 1.954 + { 1.955 + ReturnedResultString = ResultsString; 1.956 + return ReturnedResultString.ToCStr(); 1.957 + } 1.958 + 1.959 + return NULL; 1.960 +} 1.961 + 1.962 +bool LatencyTest::areResultsComplete() 1.963 +{ 1.964 + UInt32 initialMeasurements = 0; 1.965 + 1.966 + UInt32 measurements1to2 = 0; 1.967 + UInt32 measurements2to1 = 0; 1.968 + 1.969 + MeasurementResult* pCurr = Results.GetFirst(); 1.970 + while(true) 1.971 + { 1.972 + // Process. 1.973 + if (!pCurr->TimedOutWaitingForTestStarted && 1.974 + !pCurr->TimedOutWaitingForColorDetected) 1.975 + { 1.976 + initialMeasurements++; 1.977 + 1.978 + if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE) 1.979 + { 1.980 + if (pCurr->TargetColor == COLOR2) 1.981 + { 1.982 + measurements1to2++; 1.983 + } 1.984 + else 1.985 + { 1.986 + measurements2to1++; 1.987 + } 1.988 + } 1.989 + } 1.990 + 1.991 + if (Results.IsLast(pCurr)) 1.992 + { 1.993 + break; 1.994 + } 1.995 + pCurr = Results.GetNext(pCurr); 1.996 + } 1.997 + 1.998 + if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && 1.999 + measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) 1.1000 + { 1.1001 + return true; 1.1002 + } 1.1003 + 1.1004 + return false; 1.1005 +} 1.1006 + 1.1007 +void LatencyTest::processResults() 1.1008 +{ 1.1009 + 1.1010 + UInt32 minTime1To2 = UINT_MAX; 1.1011 + UInt32 maxTime1To2 = 0; 1.1012 + float averageTime1To2 = 0.0f; 1.1013 + UInt32 minTime2To1 = UINT_MAX; 1.1014 + UInt32 maxTime2To1 = 0; 1.1015 + float averageTime2To1 = 0.0f; 1.1016 + 1.1017 + float minUSBTripMilliS = BIG_FLOAT; 1.1018 + float maxUSBTripMilliS = SMALL_FLOAT; 1.1019 + float averageUSBTripMilliS = 0.0f; 1.1020 + UInt32 countUSBTripTime = 0; 1.1021 + 1.1022 + UInt32 measurementsCount = 0; 1.1023 + UInt32 measurements1to2 = 0; 1.1024 + UInt32 measurements2to1 = 0; 1.1025 + 1.1026 + MeasurementResult* pCurr = Results.GetFirst(); 1.1027 + UInt32 count = 0; 1.1028 + while(true) 1.1029 + { 1.1030 + count++; 1.1031 + 1.1032 + if (!pCurr->TimedOutWaitingForTestStarted && 1.1033 + !pCurr->TimedOutWaitingForColorDetected) 1.1034 + { 1.1035 + measurementsCount++; 1.1036 + 1.1037 + if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE) 1.1038 + { 1.1039 + if (pCurr->TargetColor == COLOR2) 1.1040 + { 1.1041 + measurements1to2++; 1.1042 + 1.1043 + if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES) 1.1044 + { 1.1045 + UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; 1.1046 + 1.1047 + minTime1To2 = Alg::Min(elapsed, minTime1To2); 1.1048 + maxTime1To2 = Alg::Max(elapsed, maxTime1To2); 1.1049 + 1.1050 + averageTime1To2 += (float) elapsed; 1.1051 + } 1.1052 + } 1.1053 + else 1.1054 + { 1.1055 + measurements2to1++; 1.1056 + 1.1057 + if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES) 1.1058 + { 1.1059 + UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; 1.1060 + 1.1061 + minTime2To1 = Alg::Min(elapsed, minTime2To1); 1.1062 + maxTime2To1 = Alg::Max(elapsed, maxTime2To1); 1.1063 + 1.1064 + averageTime2To1 += (float) elapsed; 1.1065 + } 1.1066 + } 1.1067 + 1.1068 + float usbRountripElapsedMilliS = 0.001f * (float) (pCurr->TestStartedTicksMicroS - pCurr->StartTestTicksMicroS); 1.1069 + minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS); 1.1070 + maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS); 1.1071 + averageUSBTripMilliS += usbRountripElapsedMilliS; 1.1072 + countUSBTripTime++; 1.1073 + } 1.1074 + } 1.1075 + 1.1076 + if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && 1.1077 + measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) 1.1078 + { 1.1079 + break; 1.1080 + } 1.1081 + 1.1082 + if (Results.IsLast(pCurr)) 1.1083 + { 1.1084 + break; 1.1085 + } 1.1086 + pCurr = Results.GetNext(pCurr); 1.1087 + } 1.1088 + 1.1089 + averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES; 1.1090 + averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES; 1.1091 + 1.1092 + averageUSBTripMilliS /= countUSBTripTime; 1.1093 + 1.1094 + float finalResult = 0.5f * (averageTime1To2 + averageTime2To1); 1.1095 + finalResult += averageUSBTripMilliS; 1.1096 + 1.1097 + ResultsString.Clear(); 1.1098 + 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]", 1.1099 + finalResult, 1.1100 + minTime1To2, averageTime1To2, maxTime1To2, 1.1101 + minTime2To1, averageTime2To1, maxTime2To1, 1.1102 + minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS, 1.1103 + DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount); 1.1104 + 1.1105 + // Display result on latency tester display. 1.1106 + LatencyTestDisplay ltd(1, (int)finalResult); 1.1107 + Device->SetDisplay(ltd); 1.1108 +} 1.1109 + 1.1110 +void LatencyTest::updateForTimeouts() 1.1111 +{ 1.1112 + if (!HaveOldTime) 1.1113 + { 1.1114 + HaveOldTime = true; 1.1115 + OldTime = Timer::GetTicksMs(); 1.1116 + return; 1.1117 + } 1.1118 + 1.1119 + UInt32 newTime = Timer::GetTicksMs(); 1.1120 + UInt32 elapsedMilliS = newTime - OldTime; 1.1121 + if (newTime < OldTime) 1.1122 + { 1.1123 + elapsedMilliS = OldTime - newTime; 1.1124 + elapsedMilliS = UINT_MAX - elapsedMilliS; 1.1125 + } 1.1126 + OldTime = newTime; 1.1127 + 1.1128 + elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often. 1.1129 + 1.1130 + 1.1131 + if (ActiveTimerMilliS == 0) 1.1132 + { 1.1133 + return; 1.1134 + } 1.1135 + 1.1136 + if (elapsedMilliS >= ActiveTimerMilliS) 1.1137 + { 1.1138 + ActiveTimerMilliS = 0; 1.1139 + handleMessage(Message(), LatencyTest_Timer); 1.1140 + return; 1.1141 + } 1.1142 + 1.1143 + ActiveTimerMilliS -= elapsedMilliS; 1.1144 +} 1.1145 + 1.1146 +}} // namespace OVR::Util