tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

VRPuppetCommandBuffer.cpp (17869B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "VRPuppetCommandBuffer.h"
      8 #include "prthread.h"
      9 #include "mozilla/ClearOnShutdown.h"
     10 
     11 namespace mozilla::gfx {
     12 
     13 static StaticRefPtr<VRPuppetCommandBuffer> sVRPuppetCommandBufferSingleton;
     14 
     15 /* static */
     16 VRPuppetCommandBuffer& VRPuppetCommandBuffer::Get() {
     17  if (sVRPuppetCommandBufferSingleton == nullptr) {
     18    sVRPuppetCommandBufferSingleton = new VRPuppetCommandBuffer();
     19    ClearOnShutdown(&sVRPuppetCommandBufferSingleton);
     20  }
     21  return *sVRPuppetCommandBufferSingleton;
     22 }
     23 
     24 /* static */
     25 bool VRPuppetCommandBuffer::IsCreated() {
     26  return sVRPuppetCommandBufferSingleton != nullptr;
     27 }
     28 
     29 VRPuppetCommandBuffer::VRPuppetCommandBuffer()
     30    : mMutex("VRPuppetCommandBuffer::mMutex") {
     31  MOZ_COUNT_CTOR(VRPuppetCommandBuffer);
     32  MOZ_ASSERT(sVRPuppetCommandBufferSingleton == nullptr);
     33  Reset();
     34 }
     35 
     36 VRPuppetCommandBuffer::~VRPuppetCommandBuffer() {
     37  MOZ_COUNT_DTOR(VRPuppetCommandBuffer);
     38 }
     39 
     40 void VRPuppetCommandBuffer::Submit(const nsTArray<uint64_t>& aBuffer) {
     41  MutexAutoLock lock(mMutex);
     42  mBuffer.AppendElements(aBuffer);
     43  mEnded = false;
     44  mEndedWithTimeout = false;
     45 }
     46 
     47 bool VRPuppetCommandBuffer::HasEnded() {
     48  MutexAutoLock lock(mMutex);
     49  return mEnded;
     50 }
     51 
     52 void VRPuppetCommandBuffer::Reset() {
     53  MutexAutoLock lock(mMutex);
     54  memset(&mPendingState, 0, sizeof(VRSystemState));
     55  memset(&mCommittedState, 0, sizeof(VRSystemState));
     56  for (int iControllerIdx = 0; iControllerIdx < kVRControllerMaxCount;
     57       iControllerIdx++) {
     58    for (int iHaptic = 0; iHaptic < kNumPuppetHaptics; iHaptic++) {
     59      mHapticPulseRemaining[iControllerIdx][iHaptic] = 0.0f;
     60      mHapticPulseIntensity[iControllerIdx][iHaptic] = 0.0f;
     61    }
     62  }
     63  mDataOffset = 0;
     64  mPresentationRequested = false;
     65  mFrameSubmitted = false;
     66  mFrameAccepted = false;
     67  mTimeoutDuration = 10.0f;
     68  mWaitRemaining = 0.0f;
     69  mBlockedTime = 0.0f;
     70  mTimerElapsed = 0.0f;
     71  mEnded = true;
     72  mEndedWithTimeout = false;
     73  mLastRunTimestamp = TimeStamp();
     74  mTimerSamples.Clear();
     75  mBuffer.Clear();
     76 }
     77 
     78 bool VRPuppetCommandBuffer::RunCommand(uint64_t aCommand, double aDeltaTime) {
     79  /**
     80   * Run a single command.  If the command is blocking on a state change and
     81   * can't be executed, return false.
     82   *
     83   * VRPuppetCommandBuffer::RunCommand is only called by
     84   *VRPuppetCommandBuffer::Run(), which is already holding the mutex.
     85   *
     86   * Note that VRPuppetCommandBuffer::RunCommand may potentially be called >1000
     87   *times per frame. It might not hurt to add an assert here, but we should
     88   *avoid adding code which may potentially malloc (eg string handling) even for
     89   *debug builds here.  This function will need to be reasonably fast, even in
     90   *debug builds which will be using it during tests.
     91   **/
     92  switch ((VRPuppet_Command)(aCommand & 0xff00000000000000)) {
     93    case VRPuppet_Command::VRPuppet_End:
     94      CompleteTest(false);
     95      break;
     96    case VRPuppet_Command::VRPuppet_ClearAll:
     97      memset(&mPendingState, 0, sizeof(VRSystemState));
     98      memset(&mCommittedState, 0, sizeof(VRSystemState));
     99      mPresentationRequested = false;
    100      mFrameSubmitted = false;
    101      mFrameAccepted = false;
    102      break;
    103    case VRPuppet_Command::VRPuppet_ClearController: {
    104      uint8_t controllerIdx = aCommand & 0x00000000000000ff;
    105      if (controllerIdx < kVRControllerMaxCount) {
    106        mPendingState.controllerState[controllerIdx].Clear();
    107      }
    108    } break;
    109    case VRPuppet_Command::VRPuppet_Timeout:
    110      mTimeoutDuration = (double)(aCommand & 0x00000000ffffffff) / 1000.0f;
    111      break;
    112    case VRPuppet_Command::VRPuppet_Wait:
    113      if (mWaitRemaining == 0.0f) {
    114        mWaitRemaining = (double)(aCommand & 0x00000000ffffffff) / 1000.0f;
    115        // Wait timer started, block
    116        return false;
    117      }
    118      mWaitRemaining -= aDeltaTime;
    119      if (mWaitRemaining > 0.0f) {
    120        // Wait timer still running, block
    121        return false;
    122      }
    123      // Wait timer has elapsed, unblock
    124      mWaitRemaining = 0.0f;
    125      break;
    126    case VRPuppet_Command::VRPuppet_WaitSubmit:
    127      if (!mFrameSubmitted) {
    128        return false;
    129      }
    130      break;
    131    case VRPuppet_Command::VRPuppet_CaptureFrame:
    132      // TODO - Capture the frame and record the output (Bug 1555180)
    133      break;
    134    case VRPuppet_Command::VRPuppet_AcknowledgeFrame:
    135      mFrameSubmitted = false;
    136      mFrameAccepted = true;
    137      break;
    138    case VRPuppet_Command::VRPuppet_RejectFrame:
    139      mFrameSubmitted = false;
    140      mFrameAccepted = false;
    141      break;
    142    case VRPuppet_Command::VRPuppet_WaitPresentationStart:
    143      if (!mPresentationRequested) {
    144        return false;
    145      }
    146      break;
    147    case VRPuppet_Command::VRPuppet_WaitPresentationEnd:
    148      if (mPresentationRequested) {
    149        return false;
    150      }
    151      break;
    152    case VRPuppet_Command::VRPuppet_WaitHapticIntensity: {
    153      // 0x0800cchhvvvvvvvv - VRPuppet_WaitHapticIntensity(c, h, v)
    154      uint8_t iControllerIdx = (aCommand & 0x0000ff0000000000) >> 40;
    155      if (iControllerIdx >= kVRControllerMaxCount) {
    156        // Puppet test is broken, ensure it fails
    157        return false;
    158      }
    159      uint8_t iHapticIdx = (aCommand & 0x000000ff00000000) >> 32;
    160      if (iHapticIdx >= kNumPuppetHaptics) {
    161        // Puppet test is broken, ensure it fails
    162        return false;
    163      }
    164      uint32_t iHapticIntensity =
    165          aCommand & 0x00000000ffffffff;  // interpreted as 16.16 fixed point
    166 
    167      SimulateHaptics(aDeltaTime);
    168      uint64_t iCurrentIntensity =
    169          round(mHapticPulseIntensity[iControllerIdx][iHapticIdx] *
    170                (1 << 16));  // convert to 16.16 fixed point
    171      if (iCurrentIntensity > 0xffffffff) {
    172        iCurrentIntensity = 0xffffffff;
    173      }
    174      if (iCurrentIntensity != iHapticIntensity) {
    175        return false;
    176      }
    177    } break;
    178 
    179    case VRPuppet_Command::VRPuppet_StartTimer:
    180      mTimerElapsed = 0.0f;
    181      break;
    182    case VRPuppet_Command::VRPuppet_StopTimer:
    183      mTimerSamples.AppendElements(mTimerElapsed);
    184      // TODO - Return the timer samples to Javascript once the command buffer
    185      // is complete (Bug 1555182)
    186      break;
    187 
    188    case VRPuppet_Command::VRPuppet_UpdateDisplay:
    189      mDataOffset = (uint8_t*)&mPendingState.displayState -
    190                    (uint8_t*)&mPendingState + (aCommand & 0x00000000ffffffff);
    191      break;
    192    case VRPuppet_Command::VRPuppet_UpdateSensor:
    193      mDataOffset = (uint8_t*)&mPendingState.sensorState -
    194                    (uint8_t*)&mPendingState + (aCommand & 0x00000000ffffffff);
    195      break;
    196    case VRPuppet_Command::VRPuppet_UpdateControllers:
    197      mDataOffset = (uint8_t*)&mPendingState.controllerState -
    198                    (uint8_t*)&mPendingState + (aCommand & 0x00000000ffffffff);
    199      break;
    200    case VRPuppet_Command::VRPuppet_Commit:
    201      memcpy(&mCommittedState, &mPendingState, sizeof(VRSystemState));
    202      break;
    203 
    204    case VRPuppet_Command::VRPuppet_Data7:
    205      WriteData((aCommand & 0x00ff000000000000) >> 48);
    206      [[fallthrough]];
    207      // Purposefully, no break
    208    case VRPuppet_Command::VRPuppet_Data6:
    209      WriteData((aCommand & 0x0000ff0000000000) >> 40);
    210      [[fallthrough]];
    211      // Purposefully, no break
    212    case VRPuppet_Command::VRPuppet_Data5:
    213      WriteData((aCommand & 0x000000ff00000000) >> 32);
    214      [[fallthrough]];
    215      // Purposefully, no break
    216    case VRPuppet_Command::VRPuppet_Data4:
    217      WriteData((aCommand & 0x00000000ff000000) >> 24);
    218      [[fallthrough]];
    219      // Purposefully, no break
    220    case VRPuppet_Command::VRPuppet_Data3:
    221      WriteData((aCommand & 0x0000000000ff0000) >> 16);
    222      [[fallthrough]];
    223      // Purposefully, no break
    224    case VRPuppet_Command::VRPuppet_Data2:
    225      WriteData((aCommand & 0x000000000000ff00) >> 8);
    226      [[fallthrough]];
    227      // Purposefully, no break
    228    case VRPuppet_Command::VRPuppet_Data1:
    229      WriteData(aCommand & 0x00000000000000ff);
    230      break;
    231  }
    232  return true;
    233 }
    234 
    235 void VRPuppetCommandBuffer::WriteData(uint8_t aData) {
    236  if (mDataOffset && mDataOffset < sizeof(VRSystemState)) {
    237    ((uint8_t*)&mPendingState)[mDataOffset++] = aData;
    238  }
    239 }
    240 
    241 void VRPuppetCommandBuffer::Run() {
    242  MutexAutoLock lock(mMutex);
    243  TimeStamp now = TimeStamp::Now();
    244  double deltaTime = 0.0f;
    245  if (!mLastRunTimestamp.IsNull()) {
    246    deltaTime = (now - mLastRunTimestamp).ToSeconds();
    247  }
    248  mLastRunTimestamp = now;
    249  mTimerElapsed += deltaTime;
    250  size_t transactionLength = 0;
    251  while (transactionLength < mBuffer.Length() && !mEnded) {
    252    if (RunCommand(mBuffer[transactionLength], deltaTime)) {
    253      mBlockedTime = 0.0f;
    254      transactionLength++;
    255    } else {
    256      mBlockedTime += deltaTime;
    257      if (mBlockedTime > mTimeoutDuration) {
    258        CompleteTest(true);
    259      }
    260      // If a command is blocked, we don't increment transactionLength,
    261      // allowing the command to be retried on the next cycle
    262      break;
    263    }
    264  }
    265  mBuffer.RemoveElementsAt(0, transactionLength);
    266 }
    267 
    268 void VRPuppetCommandBuffer::Run(VRSystemState& aState) {
    269  Run();
    270  // We don't want to stomp over some members
    271  bool bEnumerationCompleted = aState.enumerationCompleted;
    272  bool bShutdown = aState.displayState.shutdown;
    273  uint32_t minRestartInterval = aState.displayState.minRestartInterval;
    274 
    275  // Overwrite it all
    276  memcpy(&aState, &mCommittedState, sizeof(VRSystemState));
    277 
    278  // Restore the members
    279  aState.enumerationCompleted = bEnumerationCompleted;
    280  aState.displayState.shutdown = bShutdown;
    281  aState.displayState.minRestartInterval = minRestartInterval;
    282 }
    283 
    284 void VRPuppetCommandBuffer::StartPresentation() {
    285  mPresentationRequested = true;
    286  Run();
    287 }
    288 
    289 void VRPuppetCommandBuffer::StopPresentation() {
    290  mPresentationRequested = false;
    291  Run();
    292 }
    293 
    294 bool VRPuppetCommandBuffer::SubmitFrame() {
    295  // Emulate blocking behavior of various XR API's as
    296  // described by puppet script
    297  mFrameSubmitted = true;
    298  mFrameAccepted = false;
    299  while (true) {
    300    Run();
    301    if (!mFrameSubmitted || mEnded) {
    302      break;
    303    }
    304    PR_Sleep(PR_INTERVAL_NO_WAIT);  // Yield
    305  }
    306 
    307  return mFrameAccepted;
    308 }
    309 
    310 void VRPuppetCommandBuffer::VibrateHaptic(uint32_t aControllerIdx,
    311                                          uint32_t aHapticIndex,
    312                                          float aIntensity, float aDuration) {
    313  if (aHapticIndex >= kNumPuppetHaptics ||
    314      aControllerIdx >= kVRControllerMaxCount) {
    315    return;
    316  }
    317 
    318  // We must Run() before and after updating haptic state to avoid script
    319  // deadlocks
    320  // The deadlocks would be caused by scripts that include two
    321  // VRPuppet_WaitHapticIntensity commands.  If
    322  // VRPuppetCommandBuffer::VibrateHaptic() is called twice without advancing
    323  // through the command buffer with VRPuppetCommandBuffer::Run() in between,
    324  // the first VRPuppet_WaitHapticInensity may not see the transient value that
    325  // it is waiting for, thus blocking forever and deadlocking the script.
    326  Run();
    327  mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration;
    328  mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity;
    329  Run();
    330 }
    331 
    332 void VRPuppetCommandBuffer::StopVibrateHaptic(uint32_t aControllerIdx) {
    333  if (aControllerIdx >= kVRControllerMaxCount) {
    334    return;
    335  }
    336  // We must Run() before and after updating haptic state to avoid script
    337  // deadlocks
    338  Run();
    339  for (int iHaptic = 0; iHaptic < kNumPuppetHaptics; iHaptic++) {
    340    mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f;
    341    mHapticPulseIntensity[aControllerIdx][iHaptic] = 0.0f;
    342  }
    343  Run();
    344 }
    345 
    346 void VRPuppetCommandBuffer::StopAllHaptics() {
    347  // We must Run() before and after updating haptic state to avoid script
    348  // deadlocks
    349  Run();
    350  for (int iControllerIdx = 0; iControllerIdx < kVRControllerMaxCount;
    351       iControllerIdx++) {
    352    for (int iHaptic = 0; iHaptic < kNumPuppetHaptics; iHaptic++) {
    353      mHapticPulseRemaining[iControllerIdx][iHaptic] = 0.0f;
    354      mHapticPulseIntensity[iControllerIdx][iHaptic] = 0.0f;
    355    }
    356  }
    357  Run();
    358 }
    359 
    360 void VRPuppetCommandBuffer::SimulateHaptics(double aDeltaTime) {
    361  for (int iControllerIdx = 0; iControllerIdx < kVRControllerMaxCount;
    362       iControllerIdx++) {
    363    for (int iHaptic = 0; iHaptic < kNumPuppetHaptics; iHaptic++) {
    364      if (mHapticPulseIntensity[iControllerIdx][iHaptic] > 0.0f) {
    365        mHapticPulseRemaining[iControllerIdx][iHaptic] -= aDeltaTime;
    366        if (mHapticPulseRemaining[iControllerIdx][iHaptic] <= 0.0f) {
    367          mHapticPulseRemaining[iControllerIdx][iHaptic] = 0.0f;
    368          mHapticPulseIntensity[iControllerIdx][iHaptic] = 0.0f;
    369        }
    370      }
    371    }
    372  }
    373 }
    374 
    375 void VRPuppetCommandBuffer::CompleteTest(bool aTimedOut) {
    376  mEndedWithTimeout = aTimedOut;
    377  mEnded = true;
    378 }
    379 
    380 /**
    381 *  Generates a sequence of VRPuppet_Data# commands, as described
    382 *  in VRPuppetCommandBuffer.h, to encode the changes to be made to
    383 *  a "destination" structure to match the "source" structure.
    384 *  As the commands are encoded, the destination structure is updated
    385 *  to match the source.
    386 *
    387 * @param aBuffer
    388 *     The buffer in which the commands will be appended.
    389 * @param aSrcStart
    390 *     Byte pointer to the start of the structure that
    391 *     will be copied from.
    392 * @param aDstStart
    393 *     Byte pointer to the start of the structure that
    394 *     will be copied to.
    395 * @param aLength
    396 *     Length of the structure that will be copied.
    397 * @param aUpdateCommand
    398 *     VRPuppet_... command indicating which structure is being
    399 *     copied:
    400 *     VRPuppet_Command::VRPuppet_UpdateDisplay:
    401 *         A single VRDisplayState struct
    402 *     VRPuppet_Command::VRPuppet_UpdateSensor:
    403 *         A single VRHMDSensorState struct
    404 *     VRPuppet_Command::VRPuppet_UpdateControllers:
    405 *         An array of VRControllerState structs with a
    406 *         count of kVRControllerMaxCount
    407 */
    408 void VRPuppetCommandBuffer::EncodeStruct(nsTArray<uint64_t>& aBuffer,
    409                                         uint8_t* aSrcStart, uint8_t* aDstStart,
    410                                         size_t aLength,
    411                                         VRPuppet_Command aUpdateCommand) {
    412  // Naive implementation, but will not be executed in realtime, so will not
    413  // affect test timer results. Could be improved to avoid unaligned reads and
    414  // to use SSE.
    415 
    416  // Pointer to source byte being compared+copied
    417  uint8_t* src = aSrcStart;
    418 
    419  // Pointer to destination byte being compared+copied
    420  uint8_t* dst = aDstStart;
    421 
    422  // Number of bytes packed into bufData
    423  uint8_t bufLen = 0;
    424 
    425  // 64-bits to be interpreted as up to 7 separate bytes
    426  // This will form the lower 56 bits of the command
    427  uint64_t bufData = 0;
    428 
    429  // purgebuffer takes the bytes stored in bufData and generates a VRPuppet
    430  // command representing those bytes as "VRPuppet Data".
    431  // VRPUppet_Data1 encodes 1 byte
    432  // VRPuppet_Data2 encodes 2 bytes
    433  // and so on, until..
    434  // VRPuppet_Data7 encodes 7 bytes
    435  // This command is appended to aBuffer, then bufLen and bufData are reset
    436  auto purgeBuffer = [&]() {
    437    // Only emit a command if there are data bytes in bufData
    438    if (bufLen > 0) {
    439      MOZ_ASSERT(bufLen <= 7);
    440      uint64_t command = (uint64_t)VRPuppet_Command::VRPuppet_Data1;
    441      command += ((uint64_t)VRPuppet_Command::VRPuppet_Data2 -
    442                  (uint64_t)VRPuppet_Command::VRPuppet_Data1) *
    443                 (bufLen - 1);
    444      command |= bufData;
    445      aBuffer.AppendElement(command);
    446      bufLen = 0;
    447      bufData = 0;
    448    }
    449  };
    450 
    451  // Loop through the bytes of the structs.
    452  // While copying the struct at aSrcStart to aDstStart,
    453  // the differences are encoded as VRPuppet commands and
    454  // appended to aBuffer.
    455  for (size_t i = 0; i < aLength; i++) {
    456    if (*src != *dst) {
    457      // This byte is different
    458 
    459      // Copy the byte to the destination
    460      *dst = *src;
    461 
    462      if (bufLen == 0) {
    463        // This is the start of a new span of changed bytes
    464 
    465        // Output a command to specify the offset of the
    466        // span.
    467        aBuffer.AppendElement((uint64_t)aUpdateCommand + i);
    468 
    469        // Store this first byte in bufData.
    470        // We will batch up to 7 bytes in one VRPuppet_DataXX
    471        // command, so we won't emit it yet.
    472        bufLen = 1;
    473        bufData = *src;
    474      } else if (bufLen <= 6) {
    475        // This is the continuation of a span of changed bytes.
    476        // There is room to add more bytes to bufData.
    477 
    478        // Store the next byte in bufData.
    479        // We will batch up to 7 bytes in one VRPuppet_DataXX
    480        // command, so we won't emit it yet.
    481        bufData = (bufData << 8) | *src;
    482        bufLen++;
    483      } else {
    484        MOZ_ASSERT(bufLen == 7);
    485        // This is the continuation of a span of changed bytes.
    486        // There are already 7 bytes in bufData, so we must emit
    487        // the VRPuppet_Data7 command for the prior bytes before
    488        // starting a new command.
    489        aBuffer.AppendElement((uint64_t)VRPuppet_Command::VRPuppet_Data7 +
    490                              bufData);
    491 
    492        // Store this byte to be included in the next VRPuppet_DataXX
    493        // command.
    494        bufLen = 1;
    495        bufData = *src;
    496      }
    497    } else {
    498      // This byte is the same.
    499      // If there are bytes in bufData, the span has now ended and we must
    500      // emit a VRPuppet_DataXX command for the accumulated bytes.
    501      // purgeBuffer will not emit any commands if there are no bytes
    502      // accumulated.
    503      purgeBuffer();
    504    }
    505    // Advance to the next source and destination byte.
    506    ++src;
    507    ++dst;
    508  }
    509  // In the event that the very last byte of the structs differ, we must
    510  // ensure that the accumulated bytes are emitted as a VRPuppet_DataXX
    511  // command.
    512  purgeBuffer();
    513 }
    514 
    515 }  // namespace mozilla::gfx