Bug 1476380 - Remove the spin wait in Android gfxVRExternal PullState calls; r?kip draft
authorImanol Fernandez <imanol@mozilla.com>
Tue, 24 Jul 2018 16:27:58 +0200
changeset 822021 fb2b493eea4b927ce8ca44c33bbdd091c6bada93
parent 822020 59ab6acdee53af4b2b2ac27e2d7cb57c131e9530
push id117256
push userigorostizaga@mozilla.com
push dateTue, 24 Jul 2018 14:44:55 +0000
reviewerskip
bugs1476380
milestone63.0a1
Bug 1476380 - Remove the spin wait in Android gfxVRExternal PullState calls; r?kip MozReview-Commit-ID: 7pwMhE7emwY
gfx/vr/gfxVRExternal.cpp
gfx/vr/gfxVRExternal.h
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -228,29 +228,40 @@ VRDisplayExternal::SubmitFrame(const lay
 
   VRManager *vm = VRManager::Get();
   VRSystemManagerExternal* manager = vm->GetExternalManager();
   manager->PushState(&state, true);
   sPushIndex++;
 
   VRDisplayState displayState;
   memset(&displayState, 0, sizeof(VRDisplayState));
+#if defined(MOZ_WIDGET_ANDROID)
+  manager->PullState(&displayState, &mLastSensorState, mDisplayInfo.mControllerState, [&]() {
+    return (displayState.mLastSubmittedFrameId >= aFrameId) || displayState.mSuppressFrames || !displayState.mIsConnected;
+  });
+
+  if (displayState.mSuppressFrames || !displayState.mIsConnected) {
+    // External implementation wants to supress frames, service has shut down or hardware has been disconnected.
+    return false;
+  }
+#else
   while (displayState.mLastSubmittedFrameId < aFrameId) {
     if (manager->PullState(&displayState, &mLastSensorState, mDisplayInfo.mControllerState)) {
       if (displayState.mSuppressFrames || !displayState.mIsConnected) {
         // External implementation wants to supress frames, service has shut down or hardware has been disconnected.
         return false;
       }
     }
 #ifdef XP_WIN
     Sleep(0);
 #else
     sleep(0);
 #endif
   }
+#endif // defined(MOZ_WIDGET_ANDROID)
 
   return displayState.mLastSubmittedFrameSuccessful;
 }
 
 VRSystemManagerExternal::VRSystemManagerExternal(VRExternalShmem* aAPIShmem /* = nullptr*/)
  : mExternalShmem(aAPIShmem)
 #if !defined(MOZ_WIDGET_ANDROID)
  , mSameProcess(aAPIShmem != nullptr)
@@ -258,16 +269,17 @@ VRSystemManagerExternal::VRSystemManager
 {
 #if defined(XP_MACOSX)
   mShmemFD = 0;
 #elif defined(XP_WIN)
   mShmemFile = NULL;
 #elif defined(MOZ_WIDGET_ANDROID)
   mDoShutdown = false;
   mExternalStructFailed = false;
+  mEnumerationCompleted = false;
 #endif
 }
 
 VRSystemManagerExternal::~VRSystemManagerExternal()
 {
   CloseShmem();
 }
 
@@ -458,23 +470,30 @@ VRSystemManagerExternal::Enumerate()
   if (mDisplay == nullptr) {
     OpenShmem();
     if (mExternalShmem) {
       VRDisplayState displayState;
       memset(&displayState, 0, sizeof(VRDisplayState));
       // We must block until enumeration has completed in order
       // to signal that the WebVR promise should be resolved at the
       // right time.
+#if defined(MOZ_WIDGET_ANDROID)
+      PullState(&displayState, nullptr, nullptr, [&](){
+        return mEnumerationCompleted;
+      });
+#else
       while (!PullState(&displayState)) {
 #ifdef XP_WIN
         Sleep(0);
 #else
         sleep(0);
-#endif
+#endif // XP_WIN
       }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
       if (displayState.mIsConnected) {
         mDisplay = new VRDisplayExternal(displayState);
       }
     }
   }
 }
 
 bool
@@ -556,55 +575,84 @@ VRSystemManagerExternal::HandleInput()
 }
 
 void
 VRSystemManagerExternal::RemoveControllers()
 {
   // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
 }
 
+
+#if defined(MOZ_WIDGET_ANDROID)
+bool
+VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState,
+                                   VRHMDSensorState* aSensorState /* = nullptr */,
+                                   VRControllerState* aControllerState /* = nullptr */,
+                                   const std::function<bool()>& aWaitCondition /* = nullptr */)
+{
+  MOZ_ASSERT(mExternalShmem);
+  if (!mExternalShmem) {
+    return false;
+  }
+  bool done = false;
+  while(!done) {
+    if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
+      while (true) {
+        memcpy(aDisplayState, (void*)&(mExternalShmem->state.displayState), sizeof(VRDisplayState));
+        if (aSensorState) {
+          memcpy(aSensorState, (void*)&(mExternalShmem->state.sensorState), sizeof(VRHMDSensorState));
+        }
+        if (aControllerState) {
+          memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
+        }
+        mEnumerationCompleted = mExternalShmem->state.enumerationCompleted;
+        mDoShutdown = aDisplayState->shutdown;
+        if (!aWaitCondition || aWaitCondition()) {
+          done = true;
+          break;
+        }
+        // Block current thead using the condition variable until data changes
+        pthread_cond_wait((pthread_cond_t*)&mExternalShmem->systemCond, (pthread_mutex_t*)&mExternalShmem->systemMutex);
+      }
+      pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
+    } else if (!aWaitCondition) {
+      // pthread_mutex_lock failed and we are not waiting for a condition to exit from PullState call.
+      // return false to indicate that PullState call failed
+      return false;
+    }
+  }
+  return true;
+}
+#else 
 bool
 VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState,
                                    VRHMDSensorState* aSensorState /* = nullptr */,
                                    VRControllerState* aControllerState /* = nullptr */)
 {
   bool success = false;
   MOZ_ASSERT(mExternalShmem);
   if (mExternalShmem) {
-#if defined(MOZ_WIDGET_ANDROID)
-    if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
-      memcpy(aDisplayState, (void*)&(mExternalShmem->state.displayState), sizeof(VRDisplayState));
-      if (aSensorState) {
-        memcpy(aSensorState, (void*)&(mExternalShmem->state.sensorState), sizeof(VRHMDSensorState));
-      }
-      if (aControllerState) {
-        memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
-      }
-      success = mExternalShmem->state.enumerationCompleted;
-      pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
-      mDoShutdown = aDisplayState->shutdown;
-    }
-#else
     VRExternalShmem tmp;
     memcpy(&tmp, (void *)mExternalShmem, sizeof(VRExternalShmem));
     if (tmp.generationA == tmp.generationB && tmp.generationA != 0 && tmp.generationA != -1 && tmp.state.enumerationCompleted) {
       memcpy(aDisplayState, &tmp.state.displayState, sizeof(VRDisplayState));
       if (aSensorState) {
         memcpy(aSensorState, &tmp.state.sensorState, sizeof(VRHMDSensorState));
       }
       if (aControllerState) {
         memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
       }
       success = true;
     }
-#endif // defined(MOZ_WIDGET_ANDROID)
   }
 
   return success;
 }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
 
 void
 VRSystemManagerExternal::PushState(VRBrowserState* aBrowserState, bool aNotifyCond)
 {
   MOZ_ASSERT(aBrowserState);
   MOZ_ASSERT(mExternalShmem);
   if (mExternalShmem) {
 #if defined(MOZ_WIDGET_ANDROID)
--- a/gfx/vr/gfxVRExternal.h
+++ b/gfx/vr/gfxVRExternal.h
@@ -81,35 +81,43 @@ public:
   virtual void ScanForControllers() override;
   virtual void RemoveControllers() override;
   virtual void VibrateHaptic(uint32_t aControllerIdx,
                              uint32_t aHapticIndex,
                              double aIntensity,
                              double aDuration,
                              const VRManagerPromise& aPromise) override;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
+#if defined(MOZ_WIDGET_ANDROID)
+  bool PullState(VRDisplayState* aDisplayState,
+                 VRHMDSensorState* aSensorState = nullptr,
+                 VRControllerState* aControllerState = nullptr,
+                 const std::function<bool()>& aWaitCondition = nullptr);
+#else
   bool PullState(VRDisplayState* aDisplayState,
                  VRHMDSensorState* aSensorState = nullptr,
                  VRControllerState* aControllerState = nullptr);
+#endif
   void PushState(VRBrowserState* aBrowserState, const bool aNotifyCond = false);
 
 protected:
   explicit VRSystemManagerExternal(VRExternalShmem* aAPIShmem = nullptr);
   virtual ~VRSystemManagerExternal();
 
 private:
   // there can only be one
   RefPtr<impl::VRDisplayExternal> mDisplay;
 #if defined(XP_MACOSX)
   int mShmemFD;
 #elif defined(XP_WIN)
   HANDLE mShmemFile;
 #elif defined(MOZ_WIDGET_ANDROID)
   bool mDoShutdown;
   bool mExternalStructFailed;
+  bool mEnumerationCompleted;
 #endif
 
   volatile VRExternalShmem* mExternalShmem;
 #if !defined(MOZ_WIDGET_ANDROID)
   bool mSameProcess;
 #endif
 
   void OpenShmem();