diff options
Diffstat (limited to 'gfx/vr/ipc/VRManagerChild.cpp')
-rw-r--r-- | gfx/vr/ipc/VRManagerChild.cpp | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/gfx/vr/ipc/VRManagerChild.cpp b/gfx/vr/ipc/VRManagerChild.cpp new file mode 100644 index 000000000..70ced86c3 --- /dev/null +++ b/gfx/vr/ipc/VRManagerChild.cpp @@ -0,0 +1,593 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VRManagerChild.h" +#include "VRManagerParent.h" +#include "VRDisplayClient.h" +#include "nsGlobalWindow.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/layers/CompositorThread.h" // for CompositorThread +#include "mozilla/dom/Navigator.h" +#include "mozilla/dom/VREventObserver.h" +#include "mozilla/dom/WindowBinding.h" // for FrameRequestCallback +#include "mozilla/dom/ContentChild.h" +#include "mozilla/layers/TextureClient.h" +#include "nsContentUtils.h" + +#ifdef MOZ_GAMEPAD +#include "mozilla/dom/GamepadManager.h" +#endif + +using layers::TextureClient; + +namespace { +const nsTArray<RefPtr<dom::VREventObserver>>::index_type kNoIndex = + nsTArray<RefPtr<dom::VREventObserver> >::NoIndex; +} // namespace + +namespace mozilla { +namespace gfx { + +static StaticRefPtr<VRManagerChild> sVRManagerChildSingleton; +static StaticRefPtr<VRManagerParent> sVRManagerParentSingleton; + +void ReleaseVRManagerParentSingleton() { + sVRManagerParentSingleton = nullptr; +} + +VRManagerChild::VRManagerChild() + : TextureForwarder() + , mDisplaysInitialized(false) + , mInputFrameID(-1) + , mMessageLoop(MessageLoop::current()) + , mFrameRequestCallbackCounter(0) + , mBackend(layers::LayersBackend::LAYERS_NONE) +{ + MOZ_COUNT_CTOR(VRManagerChild); + MOZ_ASSERT(NS_IsMainThread()); + + mStartTimeStamp = TimeStamp::Now(); +} + +VRManagerChild::~VRManagerChild() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_COUNT_DTOR(VRManagerChild); +} + +/*static*/ void +VRManagerChild::IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier) +{ + if (sVRManagerChildSingleton) { + sVRManagerChildSingleton->mBackend = aIdentifier.mParentBackend; + sVRManagerChildSingleton->mSyncObject = SyncObject::CreateSyncObject(aIdentifier.mSyncHandle); + } +} + +layers::LayersBackend +VRManagerChild::GetBackendType() const +{ + return mBackend; +} + +/*static*/ VRManagerChild* +VRManagerChild::Get() +{ + MOZ_ASSERT(sVRManagerChildSingleton); + return sVRManagerChildSingleton; +} + +/* static */ bool +VRManagerChild::IsCreated() +{ + return !!sVRManagerChildSingleton; +} + +/* static */ bool +VRManagerChild::InitForContent(Endpoint<PVRManagerChild>&& aEndpoint) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sVRManagerChildSingleton); + + RefPtr<VRManagerChild> child(new VRManagerChild()); + if (!aEndpoint.Bind(child)) { + NS_RUNTIMEABORT("Couldn't Open() Compositor channel."); + return false; + } + sVRManagerChildSingleton = child; + return true; +} + +/* static */ bool +VRManagerChild::ReinitForContent(Endpoint<PVRManagerChild>&& aEndpoint) +{ + MOZ_ASSERT(NS_IsMainThread()); + + ShutDown(); + + return InitForContent(Move(aEndpoint)); +} + +/*static*/ void +VRManagerChild::InitSameProcess() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sVRManagerChildSingleton); + + sVRManagerChildSingleton = new VRManagerChild(); + sVRManagerParentSingleton = VRManagerParent::CreateSameProcess(); + sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(), + mozilla::layers::CompositorThreadHolder::Loop(), + mozilla::ipc::ChildSide); +} + +/* static */ void +VRManagerChild::InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sVRManagerChildSingleton); + + sVRManagerChildSingleton = new VRManagerChild(); + if (!aEndpoint.Bind(sVRManagerChildSingleton)) { + NS_RUNTIMEABORT("Couldn't Open() Compositor channel."); + return; + } +} + +/*static*/ void +VRManagerChild::ShutDown() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (sVRManagerChildSingleton) { + sVRManagerChildSingleton->Destroy(); + sVRManagerChildSingleton = nullptr; + } +} + +/*static*/ void +VRManagerChild::DeferredDestroy(RefPtr<VRManagerChild> aVRManagerChild) +{ + aVRManagerChild->Close(); +} + +void +VRManagerChild::Destroy() +{ + mTexturesWaitingRecycled.Clear(); + + // Keep ourselves alive until everything has been shut down + RefPtr<VRManagerChild> selfRef = this; + + // The DeferredDestroyVRManager task takes ownership of + // the VRManagerChild and will release it when it runs. + MessageLoop::current()->PostTask( + NewRunnableFunction(DeferredDestroy, selfRef)); +} + +layers::PTextureChild* +VRManagerChild::AllocPTextureChild(const SurfaceDescriptor&, + const LayersBackend&, + const TextureFlags&, + const uint64_t&) +{ + return TextureClient::CreateIPDLActor(); +} + +bool +VRManagerChild::DeallocPTextureChild(PTextureChild* actor) +{ + return TextureClient::DestroyIPDLActor(actor); +} + +PVRLayerChild* +VRManagerChild::AllocPVRLayerChild(const uint32_t& aDisplayID, + const float& aLeftEyeX, + const float& aLeftEyeY, + const float& aLeftEyeWidth, + const float& aLeftEyeHeight, + const float& aRightEyeX, + const float& aRightEyeY, + const float& aRightEyeWidth, + const float& aRightEyeHeight) +{ + RefPtr<VRLayerChild> layer = new VRLayerChild(aDisplayID, this); + return layer.forget().take(); +} + +bool +VRManagerChild::DeallocPVRLayerChild(PVRLayerChild* actor) +{ + delete actor; + return true; +} + +void +VRManagerChild::UpdateDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayUpdates) +{ + bool bDisplayConnected = false; + bool bDisplayDisconnected = false; + + // Check if any displays have been disconnected + for (auto& display : mDisplays) { + bool found = false; + for (auto& displayUpdate : aDisplayUpdates) { + if (display->GetDisplayInfo().GetDisplayID() == displayUpdate.GetDisplayID()) { + found = true; + break; + } + } + if (!found) { + display->NotifyDisconnected(); + bDisplayDisconnected = true; + } + } + + // mDisplays could be a hashed container for more scalability, but not worth + // it now as we expect < 10 entries. + nsTArray<RefPtr<VRDisplayClient>> displays; + for (VRDisplayInfo& displayUpdate : aDisplayUpdates) { + bool isNewDisplay = true; + for (auto& display : mDisplays) { + const VRDisplayInfo& prevInfo = display->GetDisplayInfo(); + if (prevInfo.GetDisplayID() == displayUpdate.GetDisplayID()) { + if (displayUpdate.GetIsConnected() && !prevInfo.GetIsConnected()) { + bDisplayConnected = true; + } + if (!displayUpdate.GetIsConnected() && prevInfo.GetIsConnected()) { + bDisplayDisconnected = true; + } + display->UpdateDisplayInfo(displayUpdate); + displays.AppendElement(display); + isNewDisplay = false; + break; + } + } + if (isNewDisplay) { + displays.AppendElement(new VRDisplayClient(displayUpdate)); + bDisplayConnected = true; + } + } + + mDisplays = displays; + + if (bDisplayConnected) { + FireDOMVRDisplayConnectEvent(); + } + if (bDisplayDisconnected) { + FireDOMVRDisplayDisconnectEvent(); + } + + mDisplaysInitialized = true; +} + +bool +VRManagerChild::RecvUpdateDisplayInfo(nsTArray<VRDisplayInfo>&& aDisplayUpdates) +{ + UpdateDisplayInfo(aDisplayUpdates); + for (auto& windowId : mNavigatorCallbacks) { + /** We must call NotifyVRDisplaysUpdated for every + * window's Navigator in mNavigatorCallbacks to ensure that + * the promise returned by Navigator.GetVRDevices + * can resolve. This must happen even if no changes + * to VRDisplays have been detected here. + */ + nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(windowId); + if (!window) { + continue; + } + ErrorResult result; + dom::Navigator* nav = window->GetNavigator(result); + if (NS_WARN_IF(result.Failed())) { + continue; + } + nav->NotifyVRDisplaysUpdated(); + } + mNavigatorCallbacks.Clear(); + return true; +} + +bool +VRManagerChild::GetVRDisplays(nsTArray<RefPtr<VRDisplayClient>>& aDisplays) +{ + if (!mDisplaysInitialized) { + /** + * If we haven't received any asynchronous callback after requesting + * display enumeration with RefreshDisplays, get the existing displays + * that have already been enumerated by other VRManagerChild instances. + */ + nsTArray<VRDisplayInfo> displays; + Unused << SendGetDisplays(&displays); + UpdateDisplayInfo(displays); + } + aDisplays = mDisplays; + return true; +} + +bool +VRManagerChild::RefreshVRDisplaysWithCallback(uint64_t aWindowId) +{ + bool success = SendRefreshDisplays(); + if (success) { + mNavigatorCallbacks.AppendElement(aWindowId); + } + return success; +} + +int +VRManagerChild::GetInputFrameID() +{ + return mInputFrameID; +} + +bool +VRManagerChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) +{ + for (InfallibleTArray<AsyncParentMessageData>::index_type i = 0; i < aMessages.Length(); ++i) { + const AsyncParentMessageData& message = aMessages[i]; + + switch (message.type()) { + case AsyncParentMessageData::TOpNotifyNotUsed: { + const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed(); + NotifyNotUsed(op.TextureId(), op.fwdTransactionId()); + break; + } + default: + NS_ERROR("unknown AsyncParentMessageData type"); + return false; + } + } + return true; +} + +PTextureChild* +VRManagerChild::CreateTexture(const SurfaceDescriptor& aSharedData, + LayersBackend aLayersBackend, + TextureFlags aFlags, + uint64_t aSerial) +{ + return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial); +} + +void +VRManagerChild::CancelWaitForRecycle(uint64_t aTextureId) +{ + RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); + if (!client) { + return; + } + mTexturesWaitingRecycled.Remove(aTextureId); +} + +void +VRManagerChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId) +{ + RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); + if (!client) { + return; + } + mTexturesWaitingRecycled.Remove(aTextureId); +} + +bool +VRManagerChild::AllocShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) +{ + return PVRManagerChild::AllocShmem(aSize, aType, aShmem); +} + +bool +VRManagerChild::AllocUnsafeShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) +{ + return PVRManagerChild::AllocUnsafeShmem(aSize, aType, aShmem); +} + +bool +VRManagerChild::DeallocShmem(ipc::Shmem& aShmem) +{ + return PVRManagerChild::DeallocShmem(aShmem); +} + +PVRLayerChild* +VRManagerChild::CreateVRLayer(uint32_t aDisplayID, const Rect& aLeftEyeRect, const Rect& aRightEyeRect) +{ + return SendPVRLayerConstructor(aDisplayID, + aLeftEyeRect.x, aLeftEyeRect.y, aLeftEyeRect.width, aLeftEyeRect.height, + aRightEyeRect.x, aRightEyeRect.y, aRightEyeRect.width, aRightEyeRect.height); +} + + +// XXX TODO - VRManagerChild::FrameRequest is the same as nsIDocument::FrameRequest, should we consolodate these? +struct VRManagerChild::FrameRequest +{ + FrameRequest(mozilla::dom::FrameRequestCallback& aCallback, + int32_t aHandle) : + mCallback(&aCallback), + mHandle(aHandle) + {} + + // Conversion operator so that we can append these to a + // FrameRequestCallbackList + operator const RefPtr<mozilla::dom::FrameRequestCallback>& () const { + return mCallback; + } + + // Comparator operators to allow RemoveElementSorted with an + // integer argument on arrays of FrameRequest + bool operator==(int32_t aHandle) const { + return mHandle == aHandle; + } + bool operator<(int32_t aHandle) const { + return mHandle < aHandle; + } + + RefPtr<mozilla::dom::FrameRequestCallback> mCallback; + int32_t mHandle; +}; + +nsresult +VRManagerChild::ScheduleFrameRequestCallback(mozilla::dom::FrameRequestCallback& aCallback, + int32_t *aHandle) +{ + if (mFrameRequestCallbackCounter == INT32_MAX) { + // Can't increment without overflowing; bail out + return NS_ERROR_NOT_AVAILABLE; + } + int32_t newHandle = ++mFrameRequestCallbackCounter; + + DebugOnly<FrameRequest*> request = + mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle)); + NS_ASSERTION(request, "This is supposed to be infallible!"); + + *aHandle = newHandle; + return NS_OK; +} + +void +VRManagerChild::CancelFrameRequestCallback(int32_t aHandle) +{ + // mFrameRequestCallbacks is stored sorted by handle + mFrameRequestCallbacks.RemoveElementSorted(aHandle); +} + +bool +VRManagerChild::RecvNotifyVSync() +{ + for (auto& display : mDisplays) { + display->NotifyVsync(); + } + + return true; +} + +bool +VRManagerChild::RecvNotifyVRVSync(const uint32_t& aDisplayID) +{ + for (auto& display : mDisplays) { + if (display->GetDisplayInfo().GetDisplayID() == aDisplayID) { + display->NotifyVRVsync(); + } + } + + return true; +} + +bool +VRManagerChild::RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent) +{ +#ifdef MOZ_GAMEPAD + // VRManagerChild could be at other processes, but GamepadManager + // only exists at the content process or the same process + // in non-e10s mode. + MOZ_ASSERT(XRE_IsContentProcess() || IsSameProcess()); + + RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService()); + if (gamepadManager) { + gamepadManager->Update(aGamepadEvent); + } +#endif + + return true; +} + +void +VRManagerChild::RunFrameRequestCallbacks() +{ + TimeStamp nowTime = TimeStamp::Now(); + mozilla::TimeDuration duration = nowTime - mStartTimeStamp; + DOMHighResTimeStamp timeStamp = duration.ToMilliseconds(); + + + nsTArray<FrameRequest> callbacks; + callbacks.AppendElements(mFrameRequestCallbacks); + mFrameRequestCallbacks.Clear(); + for (auto& callback : callbacks) { + callback.mCallback->Call(timeStamp); + } +} + +void +VRManagerChild::FireDOMVRDisplayConnectEvent() +{ + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, + &VRManagerChild::FireDOMVRDisplayConnectEventInternal)); +} + +void +VRManagerChild::FireDOMVRDisplayDisconnectEvent() +{ + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, + &VRManagerChild::FireDOMVRDisplayDisconnectEventInternal)); +} + +void +VRManagerChild::FireDOMVRDisplayPresentChangeEvent() +{ + nsContentUtils::AddScriptRunner(NewRunnableMethod(this, + &VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal)); +} + +void +VRManagerChild::FireDOMVRDisplayConnectEventInternal() +{ + for (auto& listener : mListeners) { + listener->NotifyVRDisplayConnect(); + } +} + +void +VRManagerChild::FireDOMVRDisplayDisconnectEventInternal() +{ + for (auto& listener : mListeners) { + listener->NotifyVRDisplayDisconnect(); + } +} + +void +VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal() +{ + for (auto& listener : mListeners) { + listener->NotifyVRDisplayPresentChange(); + } +} + +void +VRManagerChild::AddListener(dom::VREventObserver* aObserver) +{ + MOZ_ASSERT(aObserver); + + if (mListeners.IndexOf(aObserver) != kNoIndex) { + return; // already exists + } + + mListeners.AppendElement(aObserver); + if (mListeners.Length() == 1) { + Unused << SendSetHaveEventListener(true); + } +} + +void +VRManagerChild::RemoveListener(dom::VREventObserver* aObserver) +{ + MOZ_ASSERT(aObserver); + + mListeners.RemoveElement(aObserver); + if (mListeners.IsEmpty()) { + Unused << SendSetHaveEventListener(false); + } +} + +void +VRManagerChild::HandleFatalError(const char* aName, const char* aMsg) const +{ + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid()); +} + +} // namespace gfx +} // namespace mozilla |