diff --git a/gfx/vr/VRManager.cpp b/gfx/vr/VRManager.cpp index 67bb9d9b2c68..3c3734502a4a 100644 --- a/gfx/vr/VRManager.cpp +++ b/gfx/vr/VRManager.cpp @@ -7,6 +7,7 @@ #include "VRManager.h" #include "VRManagerParent.h" +#include "VRShMem.h" #include "VRThread.h" #include "gfxVR.h" #include "mozilla/ClearOnShutdown.h" @@ -19,7 +20,6 @@ #include "mozilla/Unused.h" #include "gfxVR.h" -#include "gfxVRMutex.h" #include #include "ipc/VRLayerParent.h" @@ -33,14 +33,9 @@ # include # include "gfxWindowsPlatform.h" # include "mozilla/gfx/DeviceManagerDx.h" -static const char* kShmemName = "moz.gecko.vr_ext.0.0.1"; #elif defined(XP_MACOSX) # include "mozilla/gfx/MacIOSurface.h" -# include -# include /* For mode constants */ -# include /* For O_* constants */ # include -static const char* kShmemName = "/moz.gecko.vr_ext.0.0.1"; #elif defined(MOZ_WIDGET_ANDROID) # include # include @@ -53,18 +48,6 @@ using namespace mozilla::gfx; using namespace mozilla::layers; using namespace mozilla::gl; -#if !defined(MOZ_WIDGET_ANDROID) -namespace { -void YieldThread() { -# if defined(XP_WIN) - ::Sleep(0); -# else - ::sleep(0); -# endif -} -} // anonymous namespace -#endif // !defined(MOZ_WIDGET_ANDROID) - namespace mozilla { namespace gfx { @@ -127,19 +110,13 @@ VRManager::VRManager() mVRDisplaysRequestedNonFocus(false), mVRControllersRequested(false), mFrameStarted(false), - mExternalShmem(nullptr), mTaskInterval(0), mCurrentSubmitTaskMonitor("CurrentSubmitTaskMonitor"), mCurrentSubmitTask(nullptr), mLastSubmittedFrameId(0), mLastStartedFrame(0), mEnumerationCompleted(false), -#if defined(XP_MACOSX) - mShmemFD(0), -#elif defined(XP_WIN) - mShmemFile(NULL), - mMutex(NULL), -#endif + mShmem(nullptr), mHapticPulseRemaining{}, mDisplayInfo{}, mLastUpdateDisplayInfo{}, @@ -160,160 +137,40 @@ VRManager::VRManager() // on ShutdownPhase::ShutdownFinal, potentially before VRManager. // We hold a reference to VRServiceHost to ensure it stays // alive until we have shut down. +#else + // For Android, there is no VRProcess available and no VR service is + // created, so default to false. + mVRProcessEnabled = false; #endif // !defined(MOZ_WIDGET_ANDROID) } void VRManager::OpenShmem() { - if (mExternalShmem) { - mExternalShmem->Clear(); - return; - } -#if defined(XP_WIN) - if (mMutex == NULL) { - mMutex = CreateMutex(NULL, // default security descriptor - false, // mutex not owned - TEXT("mozilla::vr::ShmemMutex")); // object name - if (mMutex == NULL) { - nsAutoCString msg; - msg.AppendPrintf("VRManager CreateMutex error \"%lu\".", GetLastError()); - NS_WARNING(msg.get()); - MOZ_ASSERT(false); - return; - } - // At xpcshell extension tests, it creates multiple VRManager - // instances in plug-contrainer.exe. It causes GetLastError() return - // `ERROR_ALREADY_EXISTS`. However, even though `ERROR_ALREADY_EXISTS`, it - // still returns the same mutex handle. - // - // https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createmutexa - MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS); - } -#endif // XP_WIN + if (mShmem == nullptr) { + mShmem = (new VRShMem(nullptr, mVRProcessEnabled, XRE_IsParentProcess())); + mShmem->CreateShMem(); + #if !defined(MOZ_WIDGET_ANDROID) - // The VR Service accesses all hardware from a separate process - // and replaces the other VRManager when enabled. - // If the VR process is not enabled, create an in-process VRService. - if (!mVRProcessEnabled) { - // If the VR process is disabled, attempt to create a - // VR service within the current process - mExternalShmem = new VRExternalShmem(); - // VRExternalShmem is asserted to be POD - mExternalShmem->Clear(); - mServiceHost->CreateService(mExternalShmem); - return; - } -#endif - -#if defined(XP_MACOSX) - if (mShmemFD == 0) { - mShmemFD = - shm_open(kShmemName, O_RDWR, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH); - } - if (mShmemFD <= 0) { - mShmemFD = 0; - return; - } - - struct stat sb; - fstat(mShmemFD, &sb); - off_t length = sb.st_size; - if (length < (off_t)sizeof(VRExternalShmem)) { - // TODO - Implement logging (Bug 1558912) - CloseShmem(); - return; - } - - mExternalShmem = (VRExternalShmem*)mmap(NULL, length, PROT_READ | PROT_WRITE, - MAP_SHARED, mShmemFD, 0); - if (mExternalShmem == MAP_FAILED) { - // TODO - Implement logging (Bug 1558912) - mExternalShmem = NULL; - CloseShmem(); - return; - } - -#elif defined(XP_WIN) - if (mShmemFile == NULL) { - mShmemFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, - 0, sizeof(VRExternalShmem), kShmemName); - MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS); - MOZ_ASSERT(mShmemFile); - if (mShmemFile == NULL) { - // TODO - Implement logging (Bug 1558912) - CloseShmem(); + // The VR Service accesses all hardware from a separate process + // and replaces the other VRManager when enabled. + // If the VR process is not enabled, create an in-process VRService. + if (!mVRProcessEnabled) { + // If the VR process is disabled, attempt to create a + // VR service within the current process + mServiceHost->CreateService(mShmem->GetExternalShmem()); return; } - } - LARGE_INTEGER length; - length.QuadPart = sizeof(VRExternalShmem); - mExternalShmem = (VRExternalShmem*)MapViewOfFile( - mShmemFile, // handle to map object - FILE_MAP_ALL_ACCESS, // read/write permission - 0, 0, length.QuadPart); - - if (mExternalShmem == NULL) { - // TODO - Implement logging (Bug 1558912) - CloseShmem(); - return; - } -#elif defined(MOZ_WIDGET_ANDROID) - mExternalShmem = - (VRExternalShmem*)mozilla::GeckoVRManager::GetExternalContext(); - if (!mExternalShmem) { - return; - } - int32_t version = -1; - int32_t size = 0; - if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == - 0) { - version = mExternalShmem->version; - size = mExternalShmem->size; - pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex)); - } else { - return; - } - if (version != kVRExternalVersion) { - mExternalShmem = nullptr; - return; - } - if (size != sizeof(VRExternalShmem)) { - mExternalShmem = nullptr; - return; - } #endif + } else { + mShmem->ClearShMem(); + } } void VRManager::CloseShmem() { -#if !defined(MOZ_WIDGET_ANDROID) - if (!mVRProcessEnabled) { - if (mExternalShmem) { - delete mExternalShmem; - mExternalShmem = nullptr; - } - return; + if (mShmem != nullptr) { + mShmem->CloseShMem(); + delete mShmem; + mShmem = nullptr; } -#endif -#if defined(XP_MACOSX) - if (mExternalShmem) { - munmap((void*)mExternalShmem, sizeof(VRExternalShmem)); - mExternalShmem = NULL; - } - if (mShmemFD) { - close(mShmemFD); - mShmemFD = 0; - } -#elif defined(XP_WIN) - if (mExternalShmem) { - UnmapViewOfFile((void*)mExternalShmem); - mExternalShmem = NULL; - } - if (mShmemFile) { - CloseHandle(mShmemFile); - mShmemFile = NULL; - } -#elif defined(MOZ_WIDGET_ANDROID) - mExternalShmem = NULL; -#endif } VRManager::~VRManager() { @@ -323,12 +180,7 @@ VRManager::~VRManager() { mServiceHost->Shutdown(); #endif CloseShmem(); -#if defined(XP_WIN) - if (mMutex) { - CloseHandle(mMutex); - mMutex = NULL; - } -#endif + MOZ_COUNT_DTOR(VRManager); } @@ -683,7 +535,7 @@ void VRManager::EnumerateVRDisplays() { #if !defined(MOZ_WIDGET_ANDROID) mServiceHost->StartService(); #endif - if (mExternalShmem) { + if (mShmem) { mDisplayInfo.Clear(); mLastUpdateDisplayInfo.Clear(); mFrameStarted = false; @@ -698,7 +550,7 @@ void VRManager::EnumerateVRDisplays() { } // if (mState == VRManagerState::Idle) if (mState == VRManagerState::Enumeration) { - MOZ_ASSERT(mExternalShmem != nullptr); + MOZ_ASSERT(mShmem != nullptr); PullState(); if (mEnumerationCompleted) { @@ -932,120 +784,19 @@ void VRManager::ResetPuppet(VRManagerParent* aManagerParent) { #endif // !defined(MOZ_WIDGET_ANDROID) -#if defined(MOZ_WIDGET_ANDROID) void VRManager::PullState( const std::function& aWaitCondition /* = nullptr */) { - if (!mExternalShmem) { - return; + if (mShmem != nullptr) { + mShmem->PullSystemState(mDisplayInfo.mDisplayState, mLastSensorState, + mDisplayInfo.mControllerState, + mEnumerationCompleted, aWaitCondition); } - bool done = false; - while (!done) { - if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == - 0) { - while (true) { - memcpy(&mDisplayInfo.mDisplayState, - (void*)&(mExternalShmem->state.displayState), - sizeof(VRDisplayState)); - memcpy(&mLastSensorState, (void*)&(mExternalShmem->state.sensorState), - sizeof(VRHMDSensorState)); - memcpy(mDisplayInfo.mControllerState, - (void*)&(mExternalShmem->state.controllerState), - sizeof(VRControllerState) * kVRControllerMaxCount); - mEnumerationCompleted = mExternalShmem->state.enumerationCompleted; - 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); - } // while (true) - 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; - } - } // while (!done) { } -#else - -void VRManager::PullState( - const std::function& aWaitCondition /* = nullptr */) { - MOZ_ASSERT(mExternalShmem); - if (!mExternalShmem) { - return; - } - while (true) { - { // Scope for WaitForMutex -# if defined(XP_WIN) - bool status = true; - WaitForMutex lock(mMutex); - status = lock.GetStatus(); - if (status) { -# endif // defined(XP_WIN) - VRExternalShmem tmp; - memcpy(&tmp, (void*)mExternalShmem, sizeof(VRExternalShmem)); - bool isCleanCopy = - tmp.generationA == tmp.generationB && tmp.generationA != 0; - if (isCleanCopy) { - memcpy(&mDisplayInfo.mDisplayState, &tmp.state.displayState, - sizeof(VRDisplayState)); - memcpy(&mLastSensorState, &tmp.state.sensorState, - sizeof(VRHMDSensorState)); - memcpy(mDisplayInfo.mControllerState, - (void*)&(mExternalShmem->state.controllerState), - sizeof(VRControllerState) * kVRControllerMaxCount); - mEnumerationCompleted = mExternalShmem->state.enumerationCompleted; - // Check for wait condition - if (!aWaitCondition || aWaitCondition()) { - return; - } - } // if (isCleanCopy) - // Yield the thread while polling - YieldThread(); -# if defined(XP_WIN) - } else if (!aWaitCondition) { - // WaitForMutex failed and we are not waiting for a condition to - // exit from PullState call. - return; - } -# endif // defined(XP_WIN) - } // End: Scope for WaitForMutex - // Yield the thread while polling - YieldThread(); - } // while (!true) -} -#endif // defined(MOZ_WIDGET_ANDROID) void VRManager::PushState(bool aNotifyCond) { - if (!mExternalShmem) { - return; + if (mShmem != nullptr) { + mShmem->PushBrowserState(mBrowserState, aNotifyCond); } -#if defined(MOZ_WIDGET_ANDROID) - if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) == - 0) { - memcpy((void*)&(mExternalShmem->geckoState), (void*)&mBrowserState, - sizeof(VRBrowserState)); - if (aNotifyCond) { - pthread_cond_signal((pthread_cond_t*)&(mExternalShmem->geckoCond)); - } - pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)); - } -#else - bool status = true; -# if defined(XP_WIN) - WaitForMutex lock(mMutex); - status = lock.GetStatus(); -# endif // defined(XP_WIN) - if (status) { - mExternalShmem->geckoGenerationA++; - memcpy((void*)&(mExternalShmem->geckoState), (void*)&mBrowserState, - sizeof(VRBrowserState)); - mExternalShmem->geckoGenerationB++; - } -#endif // defined(MOZ_WIDGET_ANDROID) } void VRManager::Destroy() { @@ -1268,7 +1019,7 @@ void VRManager::StopPresentation() { } bool VRManager::IsPresenting() { - if (mExternalShmem) { + if (mShmem) { return mDisplayInfo.mPresentingGroups != 0; } return false; diff --git a/gfx/vr/VRManager.h b/gfx/vr/VRManager.h index eb3b96690480..eccfbd9ed7d7 100644 --- a/gfx/vr/VRManager.h +++ b/gfx/vr/VRManager.h @@ -20,6 +20,7 @@ class VRLayerParent; class VRManagerParent; class VRServiceHost; class VRThread; +class VRShMem; enum class VRManagerState : uint32_t { Disabled, // All VRManager activity is stopped @@ -131,7 +132,6 @@ class VRManager { bool mVRDisplaysRequestedNonFocus; bool mVRControllersRequested; bool mFrameStarted; - volatile VRExternalShmem* mExternalShmem; uint32_t mTaskInterval; RefPtr mTaskTimer; mozilla::Monitor mCurrentSubmitTaskMonitor; @@ -139,14 +139,13 @@ class VRManager { uint64_t mLastSubmittedFrameId; uint64_t mLastStartedFrame; bool mEnumerationCompleted; -#if defined(XP_MACOSX) - int mShmemFD; -#elif defined(XP_WIN) - base::ProcessHandle mShmemFile; - HANDLE mMutex; -#endif -#if !defined(MOZ_WIDGET_ANDROID) + + // Note: mShmem doesn't support RefPtr; thus, do not share this private + // pointer so that its lifetime can still be controlled by VRManager + VRShMem* mShmem; bool mVRProcessEnabled; + +#if !defined(MOZ_WIDGET_ANDROID) RefPtr mServiceHost; #endif diff --git a/gfx/vr/VRShMem.cpp b/gfx/vr/VRShMem.cpp index 075bcd52cced..6ee819579796 100644 --- a/gfx/vr/VRShMem.cpp +++ b/gfx/vr/VRShMem.cpp @@ -32,6 +32,7 @@ using namespace mozilla::gfx; // release builds running on same machine? (Bug 1563232) #ifdef XP_WIN static const char* kShmemName = "moz.gecko.vr_ext.0.0.1"; +static LPCTSTR kMutexName = TEXT("mozilla::vr::ShmemMutex"); #elif defined(XP_MACOSX) static const char* kShmemName = "/moz.gecko.vr_ext.0.0.1"; #endif // XP_WIN @@ -48,10 +49,10 @@ void YieldThread() { } // anonymous namespace #endif // !defined(MOZ_WIDGET_ANDROID) -VRShMem::VRShMem(volatile VRExternalShmem* aShmem, bool aSameProcess, +VRShMem::VRShMem(volatile VRExternalShmem* aShmem, bool aVRProcessEnabled, bool aIsParentProcess) : mExternalShmem(aShmem), - mSameProcess(aSameProcess) + mVRProcessEnabled(aVRProcessEnabled) #if defined(XP_WIN) , mIsParentProcess(aIsParentProcess) @@ -65,32 +66,44 @@ VRShMem::VRShMem(volatile VRExternalShmem* aShmem, bool aSameProcess, mMutex(nullptr) #endif { - // To confirm: - // - aShmem is null for VRManager, or for service in multi-proc - // - aShmem is !null for VRService in-proc - // make this into an assert - if (!(aShmem == nullptr || aSameProcess)) { - //::DebugBreak(); - } + // Regarding input parameters, + // - aShmem is null for VRManager or for VRService in multi-proc + // - aShmem is !null for VRService in-proc (i.e., no VR proc) + MOZ_ASSERT(aShmem == nullptr || !aVRProcessEnabled); +} - // copied from VRService Ctor - // When we have the VR process, we map the memory - // of mAPIShmem from GPU process and pass it to the CTOR. - // If we don't have the VR process, we will instantiate - // mAPIShmem in VRService. +// Note: This function should only be called for in-proc scenarios, where the +// shared memory is only shared within the same proc (rather than across +// processes). Also, this local heap memory's lifetime is tied to the class. +// Callers to this must ensure that its reference doesn't outlive the owning +// VRShMem instance. +volatile VRExternalShmem* VRShMem::GetExternalShmem() const { + MOZ_ASSERT(!mVRProcessEnabled); +#if defined(XP_MACOSX) + MOZ_ASSERT(mShmemFD == 0); +#elif defined(XP_WIN) + MOZ_ASSERT(mShmemFile == nullptr); +#endif + return mExternalShmem; +} + +bool VRShMem::IsDisplayStateShutdown() const { + // adapted from VRService::Refresh + // Does this need the mutex for getting .shutdown? + return mExternalShmem != nullptr && + mExternalShmem->state.displayState.shutdown; } // Callers/Processes to CreateShMem should followup with CloseShMem -// [copied from VRManager::OpenShmem] void VRShMem::CreateShMem() { if (mExternalShmem) { return; } #if defined(XP_WIN) if (mMutex == nullptr) { - mMutex = CreateMutex(nullptr, // default security descriptor - false, // mutex not owned - TEXT("mozilla::vr::ShmemMutex")); // object name + mMutex = CreateMutex(nullptr, // default security descriptor + false, // mutex not owned + kMutexName); // object name if (mMutex == nullptr) { # ifdef MOZILLA_INTERNAL_API nsAutoCString msg; @@ -113,17 +126,11 @@ void VRShMem::CreateShMem() { // The VR Service accesses all hardware from a separate process // and replaces the other VRManager when enabled. // If the VR process is not enabled, create an in-process VRService. - if (mSameProcess) { + if (!mVRProcessEnabled) { // If the VR process is disabled, attempt to create a // VR service within the current process mExternalShmem = new VRExternalShmem(); -# ifdef MOZILLA_INTERNAL_API - // TODO: Create external variant to Clear (Bug 1563233) - // VRExternalShmem is asserted to be POD - mExternalShmem->Clear(); -# endif - // TODO: move this call to the caller (Bug 1563233) - // mServiceHost->CreateService(mExternalShmem); + ClearShMem(); return; } #endif @@ -208,11 +215,21 @@ void VRShMem::CreateShMem() { #endif } +void VRShMem::ClearShMem() { + if (mExternalShmem != nullptr) { +#ifdef MOZILLA_INTERNAL_API + // VRExternalShmem is asserted to be POD + mExternalShmem->Clear(); +#else + memset((void*)mExternalShmem, 0, sizeof(VRExternalShmem)); +#endif + } +} + // The cleanup corresponding to CreateShMem -// [copied from VRManager::CloseShmem, dtor] void VRShMem::CloseShMem() { #if !defined(MOZ_WIDGET_ANDROID) - if (mSameProcess) { + if (!mVRProcessEnabled) { if (mExternalShmem) { delete mExternalShmem; mExternalShmem = nullptr; @@ -241,25 +258,51 @@ void VRShMem::CloseShMem() { #elif defined(MOZ_WIDGET_ANDROID) mExternalShmem = NULL; #endif + +#if defined(XP_WIN) + // from ~VRManager. DOes this need to be separated into a separate function? + if (mMutex) { + CloseHandle(mMutex); + mMutex = nullptr; + } +#endif } // Called to use an existing shmem instance created by another process // Callers to JoinShMem should call LeaveShMem for cleanup // [copied from VRService::InitShmem, VRService::Start] bool VRShMem::JoinShMem() { - // was if (!mVRProcessEnabled) { - if (mSameProcess) { +#if defined(XP_WIN) + // Adding `!XRE_IsParentProcess()` to avoid Win 7 32-bit WebVR tests + // to OpenMutex when there is no GPU process to create + // VRSystemManagerExternal and its mutex. + if (!mMutex && !mIsParentProcess) { + mMutex = OpenMutex(MUTEX_ALL_ACCESS, // request full access + false, // handle not inheritable + kMutexName); // object name + + if (mMutex == nullptr) { +# ifdef MOZILLA_INTERNAL_API + nsAutoCString msg; + msg.AppendPrintf("VRService OpenMutex error \"%lu\".", GetLastError()); + NS_WARNING(msg.get()); +# endif + MOZ_ASSERT(false); + } + MOZ_ASSERT(GetLastError() == 0); + } +#endif + + if (!mVRProcessEnabled) { return true; } #if defined(XP_WIN) - const char* kShmemName = "moz.gecko.vr_ext.0.0.1"; - base::ProcessHandle targetHandle = 0; - // Opening a file-mapping object by name - targetHandle = OpenFileMappingA(FILE_MAP_ALL_ACCESS, // read/write access - FALSE, // do not inherit the name - kShmemName); // name of mapping object + base::ProcessHandle targetHandle = + OpenFileMappingA(FILE_MAP_ALL_ACCESS, // read/write access + FALSE, // do not inherit the name + kShmemName); // name of mapping object MOZ_ASSERT(GetLastError() == 0); @@ -282,35 +325,12 @@ bool VRShMem::JoinShMem() { // // TODO: ** Does this mean that ShMem only works in Windows for now? If so, // let's delete the code from other platforms (Bug 1563234) + MOZ_ASSERT(false, "JoinShMem not implemented"); #endif - -#if defined(XP_WIN) - // Adding `!XRE_IsParentProcess()` to avoid Win 7 32-bit WebVR tests - // to OpenMutex when there is no GPU process to create - // VRSystemManagerExternal and its mutex. - // was if (!mMutex && !XRE_IsParentProcess()) { - if (!mMutex && !mIsParentProcess) { - mMutex = OpenMutex(MUTEX_ALL_ACCESS, // request full access - false, // handle not inheritable - TEXT("mozilla::vr::ShmemMutex")); // object name - - if (mMutex == nullptr) { -# ifdef MOZILLA_INTERNAL_API - nsAutoCString msg; - msg.AppendPrintf("VRService OpenMutex error \"%lu\".", GetLastError()); - NS_WARNING(msg.get()); -# endif - MOZ_ASSERT(false); - } - MOZ_ASSERT(GetLastError() == 0); - } -#endif - return true; } // The cleanup corresponding to JoinShMem -// [copied from VRService::Stop] void VRShMem::LeaveShMem() { #if defined(XP_WIN) if (mShmemFile) { @@ -319,8 +339,7 @@ void VRShMem::LeaveShMem() { } #endif - // was if (mVRProcessEnabled && mAPIShmem) { - if (mExternalShmem != nullptr && !mSameProcess) { + if (mExternalShmem != nullptr && mVRProcessEnabled) { #if defined(XP_WIN) UnmapViewOfFile((void*)mExternalShmem); #endif @@ -334,7 +353,6 @@ void VRShMem::LeaveShMem() { #endif } -// [copied from from VRManager::PushState] void VRShMem::PushBrowserState(VRBrowserState& aBrowserState, bool aNotifyCond) { if (!mExternalShmem) { @@ -365,7 +383,6 @@ void VRShMem::PushBrowserState(VRBrowserState& aBrowserState, #endif // defined(MOZ_WIDGET_ANDROID) } -// [copied from VRService::PullState] void VRShMem::PullBrowserState(mozilla::gfx::VRBrowserState& aState) { if (!mExternalShmem) { return; @@ -389,10 +406,10 @@ void VRShMem::PullBrowserState(mozilla::gfx::VRBrowserState& aState) { pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)); } */ + MOZ_ASSERT(false, "PullBrowserState not implemented"); #else bool status = true; # if defined(XP_WIN) - // was if (!XRE_IsParentProcess()) { if (!mIsParentProcess) { // TODO: Is this scoped lock okay? Seems like it should allow some // race condition (Bug 1563234) @@ -418,7 +435,6 @@ void VRShMem::PullBrowserState(mozilla::gfx::VRBrowserState& aState) { #endif // defined(MOZ_WIDGET_ANDROID) } -// [copied from VRService::PushState] void VRShMem::PushSystemState(const mozilla::gfx::VRSystemState& aState) { if (!mExternalShmem) { return; @@ -431,6 +447,7 @@ void VRShMem::PushSystemState(const mozilla::gfx::VRSystemState& aState) { #if defined(MOZ_WIDGET_ANDROID) // TODO: This code is out-of-date and fails to compile, as // VRService isn't compiled for Android (Bug 1563234) + MOZ_ASSERT(false, "JoinShMem not implemented"); /* if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) { @@ -445,8 +462,9 @@ void VRShMem::PushSystemState(const mozilla::gfx::VRSystemState& aState) { #else bool lockState = true; # if defined(XP_WIN) - // was if (!XRE_IsParentProcess()) { if (!mIsParentProcess) { + // TODO: Is this scoped lock okay? Seems like it should allow some + // race condition (Bug 1563234) WaitForMutex lock(mMutex); lockState = lock.GetStatus(); } @@ -459,7 +477,6 @@ void VRShMem::PushSystemState(const mozilla::gfx::VRSystemState& aState) { #endif // defined(MOZ_WIDGET_ANDROID) } -// [copied from void VRManager::PullState] #if defined(MOZ_WIDGET_ANDROID) void VRShMem::PullSystemState( VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState, diff --git a/gfx/vr/VRShMem.h b/gfx/vr/VRShMem.h index 7a3c3d2fb3d1..d88f98706f4e 100644 --- a/gfx/vr/VRShMem.h +++ b/gfx/vr/VRShMem.h @@ -22,11 +22,12 @@ namespace mozilla { namespace gfx { class VRShMem final { public: - VRShMem(volatile VRExternalShmem* aShmem, bool aSameProcess, + VRShMem(volatile VRExternalShmem* aShmem, bool aVRProcessEnabled, bool aIsParentProcess); ~VRShMem() = default; void CreateShMem(); + void ClearShMem(); void CloseShMem(); bool JoinShMem(); @@ -42,9 +43,14 @@ class VRShMem final { bool& aEnumerationCompleted, const std::function& aWaitCondition = nullptr); + bool HasExternalShmem() const { return mExternalShmem != nullptr; } + volatile VRExternalShmem* GetExternalShmem() const; + bool IsDisplayStateShutdown() const; + private: volatile VRExternalShmem* mExternalShmem = nullptr; - bool mSameProcess = false; + bool mVRProcessEnabled; + #if defined(XP_WIN) bool mIsParentProcess = false; #endif diff --git a/gfx/vr/service/VRService.cpp b/gfx/vr/service/VRService.cpp index d7e9dabac14f..d1d60adf6b3e 100644 --- a/gfx/vr/service/VRService.cpp +++ b/gfx/vr/service/VRService.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "VRService.h" +#include "../VRShMem.h" #include "mozilla/StaticPrefs.h" #include "../gfxVRMutex.h" #include "base/thread.h" // for Thread @@ -60,21 +61,15 @@ already_AddRefed VRService::Create( VRService::VRService(volatile VRExternalShmem* aShmem) : mSystemState{}, mBrowserState{}, - mBrowserGeneration(0), mServiceThread(nullptr), mShutdownRequested(false), - mAPIShmem(aShmem), - mTargetShmemFile(0), mLastHapticState{}, - mFrameStartTime{}, -#if defined(XP_WIN) - mMutex(NULL), -#endif - mVRProcessEnabled(aShmem == nullptr) { + mFrameStartTime{} { // When we have the VR process, we map the memory // of mAPIShmem from GPU process and pass it to the CTOR. // If we don't have the VR process, we will instantiate // mAPIShmem in VRService. + mShmem = new VRShMem(aShmem, aShmem == nullptr, XRE_IsParentProcess()); } VRService::~VRService() { @@ -84,35 +79,12 @@ VRService::~VRService() { } void VRService::Refresh() { - if (!mAPIShmem) { - return; - } - - if (mAPIShmem->state.displayState.shutdown) { + if (mShmem != nullptr && mShmem->IsDisplayStateShutdown()) { Stop(); } } void VRService::Start() { -#if defined(XP_WIN) - // Adding `!XRE_IsParentProcess()` to avoid Win 7 32-bit WebVR tests - // to OpenMutex when there is no GPU process to create - // VRSystemManagerExternal and its mutex. - if (!mMutex && !XRE_IsParentProcess()) { - mMutex = OpenMutex(MUTEX_ALL_ACCESS, // request full access - false, // handle not inheritable - TEXT("mozilla::vr::ShmemMutex")); // object name - - if (mMutex == NULL) { - nsAutoCString msg; - msg.AppendPrintf("VRService OpenMutex error \"%lu\".", GetLastError()); - NS_WARNING(msg.get()); - MOZ_ASSERT(false); - } - MOZ_ASSERT(GetLastError() == 0); - } -#endif - if (!mServiceThread) { /** * We must ensure that any time the service is re-started, that @@ -155,63 +127,17 @@ void VRService::Stop() { delete mServiceThread; mServiceThread = nullptr; } - if (mTargetShmemFile) { -#if defined(XP_WIN) - CloseHandle(mTargetShmemFile); -#endif - mTargetShmemFile = 0; + + if (mShmem != nullptr) { + mShmem->LeaveShMem(); + delete mShmem; + mShmem = nullptr; } - if (mVRProcessEnabled && mAPIShmem) { -#if defined(XP_WIN) - UnmapViewOfFile((void*)mAPIShmem); -#endif - mAPIShmem = nullptr; - } -#if defined(XP_WIN) - if (mMutex) { - CloseHandle(mMutex); - mMutex = NULL; - } -#endif + mSession = nullptr; } -bool VRService::InitShmem() { - if (!mVRProcessEnabled) { - return true; - } - -#if defined(XP_WIN) - const char* kShmemName = "moz.gecko.vr_ext.0.0.1"; - base::ProcessHandle targetHandle = 0; - - // Opening a file-mapping object by name - targetHandle = OpenFileMappingA(FILE_MAP_ALL_ACCESS, // read/write access - FALSE, // do not inherit the name - kShmemName); // name of mapping object - - MOZ_ASSERT(GetLastError() == 0); - - LARGE_INTEGER length; - length.QuadPart = sizeof(VRExternalShmem); - mAPIShmem = (VRExternalShmem*)MapViewOfFile( - reinterpret_cast( - targetHandle), // handle to map object - FILE_MAP_ALL_ACCESS, // read/write permission - 0, 0, length.QuadPart); - MOZ_ASSERT(GetLastError() == 0); - // TODO - Implement logging - mTargetShmemFile = targetHandle; - if (!mAPIShmem) { - MOZ_ASSERT(mAPIShmem); - return false; - } -#else - // TODO: Implement shmem for other platforms. -#endif - - return true; -} +bool VRService::InitShmem() { return mShmem->JoinShMem(); } bool VRService::IsInServiceThread() { return (mServiceThread != nullptr) && @@ -447,81 +373,13 @@ void VRService::UpdateHaptics() { } void VRService::PushState(const mozilla::gfx::VRSystemState& aState) { - if (!mAPIShmem) { - return; + if (mShmem != nullptr) { + mShmem->PushSystemState(aState); } - // Copying the VR service state to the shmem is atomic, infallable, - // and non-blocking on x86/x64 architectures. Arm requires a mutex - // that is locked for the duration of the memcpy to and from shmem on - // both sides. - -#if defined(MOZ_WIDGET_ANDROID) - if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == - 0) { - // We are casting away the volatile keyword, which is not accepted by - // memcpy. It is possible (although very unlikely) that the compiler - // may optimize out the memcpy here as memcpy isn't explicitly safe for - // volatile memory in the C++ standard. - memcpy((void*)&mAPIShmem->state, &aState, sizeof(VRSystemState)); - pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex)); - } -#else - bool state = true; -# if defined(XP_WIN) - if (!XRE_IsParentProcess()) { - WaitForMutex lock(mMutex); - state = lock.GetStatus(); - } -# endif // defined(XP_WIN) - if (state) { - mAPIShmem->generationA++; - memcpy((void*)&mAPIShmem->state, &aState, sizeof(VRSystemState)); - mAPIShmem->generationB++; - } -#endif // defined(MOZ_WIDGET_ANDROID) } void VRService::PullState(mozilla::gfx::VRBrowserState& aState) { - if (!mAPIShmem) { - return; + if (mShmem != nullptr) { + mShmem->PullBrowserState(aState); } - // Copying the browser state from the shmem is non-blocking - // on x86/x64 architectures. Arm requires a mutex that is - // locked for the duration of the memcpy to and from shmem on - // both sides. - // On x86/x64 It is fallable -- If a dirty copy is detected by - // a mismatch of geckoGenerationA and geckoGenerationB, - // the copy is discarded and will not replace the last known - // browser state. - -#if defined(MOZ_WIDGET_ANDROID) - if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) == - 0) { - memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState)); - pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)); - } -#else - bool status = true; -# if defined(XP_WIN) - if (!XRE_IsParentProcess()) { - WaitForMutex lock(mMutex); - status = lock.GetStatus(); - } -# endif // defined(XP_WIN) - if (status) { - VRExternalShmem tmp; - if (mAPIShmem->geckoGenerationA != mBrowserGeneration) { - // TODO - (void *) cast removes volatile semantics. - // The memcpy is not likely to be optimized out, but is theoretically - // possible. Suggest refactoring to either explicitly enforce memory - // order or to use locks. - memcpy(&tmp, (void*)mAPIShmem, sizeof(VRExternalShmem)); - if (tmp.geckoGenerationA == tmp.geckoGenerationB && - tmp.geckoGenerationA != 0) { - memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState)); - mBrowserGeneration = tmp.geckoGenerationA; - } - } - } -#endif // defined(MOZ_WIDGET_ANDROID) } diff --git a/gfx/vr/service/VRService.h b/gfx/vr/service/VRService.h index 749c7efd0427..2bd64387f4d9 100644 --- a/gfx/vr/service/VRService.h +++ b/gfx/vr/service/VRService.h @@ -18,6 +18,7 @@ namespace mozilla { namespace gfx { class VRSession; +class VRShMem; static const int kVRFrameTimingHistoryDepth = 100; @@ -53,20 +54,16 @@ class VRService { * mBrowserState is memcpy'ed from the Shmem atomically */ VRBrowserState mBrowserState; - int64_t mBrowserGeneration; UniquePtr mSession; base::Thread* mServiceThread; bool mShutdownRequested; - volatile VRExternalShmem* MOZ_OWNING_REF mAPIShmem; - base::ProcessHandle mTargetShmemFile; + // Note: mShmem doesn't support RefPtr; thus, do not share this private + // pointer so that its lifetime can still be controlled by VRService + VRShMem* mShmem; VRHapticState mLastHapticState[kVRHapticsMaxCount]; TimeStamp mFrameStartTime[kVRFrameTimingHistoryDepth]; -#if defined(XP_WIN) - HANDLE mMutex; -#endif - bool mVRProcessEnabled; bool IsInServiceThread(); void UpdateHaptics(); diff --git a/gfx/vr/vrhost/vrhost.cpp b/gfx/vr/vrhost/vrhost.cpp index 217a67820d34..5c3091d406a5 100644 --- a/gfx/vr/vrhost/vrhost.cpp +++ b/gfx/vr/vrhost/vrhost.cpp @@ -50,7 +50,7 @@ void TestTheManager() { ); printf("\n01 mgr: create mgr\n"); - mozilla::gfx::VRShMem shmem(nullptr, false, false); + mozilla::gfx::VRShMem shmem(nullptr, true, true); shmem.CreateShMem(); printf("02 mgr: wait for signal\n"); @@ -88,6 +88,9 @@ void TestTheManager() { state.controllerState[1].hand, state.sensorState.inputFrameID); shmem.CloseShMem(); + + printf("mgr complete"); + fflush(nullptr); } // For testing VRShMem as the Service (i.e., the one who consumes the @@ -100,7 +103,7 @@ void TestTheService() { ); printf("\n03 svc: create svc\n"); - mozilla::gfx::VRShMem shmem(nullptr, false, false); + mozilla::gfx::VRShMem shmem(nullptr, true, false); shmem.JoinShMem(); printf("04 svc: send signal\n"); @@ -136,4 +139,7 @@ void TestTheService() { ::SetEvent(hEvent); shmem.LeaveShMem(); + + printf("svc complete"); + fflush(nullptr); } \ No newline at end of file