diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/layers/ipc | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
90 files changed, 19753 insertions, 0 deletions
diff --git a/gfx/layers/ipc/APZCTreeManagerChild.cpp b/gfx/layers/ipc/APZCTreeManagerChild.cpp new file mode 100644 index 0000000000..3d51aba9f1 --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/APZCTreeManagerChild.h" + +#include "InputData.h" // for InputData +#include "mozilla/dom/BrowserParent.h" // for BrowserParent +#include "mozilla/layers/APZCCallbackHelper.h" // for APZCCallbackHelper +#include "mozilla/layers/APZInputBridgeChild.h" // for APZInputBridgeChild +#include "mozilla/layers/GeckoContentController.h" // for GeckoContentController +#include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/jni/Utils.h" // for DispatchToGeckoPriorityQueue +#endif + +namespace mozilla { +namespace layers { + +APZCTreeManagerChild::APZCTreeManagerChild() + : mCompositorSession(nullptr), mIPCOpen(false) {} + +APZCTreeManagerChild::~APZCTreeManagerChild() = default; + +void APZCTreeManagerChild::SetCompositorSession( + RemoteCompositorSession* aSession) { + // Exactly one of mCompositorSession and aSession must be null (i.e. either + // we're setting mCompositorSession or we're clearing it). + MOZ_ASSERT(!mCompositorSession ^ !aSession); + mCompositorSession = aSession; +} + +void APZCTreeManagerChild::SetInputBridge(APZInputBridgeChild* aInputBridge) { + // The input bridge only exists from the UI process to the GPU process. + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!mInputBridge); + + mInputBridge = aInputBridge; +} + +void APZCTreeManagerChild::Destroy() { + MOZ_ASSERT(NS_IsMainThread()); + if (mInputBridge) { + mInputBridge->Destroy(); + mInputBridge = nullptr; + } +} + +void APZCTreeManagerChild::SetKeyboardMap(const KeyboardMap& aKeyboardMap) { + MOZ_ASSERT(NS_IsMainThread()); + SendSetKeyboardMap(aKeyboardMap); +} + +void APZCTreeManagerChild::ZoomToRect(const ScrollableLayerGuid& aGuid, + const ZoomTarget& aZoomTarget, + const uint32_t aFlags) { + MOZ_ASSERT(NS_IsMainThread()); + SendZoomToRect(aGuid, aZoomTarget, aFlags); +} + +void APZCTreeManagerChild::ContentReceivedInputBlock(uint64_t aInputBlockId, + bool aPreventDefault) { + MOZ_ASSERT(NS_IsMainThread()); + SendContentReceivedInputBlock(aInputBlockId, aPreventDefault); +} + +void APZCTreeManagerChild::SetTargetAPZC( + uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) { + MOZ_ASSERT(NS_IsMainThread()); + SendSetTargetAPZC(aInputBlockId, aTargets); +} + +void APZCTreeManagerChild::UpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe<ZoomConstraints>& aConstraints) { + MOZ_ASSERT(NS_IsMainThread()); + if (mIPCOpen) { + SendUpdateZoomConstraints(aGuid, aConstraints); + } +} + +void APZCTreeManagerChild::SetDPI(float aDpiValue) { + MOZ_ASSERT(NS_IsMainThread()); + SendSetDPI(aDpiValue); +} + +void APZCTreeManagerChild::SetAllowedTouchBehavior( + uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) { + MOZ_ASSERT(NS_IsMainThread()); + SendSetAllowedTouchBehavior(aInputBlockId, aValues); +} + +void APZCTreeManagerChild::SetBrowserGestureResponse( + uint64_t aInputBlockId, BrowserGestureResponse aResponse) { + MOZ_ASSERT(NS_IsMainThread()); + SendSetBrowserGestureResponse(aInputBlockId, aResponse); +} + +void APZCTreeManagerChild::StartScrollbarDrag( + const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) { + MOZ_ASSERT(NS_IsMainThread()); + SendStartScrollbarDrag(aGuid, aDragMetrics); +} + +bool APZCTreeManagerChild::StartAutoscroll(const ScrollableLayerGuid& aGuid, + const ScreenPoint& aAnchorLocation) { + MOZ_ASSERT(NS_IsMainThread()); + return SendStartAutoscroll(aGuid, aAnchorLocation); +} + +void APZCTreeManagerChild::StopAutoscroll(const ScrollableLayerGuid& aGuid) { + MOZ_ASSERT(NS_IsMainThread()); + SendStopAutoscroll(aGuid); +} + +void APZCTreeManagerChild::SetLongTapEnabled(bool aTapGestureEnabled) { + MOZ_ASSERT(NS_IsMainThread()); + SendSetLongTapEnabled(aTapGestureEnabled); +} + +APZInputBridge* APZCTreeManagerChild::InputBridge() { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(mInputBridge); + + return mInputBridge.get(); +} + +void APZCTreeManagerChild::AddIPDLReference() { + MOZ_ASSERT(mIPCOpen == false); + mIPCOpen = true; + AddRef(); +} + +void APZCTreeManagerChild::ReleaseIPDLReference() { + mIPCOpen = false; + Release(); +} + +void APZCTreeManagerChild::ActorDestroy(ActorDestroyReason aWhy) { + mIPCOpen = false; +} + +mozilla::ipc::IPCResult APZCTreeManagerChild::RecvHandleTap( + const TapType& aType, const LayoutDevicePoint& aPoint, + const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + MOZ_ASSERT(XRE_IsParentProcess()); + if (mCompositorSession && + mCompositorSession->RootLayerTreeId() == aGuid.mLayersId && + mCompositorSession->GetContentController()) { + RefPtr<GeckoContentController> controller = + mCompositorSession->GetContentController(); + controller->HandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId); + return IPC_OK(); + } + dom::BrowserParent* tab = + dom::BrowserParent::GetBrowserParentFromLayersId(aGuid.mLayersId); + if (tab) { +#ifdef MOZ_WIDGET_ANDROID + // On Android, touch events are dispatched from the UI thread to the main + // thread using the Android priority queue. It is possible that this tap has + // made it to the GPU process and back before they have been processed. We + // must therefore dispatch this message to the same queue, otherwise the tab + // may receive the tap event before the touch events that synthesized it. + mozilla::jni::DispatchToGeckoPriorityQueue( + NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, + ScrollableLayerGuid, uint64_t>( + "dom::BrowserParent::SendHandleTap", tab, + &dom::BrowserParent::SendHandleTap, aType, aPoint, aModifiers, + aGuid, aInputBlockId)); +#else + tab->SendHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId); +#endif + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerChild::RecvNotifyPinchGesture( + const PinchGestureType& aType, const ScrollableLayerGuid& aGuid, + const LayoutDevicePoint& aFocusPoint, const LayoutDeviceCoord& aSpanChange, + const Modifiers& aModifiers) { + // This will only get sent from the GPU process to the parent process, so + // this function should never get called in the content process. + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + // We want to handle it in this process regardless of what the target guid + // of the pinch is. This may change in the future. + if (mCompositorSession && mCompositorSession->GetWidget()) { + APZCCallbackHelper::NotifyPinchGesture(aType, aFocusPoint, aSpanChange, + aModifiers, + mCompositorSession->GetWidget()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerChild::RecvCancelAutoscroll( + const ScrollableLayerGuid::ViewID& aScrollId) { + // This will only get sent from the GPU process to the parent process, so + // this function should never get called in the content process. + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + APZCCallbackHelper::CancelAutoscroll(aScrollId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerChild::RecvNotifyScaleGestureComplete( + const ScrollableLayerGuid::ViewID& aScrollId, float aScale) { + // This will only get sent from the GPU process to the parent process, so + // this function should never get called in the content process. + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (mCompositorSession && mCompositorSession->GetWidget()) { + APZCCallbackHelper::NotifyScaleGestureComplete( + mCompositorSession->GetWidget(), aScale); + } + return IPC_OK(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/APZCTreeManagerChild.h b/gfx/layers/ipc/APZCTreeManagerChild.h new file mode 100644 index 0000000000..b92e322dea --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerChild.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_APZCTreeManagerChild_h +#define mozilla_layers_APZCTreeManagerChild_h + +#include "mozilla/layers/APZInputBridge.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "mozilla/layers/PAPZCTreeManagerChild.h" + +#include <unordered_map> + +namespace mozilla { +namespace layers { + +class APZInputBridgeChild; +class RemoteCompositorSession; + +class APZCTreeManagerChild : public IAPZCTreeManager, + public PAPZCTreeManagerChild { + friend class PAPZCTreeManagerChild; + using TapType = GeckoContentController_TapType; + + public: + APZCTreeManagerChild(); + + void SetCompositorSession(RemoteCompositorSession* aSession); + void SetInputBridge(APZInputBridgeChild* aInputBridge); + void Destroy(); + + void SetKeyboardMap(const KeyboardMap& aKeyboardMap) override; + + void ZoomToRect(const ScrollableLayerGuid& aGuid, + const ZoomTarget& aZoomTarget, + const uint32_t aFlags = DEFAULT_BEHAVIOR) override; + + void ContentReceivedInputBlock(uint64_t aInputBlockId, + bool aPreventDefault) override; + + void SetTargetAPZC(uint64_t aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) override; + + void UpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe<ZoomConstraints>& aConstraints) override; + + void SetDPI(float aDpiValue) override; + + void SetAllowedTouchBehavior( + uint64_t aInputBlockId, + const nsTArray<TouchBehaviorFlags>& aValues) override; + + void SetBrowserGestureResponse(uint64_t aInputBlockId, + BrowserGestureResponse aResponse) override; + + void StartScrollbarDrag(const ScrollableLayerGuid& aGuid, + const AsyncDragMetrics& aDragMetrics) override; + + bool StartAutoscroll(const ScrollableLayerGuid& aGuid, + const ScreenPoint& aAnchorLocation) override; + + void StopAutoscroll(const ScrollableLayerGuid& aGuid) override; + + void SetLongTapEnabled(bool aTapGestureEnabled) override; + + APZInputBridge* InputBridge() override; + + void AddIPDLReference(); + void ReleaseIPDLReference(); + void ActorDestroy(ActorDestroyReason aWhy) override; + + protected: + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvHandleTap(const TapType& aType, + const LayoutDevicePoint& aPoint, + const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + + mozilla::ipc::IPCResult RecvNotifyPinchGesture( + const PinchGestureType& aType, const ScrollableLayerGuid& aGuid, + const LayoutDevicePoint& aFocusPoint, + const LayoutDeviceCoord& aSpanChange, const Modifiers& aModifiers); + + mozilla::ipc::IPCResult RecvCancelAutoscroll( + const ScrollableLayerGuid::ViewID& aScrollId); + + mozilla::ipc::IPCResult RecvNotifyScaleGestureComplete( + const ScrollableLayerGuid::ViewID& aScrollId, float aScale); + + virtual ~APZCTreeManagerChild(); + + private: + MOZ_NON_OWNING_REF RemoteCompositorSession* mCompositorSession; + RefPtr<APZInputBridgeChild> mInputBridge; + bool mIPCOpen; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_APZCTreeManagerChild_h diff --git a/gfx/layers/ipc/APZCTreeManagerParent.cpp b/gfx/layers/ipc/APZCTreeManagerParent.cpp new file mode 100644 index 0000000000..ff05acd083 --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp @@ -0,0 +1,199 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/APZCTreeManagerParent.h" + +#include "apz/src/APZCTreeManager.h" +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layers/APZUpdater.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace layers { + +APZCTreeManagerParent::APZCTreeManagerParent( + LayersId aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager, + RefPtr<APZUpdater> aAPZUpdater) + : mLayersId(aLayersId), + mTreeManager(std::move(aAPZCTreeManager)), + mUpdater(std::move(aAPZUpdater)) { + MOZ_ASSERT(mTreeManager != nullptr); + MOZ_ASSERT(mUpdater != nullptr); + MOZ_ASSERT(mUpdater->HasTreeManager(mTreeManager)); +} + +APZCTreeManagerParent::~APZCTreeManagerParent() = default; + +void APZCTreeManagerParent::ChildAdopted( + RefPtr<APZCTreeManager> aAPZCTreeManager, RefPtr<APZUpdater> aAPZUpdater) { + MOZ_ASSERT(aAPZCTreeManager != nullptr); + MOZ_ASSERT(aAPZUpdater != nullptr); + MOZ_ASSERT(aAPZUpdater->HasTreeManager(aAPZCTreeManager)); + mTreeManager = std::move(aAPZCTreeManager); + mUpdater = std::move(aAPZUpdater); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetKeyboardMap( + const KeyboardMap& aKeyboardMap) { + mUpdater->RunOnUpdaterThread( + mLayersId, NewRunnableMethod<KeyboardMap>( + "layers::IAPZCTreeManager::SetKeyboardMap", mTreeManager, + &IAPZCTreeManager::SetKeyboardMap, aKeyboardMap)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvZoomToRect( + const ScrollableLayerGuid& aGuid, const ZoomTarget& aZoomTarget, + const uint32_t& aFlags) { + if (!IsGuidValid(aGuid)) { + return IPC_FAIL_NO_REASON(this); + } + + mUpdater->RunOnUpdaterThread( + aGuid.mLayersId, + NewRunnableMethod<ScrollableLayerGuid, ZoomTarget, uint32_t>( + "layers::IAPZCTreeManager::ZoomToRect", mTreeManager, + &IAPZCTreeManager::ZoomToRect, aGuid, aZoomTarget, aFlags)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvContentReceivedInputBlock( + const uint64_t& aInputBlockId, const bool& aPreventDefault) { + mUpdater->RunOnUpdaterThread( + mLayersId, NewRunnableMethod<uint64_t, bool>( + "layers::IAPZCTreeManager::ContentReceivedInputBlock", + mTreeManager, &IAPZCTreeManager::ContentReceivedInputBlock, + aInputBlockId, aPreventDefault)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetTargetAPZC( + const uint64_t& aInputBlockId, nsTArray<ScrollableLayerGuid>&& aTargets) { + mUpdater->RunOnUpdaterThread( + mLayersId, + NewRunnableMethod<uint64_t, + StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>( + "layers::IAPZCTreeManager::SetTargetAPZC", mTreeManager, + &IAPZCTreeManager::SetTargetAPZC, aInputBlockId, + std::move(aTargets))); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvUpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe<ZoomConstraints>& aConstraints) { + if (!IsGuidValid(aGuid)) { + return IPC_FAIL_NO_REASON(this); + } + + mTreeManager->UpdateZoomConstraints(aGuid, aConstraints); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetDPI( + const float& aDpiValue) { + mUpdater->RunOnUpdaterThread( + mLayersId, + NewRunnableMethod<float>("layers::IAPZCTreeManager::SetDPI", mTreeManager, + &IAPZCTreeManager::SetDPI, aDpiValue)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetAllowedTouchBehavior( + const uint64_t& aInputBlockId, nsTArray<TouchBehaviorFlags>&& aValues) { + mUpdater->RunOnUpdaterThread( + mLayersId, + NewRunnableMethod<uint64_t, + StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>( + "layers::IAPZCTreeManager::SetAllowedTouchBehavior", mTreeManager, + &IAPZCTreeManager::SetAllowedTouchBehavior, aInputBlockId, + std::move(aValues))); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetBrowserGestureResponse( + const uint64_t& aInputBlockId, const BrowserGestureResponse& aResponse) { + mUpdater->RunOnUpdaterThread( + mLayersId, NewRunnableMethod<uint64_t, BrowserGestureResponse>( + "layers::IAPZCTreeManager::SetBrowserGestureResponse", + mTreeManager, &IAPZCTreeManager::SetBrowserGestureResponse, + aInputBlockId, aResponse)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartScrollbarDrag( + const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) { + if (!IsGuidValid(aGuid)) { + return IPC_FAIL_NO_REASON(this); + } + + mUpdater->RunOnUpdaterThread( + aGuid.mLayersId, + NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>( + "layers::IAPZCTreeManager::StartScrollbarDrag", mTreeManager, + &IAPZCTreeManager::StartScrollbarDrag, aGuid, aDragMetrics)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartAutoscroll( + const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation) { + // Unlike RecvStartScrollbarDrag(), this message comes from the parent + // process (via nsBaseWidget::mAPZC) rather than from the child process + // (via BrowserChild::mApzcTreeManager), so there is no need to check the + // layers id against mLayersId (and in any case, it wouldn't match, because + // mLayersId stores the parent process's layers id, while nsBaseWidget is + // sending the child process's layers id). + + mUpdater->RunOnControllerThread( + mLayersId, + NewRunnableMethod<ScrollableLayerGuid, ScreenPoint>( + "layers::IAPZCTreeManager::StartAutoscroll", mTreeManager, + &IAPZCTreeManager::StartAutoscroll, aGuid, aAnchorLocation)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStopAutoscroll( + const ScrollableLayerGuid& aGuid) { + // See RecvStartAutoscroll() for why we don't check the layers id. + + mUpdater->RunOnControllerThread( + mLayersId, NewRunnableMethod<ScrollableLayerGuid>( + "layers::IAPZCTreeManager::StopAutoscroll", mTreeManager, + &IAPZCTreeManager::StopAutoscroll, aGuid)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetLongTapEnabled( + const bool& aLongTapEnabled) { + mUpdater->RunOnUpdaterThread( + mLayersId, + NewRunnableMethod<bool>( + "layers::IAPZCTreeManager::SetLongTapEnabled", mTreeManager, + &IAPZCTreeManager::SetLongTapEnabled, aLongTapEnabled)); + + return IPC_OK(); +} + +bool APZCTreeManagerParent::IsGuidValid(const ScrollableLayerGuid& aGuid) { + if (aGuid.mLayersId != mLayersId) { + NS_ERROR("Unexpected layers id"); + return false; + } + return true; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/APZCTreeManagerParent.h b/gfx/layers/ipc/APZCTreeManagerParent.h new file mode 100644 index 0000000000..c75091c796 --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerParent.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_APZCTreeManagerParent_h +#define mozilla_layers_APZCTreeManagerParent_h + +#include "mozilla/layers/PAPZCTreeManagerParent.h" + +namespace mozilla { +namespace layers { + +class APZCTreeManager; +class APZUpdater; + +class APZCTreeManagerParent : public PAPZCTreeManagerParent { + public: + APZCTreeManagerParent(LayersId aLayersId, + RefPtr<APZCTreeManager> aAPZCTreeManager, + RefPtr<APZUpdater> mAPZUpdater); + virtual ~APZCTreeManagerParent(); + + LayersId GetLayersId() const { return mLayersId; } + + /** + * Called when the layer tree that this protocol is connected to + * is adopted by another compositor, and we need to switch APZCTreeManagers. + */ + void ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager, + RefPtr<APZUpdater> aAPZUpdater); + + mozilla::ipc::IPCResult RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap); + + mozilla::ipc::IPCResult RecvZoomToRect(const ScrollableLayerGuid& aGuid, + const ZoomTarget& aZoomTarget, + const uint32_t& aFlags); + + mozilla::ipc::IPCResult RecvContentReceivedInputBlock( + const uint64_t& aInputBlockId, const bool& aPreventDefault); + + mozilla::ipc::IPCResult RecvSetTargetAPZC( + const uint64_t& aInputBlockId, nsTArray<ScrollableLayerGuid>&& aTargets); + + mozilla::ipc::IPCResult RecvUpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe<ZoomConstraints>& aConstraints); + + mozilla::ipc::IPCResult RecvSetDPI(const float& aDpiValue); + + mozilla::ipc::IPCResult RecvSetAllowedTouchBehavior( + const uint64_t& aInputBlockId, nsTArray<TouchBehaviorFlags>&& aValues); + + mozilla::ipc::IPCResult RecvSetBrowserGestureResponse( + const uint64_t& aInputBlockId, const BrowserGestureResponse& aResponse); + + mozilla::ipc::IPCResult RecvStartScrollbarDrag( + const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics); + + mozilla::ipc::IPCResult RecvStartAutoscroll( + const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation); + + mozilla::ipc::IPCResult RecvStopAutoscroll(const ScrollableLayerGuid& aGuid); + + mozilla::ipc::IPCResult RecvSetLongTapEnabled(const bool& aTapGestureEnabled); + + void ActorDestroy(ActorDestroyReason aWhy) override {} + + private: + bool IsGuidValid(const ScrollableLayerGuid& aGuid); + + LayersId mLayersId; + RefPtr<APZCTreeManager> mTreeManager; + RefPtr<APZUpdater> mUpdater; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_APZCTreeManagerParent_h diff --git a/gfx/layers/ipc/APZChild.cpp b/gfx/layers/ipc/APZChild.cpp new file mode 100644 index 0000000000..00146ac59a --- /dev/null +++ b/gfx/layers/ipc/APZChild.cpp @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/APZChild.h" +#include "mozilla/layers/GeckoContentController.h" + +#include "mozilla/dom/BrowserChild.h" +#include "mozilla/layers/APZCCallbackHelper.h" + +#include "InputData.h" // for InputData + +namespace mozilla { +namespace layers { + +APZChild::APZChild(RefPtr<GeckoContentController> aController) + : mController(aController) { + MOZ_ASSERT(mController); +} + +APZChild::~APZChild() { + if (mAPZTaskRunnable) { + mAPZTaskRunnable->Revoke(); + mAPZTaskRunnable = nullptr; + } + if (mController) { + mController->Destroy(); + mController = nullptr; + } +} + +mozilla::ipc::IPCResult APZChild::RecvLayerTransforms( + nsTArray<MatrixMessage>&& aTransforms) { + mController->NotifyLayerTransforms(std::move(aTransforms)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvRequestContentRepaint( + const RepaintRequest& aRequest) { + MOZ_ASSERT(mController->IsRepaintThread()); + + EnsureAPZTaskRunnable(); + + mAPZTaskRunnable->QueueRequest(aRequest); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvUpdateOverscrollVelocity( + const ScrollableLayerGuid& aGuid, const float& aX, const float& aY, + const bool& aIsRootContent) { + mController->UpdateOverscrollVelocity(aGuid, aX, aY, aIsRootContent); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvUpdateOverscrollOffset( + const ScrollableLayerGuid& aGuid, const float& aX, const float& aY, + const bool& aIsRootContent) { + mController->UpdateOverscrollOffset(aGuid, aX, aY, aIsRootContent); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvNotifyMozMouseScrollEvent( + const ViewID& aScrollId, const nsString& aEvent) { + mController->NotifyMozMouseScrollEvent(aScrollId, aEvent); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvNotifyAPZStateChange( + const ScrollableLayerGuid& aGuid, const APZStateChange& aChange, + const int& aArg, Maybe<uint64_t> aInputBlockId) { + mController->NotifyAPZStateChange(aGuid, aChange, aArg, aInputBlockId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvNotifyFlushComplete() { + MOZ_ASSERT(mController->IsRepaintThread()); + EnsureAPZTaskRunnable(); + + mAPZTaskRunnable->QueueFlushCompleteNotification(); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvNotifyAsyncScrollbarDragInitiated( + const uint64_t& aDragBlockId, const ViewID& aScrollId, + const ScrollDirection& aDirection) { + mController->NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId, + aDirection); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvNotifyAsyncScrollbarDragRejected( + const ViewID& aScrollId) { + mController->NotifyAsyncScrollbarDragRejected(aScrollId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvNotifyAsyncAutoscrollRejected( + const ViewID& aScrollId) { + mController->NotifyAsyncAutoscrollRejected(aScrollId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvDestroy() { + // mController->Destroy will be called in the destructor + PAPZChild::Send__delete__(this); + return IPC_OK(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/APZChild.h b/gfx/layers/ipc/APZChild.h new file mode 100644 index 0000000000..dfb3216127 --- /dev/null +++ b/gfx/layers/ipc/APZChild.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_APZChild_h +#define mozilla_layers_APZChild_h + +#include "mozilla/layers/PAPZChild.h" +#include "mozilla/layers/APZTaskRunnable.h" + +namespace mozilla { +namespace layers { + +class GeckoContentController; + +/** + * APZChild implements PAPZChild and is used to remote a GeckoContentController + * that lives in a different process than where APZ lives. + */ +class APZChild final : public PAPZChild { + public: + using APZStateChange = GeckoContentController_APZStateChange; + + explicit APZChild(RefPtr<GeckoContentController> aController); + virtual ~APZChild(); + + mozilla::ipc::IPCResult RecvLayerTransforms( + nsTArray<MatrixMessage>&& aTransforms); + + mozilla::ipc::IPCResult RecvRequestContentRepaint( + const RepaintRequest& aRequest); + + mozilla::ipc::IPCResult RecvUpdateOverscrollVelocity( + const ScrollableLayerGuid& aGuid, const float& aX, const float& aY, + const bool& aIsRootContent); + + mozilla::ipc::IPCResult RecvUpdateOverscrollOffset( + const ScrollableLayerGuid& aGuid, const float& aX, const float& aY, + const bool& aIsRootContent); + + mozilla::ipc::IPCResult RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId, + const nsString& aEvent); + + mozilla::ipc::IPCResult RecvNotifyAPZStateChange( + const ScrollableLayerGuid& aGuid, const APZStateChange& aChange, + const int& aArg, Maybe<uint64_t> aInputBlockId); + + mozilla::ipc::IPCResult RecvNotifyFlushComplete(); + + mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragInitiated( + const uint64_t& aDragBlockId, const ViewID& aScrollId, + const ScrollDirection& aDirection); + mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragRejected( + const ViewID& aScrollId); + + mozilla::ipc::IPCResult RecvNotifyAsyncAutoscrollRejected( + const ViewID& aScrollId); + + mozilla::ipc::IPCResult RecvDestroy(); + + private: + void EnsureAPZTaskRunnable() { + if (!mAPZTaskRunnable) { + mAPZTaskRunnable = new APZTaskRunnable(mController); + } + } + + RefPtr<GeckoContentController> mController; + // A runnable invoked in a nsRefreshDriver's tick to update multiple + // RepaintRequests and notify a "apz-repaints-flushed" at the same time. + RefPtr<APZTaskRunnable> mAPZTaskRunnable; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_APZChild_h diff --git a/gfx/layers/ipc/APZInputBridgeChild.cpp b/gfx/layers/ipc/APZInputBridgeChild.cpp new file mode 100644 index 0000000000..bf059143ec --- /dev/null +++ b/gfx/layers/ipc/APZInputBridgeChild.cpp @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/APZInputBridgeChild.h" + +#include "InputData.h" // for InputData, etc +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layers/SynchronousTask.h" + +namespace mozilla { +namespace layers { + +/* static */ +RefPtr<APZInputBridgeChild> APZInputBridgeChild::Create( + const uint64_t& aProcessToken, Endpoint<PAPZInputBridgeChild>&& aEndpoint) { + RefPtr<APZInputBridgeChild> child = new APZInputBridgeChild(aProcessToken); + + MOZ_ASSERT(APZThreadUtils::IsControllerThreadAlive()); + + APZThreadUtils::RunOnControllerThread( + NewRunnableMethod<Endpoint<PAPZInputBridgeChild>&&>( + "layers::APZInputBridgeChild::Open", child, + &APZInputBridgeChild::Open, std::move(aEndpoint))); + + return child; +} + +APZInputBridgeChild::APZInputBridgeChild(const uint64_t& aProcessToken) + : mIsOpen(false), mProcessToken(aProcessToken) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); +} + +APZInputBridgeChild::~APZInputBridgeChild() = default; + +void APZInputBridgeChild::Open(Endpoint<PAPZInputBridgeChild>&& aEndpoint) { + APZThreadUtils::AssertOnControllerThread(); + + mIsOpen = aEndpoint.Bind(this); + + if (!mIsOpen) { + // The GPU Process Manager might be gone if we receive ActorDestroy very + // late in shutdown. + if (gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get()) { + gpm->NotifyRemoteActorDestroyed(mProcessToken); + } + return; + } +} + +void APZInputBridgeChild::Destroy() { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + // Destroy will get called from the main thread, so we must synchronously + // dispatch to the controller thread to close the bridge. + layers::SynchronousTask task("layers::APZInputBridgeChild::Destroy"); + APZThreadUtils::RunOnControllerThread( + NS_NewRunnableFunction("layers::APZInputBridgeChild::Destroy", [&]() { + APZThreadUtils::AssertOnControllerThread(); + AutoCompleteTask complete(&task); + + // Clear the process token so that we don't notify the GPUProcessManager + // about an abnormal shutdown, thereby tearing down the GPU process. + mProcessToken = 0; + + if (mIsOpen) { + PAPZInputBridgeChild::Close(); + mIsOpen = false; + } + })); + + task.Wait(); +} + +void APZInputBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { + mIsOpen = false; + + if (mProcessToken) { + gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); + mProcessToken = 0; + } +} + +APZEventResult APZInputBridgeChild::ReceiveInputEvent( + InputData& aEvent, InputBlockCallback&& aCallback) { + MOZ_ASSERT(mIsOpen); + APZThreadUtils::AssertOnControllerThread(); + + APZEventResult res; + switch (aEvent.mInputType) { + case MULTITOUCH_INPUT: { + MultiTouchInput& event = aEvent.AsMultiTouchInput(); + MultiTouchInput processedEvent; + + SendReceiveMultiTouchInputEvent(event, !!aCallback, &res, + &processedEvent); + + event = processedEvent; + break; + } + case MOUSE_INPUT: { + MouseInput& event = aEvent.AsMouseInput(); + MouseInput processedEvent; + + SendReceiveMouseInputEvent(event, !!aCallback, &res, &processedEvent); + + event = processedEvent; + break; + } + case PANGESTURE_INPUT: { + PanGestureInput& event = aEvent.AsPanGestureInput(); + PanGestureInput processedEvent; + + SendReceivePanGestureInputEvent(event, !!aCallback, &res, + &processedEvent); + + event = processedEvent; + break; + } + case PINCHGESTURE_INPUT: { + PinchGestureInput& event = aEvent.AsPinchGestureInput(); + PinchGestureInput processedEvent; + + SendReceivePinchGestureInputEvent(event, !!aCallback, &res, + &processedEvent); + + event = processedEvent; + break; + } + case TAPGESTURE_INPUT: { + TapGestureInput& event = aEvent.AsTapGestureInput(); + TapGestureInput processedEvent; + + SendReceiveTapGestureInputEvent(event, !!aCallback, &res, + &processedEvent); + + event = processedEvent; + break; + } + case SCROLLWHEEL_INPUT: { + ScrollWheelInput& event = aEvent.AsScrollWheelInput(); + ScrollWheelInput processedEvent; + + SendReceiveScrollWheelInputEvent(event, !!aCallback, &res, + &processedEvent); + + event = processedEvent; + break; + } + case KEYBOARD_INPUT: { + KeyboardInput& event = aEvent.AsKeyboardInput(); + KeyboardInput processedEvent; + + SendReceiveKeyboardInputEvent(event, !!aCallback, &res, &processedEvent); + + event = processedEvent; + break; + } + default: { + MOZ_ASSERT_UNREACHABLE("Invalid InputData type."); + res.SetStatusAsConsumeNoDefault(); + break; + } + } + + if (aCallback && res.WillHaveDelayedResult()) { + mInputBlockCallbacks.emplace(res.mInputBlockId, std::move(aCallback)); + } + + return res; +} + +mozilla::ipc::IPCResult APZInputBridgeChild::RecvCallInputBlockCallback( + uint64_t aInputBlockId, const APZHandledResult& aHandledResult) { + auto it = mInputBlockCallbacks.find(aInputBlockId); + if (it != mInputBlockCallbacks.end()) { + it->second(aInputBlockId, aHandledResult); + // The callback is one-shot; discard it after calling it. + mInputBlockCallbacks.erase(it); + } + + return IPC_OK(); +} + +void APZInputBridgeChild::ProcessUnhandledEvent( + LayoutDeviceIntPoint* aRefPoint, ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutFocusSequenceNumber, LayersId* aOutLayersId) { + MOZ_ASSERT(mIsOpen); + APZThreadUtils::AssertOnControllerThread(); + + SendProcessUnhandledEvent(*aRefPoint, aRefPoint, aOutTargetGuid, + aOutFocusSequenceNumber, aOutLayersId); +} + +void APZInputBridgeChild::UpdateWheelTransaction( + LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage, + const Maybe<ScrollableLayerGuid>& aTargetGuid) { + MOZ_ASSERT(mIsOpen); + APZThreadUtils::AssertOnControllerThread(); + + SendUpdateWheelTransaction(aRefPoint, aEventMessage, aTargetGuid); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/APZInputBridgeChild.h b/gfx/layers/ipc/APZInputBridgeChild.h new file mode 100644 index 0000000000..dd77d95f36 --- /dev/null +++ b/gfx/layers/ipc/APZInputBridgeChild.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_APZInputBridgeChild_h +#define mozilla_layers_APZInputBridgeChild_h + +#include "mozilla/layers/APZInputBridge.h" +#include "mozilla/layers/PAPZInputBridgeChild.h" + +namespace mozilla { +namespace layers { + +class APZInputBridgeChild : public PAPZInputBridgeChild, public APZInputBridge { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZInputBridgeChild, final) + + public: + static RefPtr<APZInputBridgeChild> Create( + const uint64_t& aProcessToken, + Endpoint<PAPZInputBridgeChild>&& aEndpoint); + + void Destroy(); + + APZEventResult ReceiveInputEvent( + InputData& aEvent, + InputBlockCallback&& aCallback = InputBlockCallback()) override; + + mozilla::ipc::IPCResult RecvCallInputBlockCallback( + uint64_t aInputBlockId, const APZHandledResult& handledResult); + + protected: + void ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutFocusSequenceNumber, + LayersId* aOutLayersId) override; + + void UpdateWheelTransaction( + LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage, + const Maybe<ScrollableLayerGuid>& aTargetGuid) override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + + explicit APZInputBridgeChild(const uint64_t& aProcessToken); + virtual ~APZInputBridgeChild(); + + private: + void Open(Endpoint<PAPZInputBridgeChild>&& aEndpoint); + + bool mIsOpen; + uint64_t mProcessToken; + + using InputBlockCallbackMap = + std::unordered_map<uint64_t, InputBlockCallback>; + InputBlockCallbackMap mInputBlockCallbacks; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_APZInputBridgeChild_h diff --git a/gfx/layers/ipc/APZInputBridgeParent.cpp b/gfx/layers/ipc/APZInputBridgeParent.cpp new file mode 100644 index 0000000000..fcc642ff7a --- /dev/null +++ b/gfx/layers/ipc/APZInputBridgeParent.cpp @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/APZInputBridgeParent.h" + +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/APZInputBridge.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "InputData.h" + +namespace mozilla { +namespace layers { + +/* static */ +RefPtr<APZInputBridgeParent> APZInputBridgeParent::Create( + const LayersId& aLayersId, Endpoint<PAPZInputBridgeParent>&& aEndpoint) { + RefPtr<APZInputBridgeParent> parent = new APZInputBridgeParent(aLayersId); + if (!aEndpoint.Bind(parent)) { + // We can't recover from this. + MOZ_CRASH("Failed to bind APZInputBridgeParent to endpoint"); + } + + return parent; +} + +APZInputBridgeParent::APZInputBridgeParent(const LayersId& aLayersId) { + MOZ_ASSERT(XRE_IsGPUProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + mTreeManager = CompositorBridgeParent::GetAPZCTreeManager(aLayersId); + MOZ_ASSERT(mTreeManager); +} + +APZInputBridgeParent::~APZInputBridgeParent() = default; + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveMultiTouchInputEvent( + const MultiTouchInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, MultiTouchInput* aOutEvent) { + MultiTouchInput event = aEvent; + + APZInputBridge::InputBlockCallback callback; + if (aWantsCallback) { + callback = [self = RefPtr<APZInputBridgeParent>(this)]( + uint64_t aInputBlockId, + const APZHandledResult& aHandledResult) { + Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult); + }; + } + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent( + event, std::move(callback)); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveMouseInputEvent( + const MouseInput& aEvent, bool aWantsCallback, APZEventResult* aOutResult, + MouseInput* aOutEvent) { + MouseInput event = aEvent; + + APZInputBridge::InputBlockCallback callback; + if (aWantsCallback) { + callback = [self = RefPtr<APZInputBridgeParent>(this)]( + uint64_t aInputBlockId, + const APZHandledResult& aHandledResult) { + Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult); + }; + } + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent( + event, std::move(callback)); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceivePanGestureInputEvent( + const PanGestureInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, PanGestureInput* aOutEvent) { + PanGestureInput event = aEvent; + + APZInputBridge::InputBlockCallback callback; + if (aWantsCallback) { + callback = [self = RefPtr<APZInputBridgeParent>(this)]( + uint64_t aInputBlockId, + const APZHandledResult& aHandledResult) { + Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult); + }; + } + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent( + event, std::move(callback)); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceivePinchGestureInputEvent( + const PinchGestureInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, PinchGestureInput* aOutEvent) { + PinchGestureInput event = aEvent; + + APZInputBridge::InputBlockCallback callback; + if (aWantsCallback) { + callback = [self = RefPtr<APZInputBridgeParent>(this)]( + uint64_t aInputBlockId, + const APZHandledResult& aHandledResult) { + Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult); + }; + } + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent( + event, std::move(callback)); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveTapGestureInputEvent( + const TapGestureInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, TapGestureInput* aOutEvent) { + TapGestureInput event = aEvent; + + APZInputBridge::InputBlockCallback callback; + if (aWantsCallback) { + callback = [self = RefPtr<APZInputBridgeParent>(this)]( + uint64_t aInputBlockId, + const APZHandledResult& aHandledResult) { + Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult); + }; + } + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent( + event, std::move(callback)); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveScrollWheelInputEvent( + const ScrollWheelInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, ScrollWheelInput* aOutEvent) { + ScrollWheelInput event = aEvent; + + APZInputBridge::InputBlockCallback callback; + if (aWantsCallback) { + callback = [self = RefPtr<APZInputBridgeParent>(this)]( + uint64_t aInputBlockId, + const APZHandledResult& aHandledResult) { + Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult); + }; + } + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent( + event, std::move(callback)); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveKeyboardInputEvent( + const KeyboardInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, KeyboardInput* aOutEvent) { + KeyboardInput event = aEvent; + + APZInputBridge::InputBlockCallback callback; + if (aWantsCallback) { + callback = [self = RefPtr<APZInputBridgeParent>(this)]( + uint64_t aInputBlockId, + const APZHandledResult& aHandledResult) { + Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult); + }; + } + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent( + event, std::move(callback)); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvUpdateWheelTransaction( + const LayoutDeviceIntPoint& aRefPoint, const EventMessage& aEventMessage, + const Maybe<ScrollableLayerGuid>& aTargetGuid) { + mTreeManager->InputBridge()->UpdateWheelTransaction(aRefPoint, aEventMessage, + aTargetGuid); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvProcessUnhandledEvent( + const LayoutDeviceIntPoint& aRefPoint, LayoutDeviceIntPoint* aOutRefPoint, + ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutFocusSequenceNumber, + LayersId* aOutLayersId) { + LayoutDeviceIntPoint refPoint = aRefPoint; + mTreeManager->InputBridge()->ProcessUnhandledEvent( + &refPoint, aOutTargetGuid, aOutFocusSequenceNumber, aOutLayersId); + *aOutRefPoint = refPoint; + + return IPC_OK(); +} + +void APZInputBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { + // We shouldn't need it after this + mTreeManager = nullptr; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/APZInputBridgeParent.h b/gfx/layers/ipc/APZInputBridgeParent.h new file mode 100644 index 0000000000..71f43d092b --- /dev/null +++ b/gfx/layers/ipc/APZInputBridgeParent.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_APZInputBridgeParent_h +#define mozilla_layers_APZInputBridgeParent_h + +#include "mozilla/layers/PAPZInputBridgeParent.h" + +namespace mozilla { +namespace layers { + +class IAPZCTreeManager; + +class APZInputBridgeParent : public PAPZInputBridgeParent { + NS_INLINE_DECL_REFCOUNTING(APZInputBridgeParent, final) + + public: + static RefPtr<APZInputBridgeParent> Create( + const LayersId& aLayersId, Endpoint<PAPZInputBridgeParent>&& aEndpoint); + + mozilla::ipc::IPCResult RecvReceiveMultiTouchInputEvent( + const MultiTouchInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, MultiTouchInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceiveMouseInputEvent(const MouseInput& aEvent, + bool aWantsCallback, + APZEventResult* aOutResult, + MouseInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceivePanGestureInputEvent( + const PanGestureInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, PanGestureInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceivePinchGestureInputEvent( + const PinchGestureInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, PinchGestureInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceiveTapGestureInputEvent( + const TapGestureInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, TapGestureInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceiveScrollWheelInputEvent( + const ScrollWheelInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, ScrollWheelInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceiveKeyboardInputEvent( + const KeyboardInput& aEvent, bool aWantsCallback, + APZEventResult* aOutResult, KeyboardInput* aOutEvent); + + mozilla::ipc::IPCResult RecvUpdateWheelTransaction( + const LayoutDeviceIntPoint& aRefPoint, const EventMessage& aEventMessage, + const Maybe<ScrollableLayerGuid>& aTargetGuid); + + mozilla::ipc::IPCResult RecvProcessUnhandledEvent( + const LayoutDeviceIntPoint& aRefPoint, LayoutDeviceIntPoint* aOutRefPoint, + ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutFocusSequenceNumber, + LayersId* aOutLayersId); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + protected: + explicit APZInputBridgeParent(const LayersId& aLayersId); + virtual ~APZInputBridgeParent(); + + private: + RefPtr<IAPZCTreeManager> mTreeManager; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_APZInputBridgeParent_h diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp new file mode 100644 index 0000000000..2f8464402d --- /dev/null +++ b/gfx/layers/ipc/CanvasChild.cpp @@ -0,0 +1,345 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "CanvasChild.h" + +#include "MainThreadUtils.h" +#include "mozilla/gfx/DrawTargetRecording.h" +#include "mozilla/gfx/Tools.h" +#include "mozilla/gfx/Rect.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/ipc/ProcessChild.h" +#include "mozilla/layers/CanvasDrawEventRecorder.h" +#include "nsIObserverService.h" +#include "RecordedCanvasEventImpl.h" + +namespace mozilla { +namespace layers { + +class RingBufferWriterServices final + : public CanvasEventRingBuffer::WriterServices { + public: + explicit RingBufferWriterServices(RefPtr<CanvasChild> aCanvasChild) + : mCanvasChild(std::move(aCanvasChild)) {} + + ~RingBufferWriterServices() final = default; + + bool ReaderClosed() final { + return !mCanvasChild->GetIPCChannel()->CanSend() || + ipc::ProcessChild::ExpectingShutdown(); + } + + void ResumeReader() final { mCanvasChild->ResumeTranslation(); } + + private: + RefPtr<CanvasChild> mCanvasChild; +}; + +class SourceSurfaceCanvasRecording final : public gfx::SourceSurface { + public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording, final) + + SourceSurfaceCanvasRecording( + const RefPtr<gfx::SourceSurface>& aRecordedSuface, + CanvasChild* aCanvasChild, + const RefPtr<CanvasDrawEventRecorder>& aRecorder) + : mRecordedSurface(aRecordedSuface), + mCanvasChild(aCanvasChild), + mRecorder(aRecorder) { + // It's important that AddStoredObject is called first because that will + // run any pending processing required by recorded objects that have been + // deleted off the main thread. + mRecorder->AddStoredObject(this); + mRecorder->RecordEvent(RecordedAddSurfaceAlias(this, aRecordedSuface)); + } + + ~SourceSurfaceCanvasRecording() { + ReferencePtr surfaceAlias = this; + if (NS_IsMainThread()) { + ReleaseOnMainThread(std::move(mRecorder), surfaceAlias, + std::move(mRecordedSurface), std::move(mCanvasChild)); + return; + } + + mRecorder->AddPendingDeletion( + [recorder = std::move(mRecorder), surfaceAlias, + aliasedSurface = std::move(mRecordedSurface), + canvasChild = std::move(mCanvasChild)]() mutable -> void { + ReleaseOnMainThread(std::move(recorder), surfaceAlias, + std::move(aliasedSurface), + std::move(canvasChild)); + }); + } + + gfx::SurfaceType GetType() const final { return mRecordedSurface->GetType(); } + + gfx::IntSize GetSize() const final { return mRecordedSurface->GetSize(); } + + gfx::SurfaceFormat GetFormat() const final { + return mRecordedSurface->GetFormat(); + } + + already_AddRefed<gfx::DataSourceSurface> GetDataSurface() final { + EnsureDataSurfaceOnMainThread(); + return do_AddRef(mDataSourceSurface); + } + + private: + void EnsureDataSurfaceOnMainThread() { + // The data can only be retrieved on the main thread. + if (!mDataSourceSurface && NS_IsMainThread()) { + mDataSourceSurface = mCanvasChild->GetDataSurface(mRecordedSurface); + } + } + + // Used to ensure that clean-up that requires it is done on the main thread. + static void ReleaseOnMainThread(RefPtr<CanvasDrawEventRecorder> aRecorder, + ReferencePtr aSurfaceAlias, + RefPtr<gfx::SourceSurface> aAliasedSurface, + RefPtr<CanvasChild> aCanvasChild) { + MOZ_ASSERT(NS_IsMainThread()); + + aRecorder->RemoveStoredObject(aSurfaceAlias); + aRecorder->RecordEvent(RecordedRemoveSurfaceAlias(aSurfaceAlias)); + aAliasedSurface = nullptr; + aCanvasChild = nullptr; + aRecorder = nullptr; + } + + RefPtr<gfx::SourceSurface> mRecordedSurface; + RefPtr<CanvasChild> mCanvasChild; + RefPtr<CanvasDrawEventRecorder> mRecorder; + RefPtr<gfx::DataSourceSurface> mDataSourceSurface; +}; + +CanvasChild::CanvasChild(Endpoint<PCanvasChild>&& aEndpoint) { + aEndpoint.Bind(this); +} + +CanvasChild::~CanvasChild() = default; + +static void NotifyCanvasDeviceReset() { + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, "canvas-device-reset", nullptr); + } +} + +ipc::IPCResult CanvasChild::RecvNotifyDeviceChanged() { + NotifyCanvasDeviceReset(); + mRecorder->RecordEvent(RecordedDeviceChangeAcknowledged()); + return IPC_OK(); +} + +/* static */ bool CanvasChild::mDeactivated = false; + +ipc::IPCResult CanvasChild::RecvDeactivate() { + mDeactivated = true; + NotifyCanvasDeviceReset(); + return IPC_OK(); +} + +void CanvasChild::EnsureRecorder(TextureType aTextureType) { + if (!mRecorder) { + MOZ_ASSERT(mTextureType == TextureType::Unknown); + mTextureType = aTextureType; + mRecorder = MakeAndAddRef<CanvasDrawEventRecorder>(); + SharedMemoryBasic::Handle handle; + CrossProcessSemaphoreHandle readerSem; + CrossProcessSemaphoreHandle writerSem; + if (!mRecorder->Init(OtherPid(), &handle, &readerSem, &writerSem, + MakeUnique<RingBufferWriterServices>(this))) { + mRecorder = nullptr; + return; + } + + if (CanSend()) { + Unused << SendInitTranslator(mTextureType, std::move(handle), + std::move(readerSem), std::move(writerSem)); + } + } + + MOZ_RELEASE_ASSERT(mTextureType == aTextureType, + "We only support one remote TextureType currently."); +} + +void CanvasChild::ActorDestroy(ActorDestroyReason aWhy) { + // Explicitly drop our reference to the recorder, because it holds a reference + // to us via the ResumeTranslation callback. + mRecorder = nullptr; +} + +void CanvasChild::ResumeTranslation() { + if (CanSend()) { + SendResumeTranslation(); + } +} + +void CanvasChild::Destroy() { + if (CanSend()) { + Close(); + } +} + +void CanvasChild::OnTextureWriteLock() { + // We drop mRecorder in ActorDestroy to break the reference cycle. + if (!mRecorder) { + return; + } + + mHasOutstandingWriteLock = true; + mLastWriteLockCheckpoint = mRecorder->CreateCheckpoint(); +} + +void CanvasChild::OnTextureForwarded() { + // We drop mRecorder in ActorDestroy to break the reference cycle. + if (!mRecorder) { + return; + } + + if (mHasOutstandingWriteLock) { + mRecorder->RecordEvent(RecordedCanvasFlush()); + if (!mRecorder->WaitForCheckpoint(mLastWriteLockCheckpoint)) { + gfxWarning() << "Timed out waiting for last write lock to be processed."; + } + + mHasOutstandingWriteLock = false; + } + + // We hold onto the last transaction's external surfaces until we have waited + // for the write locks in this transaction. This means we know that the + // surfaces have been picked up in the canvas threads and there is no race + // with them being removed from SharedSurfacesParent. Note this releases the + // current contents of mLastTransactionExternalSurfaces. + mRecorder->TakeExternalSurfaces(mLastTransactionExternalSurfaces); +} + +void CanvasChild::EnsureBeginTransaction() { + // We drop mRecorder in ActorDestroy to break the reference cycle. + if (!mRecorder) { + return; + } + + if (!mIsInTransaction) { + mRecorder->RecordEvent(RecordedCanvasBeginTransaction()); + mIsInTransaction = true; + } +} + +void CanvasChild::EndTransaction() { + // We drop mRecorder in ActorDestroy to break the reference cycle. + if (!mRecorder) { + return; + } + + if (mIsInTransaction) { + mRecorder->RecordEvent(RecordedCanvasEndTransaction()); + mIsInTransaction = false; + mLastNonEmptyTransaction = TimeStamp::NowLoRes(); + } + + ++mTransactionsSinceGetDataSurface; +} + +bool CanvasChild::ShouldBeCleanedUp() const { + // Always return true if we've been deactivated. + if (Deactivated()) { + return true; + } + + // We can only be cleaned up if nothing else references our recorder. + if (mRecorder && !mRecorder->hasOneRef()) { + return false; + } + + static const TimeDuration kCleanUpCanvasThreshold = + TimeDuration::FromSeconds(10); + return TimeStamp::NowLoRes() - mLastNonEmptyTransaction > + kCleanUpCanvasThreshold; +} + +already_AddRefed<gfx::DrawTarget> CanvasChild::CreateDrawTarget( + gfx::IntSize aSize, gfx::SurfaceFormat aFormat) { + // We drop mRecorder in ActorDestroy to break the reference cycle. + if (!mRecorder) { + return nullptr; + } + + RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget( + gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat); + RefPtr<gfx::DrawTarget> dt = MakeAndAddRef<gfx::DrawTargetRecording>( + mRecorder, dummyDt, gfx::IntRect(gfx::IntPoint(0, 0), aSize)); + return dt.forget(); +} + +void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) { + // We drop mRecorder in ActorDestroy to break the reference cycle. + if (!mRecorder) { + return; + } + + mRecorder->RecordEvent(aEvent); +} + +already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface( + const gfx::SourceSurface* aSurface) { + MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aSurface); + + // We drop mRecorder in ActorDestroy to break the reference cycle. + if (!mRecorder) { + return nullptr; + } + + // mTransactionsSinceGetDataSurface is used to determine if we want to prepare + // a DataSourceSurface in the GPU process up front at the end of the + // transaction, but that only makes sense if the canvas JS is requesting data + // in between transactions. + if (!mIsInTransaction) { + mTransactionsSinceGetDataSurface = 0; + } + EnsureBeginTransaction(); + mRecorder->RecordEvent(RecordedPrepareDataForSurface(aSurface)); + uint32_t checkpoint = mRecorder->CreateCheckpoint(); + + gfx::IntSize ssSize = aSurface->GetSize(); + gfx::SurfaceFormat ssFormat = aSurface->GetFormat(); + size_t dataFormatWidth = ssSize.width * BytesPerPixel(ssFormat); + RefPtr<gfx::DataSourceSurface> dataSurface = + gfx::Factory::CreateDataSourceSurfaceWithStride(ssSize, ssFormat, + dataFormatWidth); + if (!dataSurface) { + gfxWarning() << "Failed to create DataSourceSurface."; + return nullptr; + } + gfx::DataSourceSurface::ScopedMap map(dataSurface, + gfx::DataSourceSurface::READ_WRITE); + char* dest = reinterpret_cast<char*>(map.GetData()); + if (!mRecorder->WaitForCheckpoint(checkpoint)) { + gfxWarning() << "Timed out preparing data for DataSourceSurface."; + return dataSurface.forget(); + } + + mRecorder->RecordEvent(RecordedGetDataForSurface(aSurface)); + mRecorder->ReturnRead(dest, ssSize.height * dataFormatWidth); + + return dataSurface.forget(); +} + +already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface( + const RefPtr<gfx::SourceSurface>& aSurface) { + MOZ_ASSERT(aSurface); + // We drop mRecorder in ActorDestroy to break the reference cycle. + if (!mRecorder) { + return nullptr; + } + + return MakeAndAddRef<SourceSurfaceCanvasRecording>(aSurface, this, mRecorder); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CanvasChild.h b/gfx/layers/ipc/CanvasChild.h new file mode 100644 index 0000000000..ca5b864008 --- /dev/null +++ b/gfx/layers/ipc/CanvasChild.h @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 https://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_layers_CanvasChild_h +#define mozilla_layers_CanvasChild_h + +#include "mozilla/gfx/RecordedEvent.h" +#include "mozilla/ipc/CrossProcessSemaphore.h" +#include "mozilla/layers/PCanvasChild.h" +#include "mozilla/layers/SourceSurfaceSharedData.h" +#include "nsRefPtrHashtable.h" +#include "nsTArray.h" + +namespace mozilla { + +namespace gfx { +class SourceSurface; +} + +namespace layers { +class CanvasDrawEventRecorder; + +class CanvasChild final : public PCanvasChild { + public: + NS_INLINE_DECL_REFCOUNTING(CanvasChild) + + explicit CanvasChild(Endpoint<PCanvasChild>&& aEndpoint); + + /** + * @returns true if remote canvas has been deactivated due to failure. + */ + static bool Deactivated() { return mDeactivated; } + + ipc::IPCResult RecvNotifyDeviceChanged(); + + ipc::IPCResult RecvDeactivate(); + + /** + * Ensures that the DrawEventRecorder has been created. + * + * @params aTextureType the TextureType to create in the CanvasTranslator. + */ + void EnsureRecorder(TextureType aTextureType); + + /** + * Send a messsage to our CanvasParent to resume translation. + */ + void ResumeTranslation(); + + /** + * Clean up IPDL actor. + */ + void Destroy(); + + /** + * Called when a RecordedTextureData is write locked. + */ + void OnTextureWriteLock(); + + /** + * Called when a RecordedTextureData is forwarded to the compositor. + */ + void OnTextureForwarded(); + + /** + * @returns true if we should be caching data surfaces in the GPU process. + */ + bool ShouldCacheDataSurface() const { + return mTransactionsSinceGetDataSurface < kCacheDataSurfaceThreshold; + } + + /** + * Ensures that we have sent a begin transaction event, since the last + * end transaction. + */ + void EnsureBeginTransaction(); + + /** + * Send an end transaction event to indicate the end of events for this frame. + */ + void EndTransaction(); + + /** + * @returns true if the canvas IPC classes have not been used for some time + * and can be cleaned up. + */ + bool ShouldBeCleanedUp() const; + + /** + * Create a DrawTargetRecording for a canvas texture. + * @param aSize size for the DrawTarget + * @param aFormat SurfaceFormat for the DrawTarget + * @returns newly created DrawTargetRecording + */ + already_AddRefed<gfx::DrawTarget> CreateDrawTarget( + gfx::IntSize aSize, gfx::SurfaceFormat aFormat); + + /** + * Record an event for processing by the CanvasParent's CanvasTranslator. + * @param aEvent the event to record + */ + void RecordEvent(const gfx::RecordedEvent& aEvent); + + /** + * Wrap the given surface, so that we can provide a DataSourceSurface if + * required. + * @param aSurface the SourceSurface to wrap + * @returns a SourceSurface that can provide a DataSourceSurface if required + */ + already_AddRefed<gfx::SourceSurface> WrapSurface( + const RefPtr<gfx::SourceSurface>& aSurface); + + /** + * Get DataSourceSurface from the translated equivalent version of aSurface in + * the GPU process. + * @param aSurface the SourceSurface in this process for which we need a + * DataSourceSurface + * @returns a DataSourceSurface created from data for aSurface retrieve from + * GPU process + */ + already_AddRefed<gfx::DataSourceSurface> GetDataSurface( + const gfx::SourceSurface* aSurface); + + protected: + void ActorDestroy(ActorDestroyReason aWhy) final; + + private: + DISALLOW_COPY_AND_ASSIGN(CanvasChild); + + ~CanvasChild() final; + + static const uint32_t kCacheDataSurfaceThreshold = 10; + + static bool mDeactivated; + + RefPtr<CanvasDrawEventRecorder> mRecorder; + TextureType mTextureType = TextureType::Unknown; + uint32_t mLastWriteLockCheckpoint = 0; + uint32_t mTransactionsSinceGetDataSurface = kCacheDataSurfaceThreshold; + TimeStamp mLastNonEmptyTransaction = TimeStamp::NowLoRes(); + std::vector<RefPtr<gfx::SourceSurface>> mLastTransactionExternalSurfaces; + bool mIsInTransaction = false; + bool mHasOutstandingWriteLock = false; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CanvasChild_h diff --git a/gfx/layers/ipc/CanvasThread.cpp b/gfx/layers/ipc/CanvasThread.cpp new file mode 100644 index 0000000000..3d0a387c04 --- /dev/null +++ b/gfx/layers/ipc/CanvasThread.cpp @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "CanvasThread.h" + +#include "mozilla/SharedThreadPool.h" +#include "nsThreadUtils.h" +#include "prsystem.h" + +bool NS_IsInCanvasThreadOrWorker() { + return mozilla::layers::CanvasThreadHolder::IsInCanvasThreadOrWorker(); +} + +namespace mozilla { +namespace layers { + +StaticDataMutex<StaticRefPtr<CanvasThreadHolder>> + CanvasThreadHolder::sCanvasThreadHolder("sCanvasThreadHolder"); + +CanvasThreadHolder::CanvasThreadHolder( + already_AddRefed<nsISerialEventTarget> aCanvasThread, + already_AddRefed<nsIThreadPool> aCanvasWorkers) + : mCanvasThread(aCanvasThread), + mCanvasWorkers(aCanvasWorkers), + mCompositorThreadKeepAlive(CompositorThreadHolder::GetSingleton()) { + MOZ_ASSERT(NS_IsInCompositorThread()); + MOZ_ASSERT(mCanvasThread); + MOZ_ASSERT(mCanvasWorkers); +} + +CanvasThreadHolder::~CanvasThreadHolder() { + // Note we can't just use NS_IsInCompositorThread() here because + // sCompositorThreadHolder might have already gone. + MOZ_ASSERT( + mCompositorThreadKeepAlive->GetCompositorThread()->IsOnCurrentThread()); +} + +/* static */ +already_AddRefed<CanvasThreadHolder> CanvasThreadHolder::EnsureCanvasThread() { + MOZ_ASSERT(NS_IsInCompositorThread()); + + auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); + if (!lockedCanvasThreadHolder.ref()) { + nsCOMPtr<nsISerialEventTarget> canvasThread; + nsresult rv = + NS_CreateBackgroundTaskQueue("Canvas", getter_AddRefs(canvasThread)); + NS_ENSURE_SUCCESS(rv, nullptr); + + // Given that the canvas workers are receiving instructions from + // content processes, it probably doesn't make sense to have more than + // half the number of processors doing canvas drawing. We set the + // lower limit to 2, so that even on single processor systems, if + // there is more than one window with canvas drawing, the OS can + // manage the load between them. + uint32_t threadLimit = std::max(2, PR_GetNumberOfProcessors() / 2); + nsCOMPtr<nsIThreadPool> canvasWorkers = + SharedThreadPool::Get("CanvasWorkers"_ns, threadLimit); + if (!canvasWorkers) { + return nullptr; + } + + lockedCanvasThreadHolder.ref() = + new CanvasThreadHolder(canvasThread.forget(), canvasWorkers.forget()); + } + + return do_AddRef(lockedCanvasThreadHolder.ref()); +} + +/* static */ +void CanvasThreadHolder::ReleaseOnCompositorThread( + already_AddRefed<CanvasThreadHolder> aCanvasThreadHolder) { + RefPtr<CanvasThreadHolder> canvasThreadHolder = aCanvasThreadHolder; + auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); + lockedCanvasThreadHolder.ref() + ->mCompositorThreadKeepAlive->GetCompositorThread() + ->Dispatch(NS_NewRunnableFunction( + "CanvasThreadHolder::StaticRelease", + [canvasThreadHolder = std::move(canvasThreadHolder)]() mutable { + RefPtr<CanvasThreadHolder> threadHolder = + canvasThreadHolder.forget(); + threadHolder = nullptr; + + auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); + if (lockedCanvasThreadHolder.ref()->mRefCnt == 1) { + lockedCanvasThreadHolder.ref() = nullptr; + } + })); +} + +/* static */ +bool CanvasThreadHolder::IsInCanvasThread() { + auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); + return lockedCanvasThreadHolder.ref() && + lockedCanvasThreadHolder.ref()->mCanvasThread->IsOnCurrentThread(); +} + +/* static */ +bool CanvasThreadHolder::IsInCanvasWorker() { + auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); + return lockedCanvasThreadHolder.ref() && + lockedCanvasThreadHolder.ref()->mCanvasWorkers->IsOnCurrentThread(); +} + +/* static */ +bool CanvasThreadHolder::IsInCanvasThreadOrWorker() { + auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); + return lockedCanvasThreadHolder.ref() && + (lockedCanvasThreadHolder.ref()->mCanvasWorkers->IsOnCurrentThread() || + lockedCanvasThreadHolder.ref()->mCanvasThread->IsOnCurrentThread()); +} + +/* static */ +void CanvasThreadHolder::MaybeDispatchToCanvasThread( + already_AddRefed<nsIRunnable> aRunnable) { + auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); + if (!lockedCanvasThreadHolder.ref()) { + // There is no canvas thread just release the runnable. + nsCOMPtr<nsIRunnable> runnable = aRunnable; + return; + } + + lockedCanvasThreadHolder.ref()->mCanvasThread->Dispatch(std::move(aRunnable)); +} + +void CanvasThreadHolder::DispatchToCanvasThread( + already_AddRefed<nsIRunnable> aRunnable) { + mCanvasThread->Dispatch(std::move(aRunnable)); +} + +already_AddRefed<TaskQueue> CanvasThreadHolder::CreateWorkerTaskQueue() { + return TaskQueue::Create(do_AddRef(mCanvasWorkers), "CanvasWorker").forget(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CanvasThread.h b/gfx/layers/ipc/CanvasThread.h new file mode 100644 index 0000000000..ebcd4bc8d5 --- /dev/null +++ b/gfx/layers/ipc/CanvasThread.h @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 https://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_layers_CanvasThread_h +#define mozilla_layers_CanvasThread_h + +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/DataMutex.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/TaskQueue.h" +#include "nsIThreadPool.h" + +namespace mozilla { +namespace layers { + +/** + * The CanvasThreadHolder is used to manage the lifetime of the canvas + * thread (IPC) and workers and also provides methods for using them. + */ +class CanvasThreadHolder final { + // We only AddRef/Release on the Compositor thread. + NS_INLINE_DECL_REFCOUNTING(CanvasThreadHolder) + + public: + /** + * Ensures that the canvas thread and workers are created. + * This must be called on the compositor thread. + * @return a CanvasThreadHolder + */ + static already_AddRefed<CanvasThreadHolder> EnsureCanvasThread(); + + /** + * Used to release CanvasThreadHolder references, which must be released on + * the compositor thread. + * @param aCanvasThreadHolder the reference to release + */ + static void ReleaseOnCompositorThread( + already_AddRefed<CanvasThreadHolder> aCanvasThreadHolder); + + /** + * @return true if in the canvas thread + */ + static bool IsInCanvasThread(); + + /** + * @return true if in a canvas worker thread + */ + static bool IsInCanvasWorker(); + + /** + * @return true if in the canvas thread or worker + */ + static bool IsInCanvasThreadOrWorker(); + + /** + * Static method to dispatch a runnable to the canvas thread. If there is no + * canvas thread this will just release aRunnable. + * @param aRunnable the runnable to dispatch + */ + static void MaybeDispatchToCanvasThread( + already_AddRefed<nsIRunnable> aRunnable); + + /** + * Dispatch a runnable to the canvas thread. + * @param aRunnable the runnable to dispatch + */ + void DispatchToCanvasThread(already_AddRefed<nsIRunnable> aRunnable); + + /** + * Create a TaskQueue for the canvas worker threads. This must be shutdown + * before the reference to CanvasThreadHolder is dropped. + * @return a TaskQueue that dispatches to the canvas worker threads + */ + already_AddRefed<TaskQueue> CreateWorkerTaskQueue(); + + private: + static StaticDataMutex<StaticRefPtr<CanvasThreadHolder>> sCanvasThreadHolder; + + CanvasThreadHolder(already_AddRefed<nsISerialEventTarget> aCanvasThread, + already_AddRefed<nsIThreadPool> aCanvasWorkers); + + ~CanvasThreadHolder(); + + nsCOMPtr<nsISerialEventTarget> mCanvasThread; + RefPtr<nsIThreadPool> mCanvasWorkers; + + // Hold a reference to prevent the compositor thread ending. + RefPtr<CompositorThreadHolder> mCompositorThreadKeepAlive; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CanvasThread_h diff --git a/gfx/layers/ipc/CanvasTranslator.cpp b/gfx/layers/ipc/CanvasTranslator.cpp new file mode 100644 index 0000000000..81c40270fb --- /dev/null +++ b/gfx/layers/ipc/CanvasTranslator.cpp @@ -0,0 +1,560 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 https://mozilla.org/MPL/2.0/. */ + +#include "CanvasTranslator.h" + +#include "gfxGradientCache.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/GPUParent.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/SharedSurfacesParent.h" +#include "mozilla/layers/TextureClient.h" +#include "mozilla/SyncRunnable.h" +#include "mozilla/Telemetry.h" +#include "nsTHashSet.h" +#include "RecordedCanvasEventImpl.h" + +#if defined(XP_WIN) +# include "mozilla/gfx/DeviceManagerDx.h" +# include "mozilla/layers/TextureD3D11.h" +#endif + +namespace mozilla { +namespace layers { + +// When in a transaction we wait for a short time because we're expecting more +// events from the content process. We don't want to wait for too long in case +// other content processes are waiting for events to process. +static const TimeDuration kReadEventTimeout = TimeDuration::FromMilliseconds(5); + +static const TimeDuration kDescriptorTimeout = + TimeDuration::FromMilliseconds(10000); + +class RingBufferReaderServices final + : public CanvasEventRingBuffer::ReaderServices { + public: + explicit RingBufferReaderServices(RefPtr<CanvasTranslator> aCanvasTranslator) + : mCanvasTranslator(std::move(aCanvasTranslator)) {} + + ~RingBufferReaderServices() final = default; + + bool WriterClosed() final { + return !mCanvasTranslator->GetIPCChannel()->CanSend(); + } + + private: + RefPtr<CanvasTranslator> mCanvasTranslator; +}; + +TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType, + const gfx::IntSize& aSize, + gfx::SurfaceFormat aFormat) { + TextureData* textureData = nullptr; + switch (aTextureType) { +#ifdef XP_WIN + case TextureType::D3D11: { + textureData = + D3D11TextureData::Create(aSize, aFormat, ALLOC_CLEAR_BUFFER, mDevice); + break; + } +#endif + default: + MOZ_CRASH("Unsupported TextureType for CanvasTranslator."); + } + + return textureData; +} + +typedef nsTHashSet<RefPtr<CanvasTranslator>> CanvasTranslatorSet; + +static CanvasTranslatorSet& CanvasTranslators() { + MOZ_ASSERT(CanvasThreadHolder::IsInCanvasThread()); + static CanvasTranslatorSet* sCanvasTranslator = new CanvasTranslatorSet(); + return *sCanvasTranslator; +} + +static void EnsureAllClosed() { + for (const auto& key : CanvasTranslators()) { + key->Close(); + } +} + +/* static */ void CanvasTranslator::Shutdown() { + // If the dispatch fails there is no canvas thread and so no translators. + CanvasThreadHolder::MaybeDispatchToCanvasThread(NewRunnableFunction( + "CanvasTranslator::EnsureAllClosed", &EnsureAllClosed)); +} + +/* static */ already_AddRefed<CanvasTranslator> CanvasTranslator::Create( + ipc::Endpoint<PCanvasParent>&& aEndpoint) { + MOZ_ASSERT(NS_IsInCompositorThread()); + + RefPtr<CanvasThreadHolder> threadHolder = + CanvasThreadHolder::EnsureCanvasThread(); + RefPtr<CanvasTranslator> canvasTranslator = + new CanvasTranslator(do_AddRef(threadHolder)); + threadHolder->DispatchToCanvasThread( + NewRunnableMethod<Endpoint<PCanvasParent>&&>( + "CanvasTranslator::Bind", canvasTranslator, &CanvasTranslator::Bind, + std::move(aEndpoint))); + return canvasTranslator.forget(); +} + +CanvasTranslator::CanvasTranslator( + already_AddRefed<CanvasThreadHolder> aCanvasThreadHolder) + : gfx::InlineTranslator(), mCanvasThreadHolder(aCanvasThreadHolder) { + // Track when remote canvas has been activated. + Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_CANVAS_REMOTE_ACTIVATED, 1); +} + +CanvasTranslator::~CanvasTranslator() { + // The textures need to be the last thing holding their DrawTargets, so that + // they can destroy them within a lock. + mDrawTargets.Clear(); + mBaseDT = nullptr; +} + +void CanvasTranslator::Bind(Endpoint<PCanvasParent>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + return; + } + + CanvasTranslators().Insert(this); +} + +mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator( + const TextureType& aTextureType, + ipc::SharedMemoryBasic::Handle&& aReadHandle, + CrossProcessSemaphoreHandle&& aReaderSem, + CrossProcessSemaphoreHandle&& aWriterSem) { + if (mStream) { + return IPC_FAIL(this, "RecvInitTranslator called twice."); + } + + mTextureType = aTextureType; + + // We need to initialize the stream first, because it might be used to + // communicate other failures back to the writer. + mStream = MakeUnique<CanvasEventRingBuffer>(); + if (!mStream->InitReader(std::move(aReadHandle), std::move(aReaderSem), + std::move(aWriterSem), + MakeUnique<RingBufferReaderServices>(this))) { + return IPC_FAIL(this, "Failed to initialize ring buffer reader."); + } + +#if defined(XP_WIN) + if (!CheckForFreshCanvasDevice(__LINE__)) { + gfxCriticalNote << "GFX: CanvasTranslator failed to get device"; + return IPC_OK(); + } +#endif + + mTranslationTaskQueue = mCanvasThreadHolder->CreateWorkerTaskQueue(); + return RecvResumeTranslation(); +} + +ipc::IPCResult CanvasTranslator::RecvResumeTranslation() { + if (mDeactivated) { + // The other side might have sent a resume message before we deactivated. + return IPC_OK(); + } + + MOZ_ALWAYS_SUCCEEDS(mTranslationTaskQueue->Dispatch( + NewRunnableMethod("CanvasTranslator::StartTranslation", this, + &CanvasTranslator::StartTranslation))); + + return IPC_OK(); +} + +void CanvasTranslator::StartTranslation() { + if (!TranslateRecording() && GetIPCChannel()->CanSend()) { + MOZ_ALWAYS_SUCCEEDS(mTranslationTaskQueue->Dispatch( + NewRunnableMethod("CanvasTranslator::StartTranslation", this, + &CanvasTranslator::StartTranslation))); + } + + // If the stream has been marked as bad and the Writer hasn't failed, + // deactivate remote canvas. + if (!mStream->good() && !mStream->WriterFailed()) { + Telemetry::ScalarAdd( + Telemetry::ScalarID::GFX_CANVAS_REMOTE_DEACTIVATED_BAD_STREAM, 1); + Deactivate(); + } +} + +void CanvasTranslator::ActorDestroy(ActorDestroyReason why) { + MOZ_ASSERT(CanvasThreadHolder::IsInCanvasThread()); + + if (!mTranslationTaskQueue) { + return FinishShutdown(); + } + + mTranslationTaskQueue->BeginShutdown()->Then( + GetCurrentSerialEventTarget(), __func__, this, + &CanvasTranslator::FinishShutdown, &CanvasTranslator::FinishShutdown); +} + +void CanvasTranslator::FinishShutdown() { + MOZ_ASSERT(CanvasThreadHolder::IsInCanvasThread()); + + // mTranslationTaskQueue has shutdown we can safely drop the ring buffer to + // break the cycle caused by RingBufferReaderServices. + mStream = nullptr; + + // CanvasTranslators has a MOZ_ASSERT(CanvasThreadHolder::IsInCanvasThread()) + // to ensure it is only called on the Canvas Thread. This takes a lock on + // CanvasThreadHolder::sCanvasThreadHolder, which is also locked in + // CanvasThreadHolder::StaticRelease on the compositor thread from + // ReleaseOnCompositorThread below. If that lock wins the race with the one in + // IsInCanvasThread and it is the last CanvasThreadHolder reference then it + // shuts down the canvas thread waiting for it to finish. However + // IsInCanvasThread is waiting for the lock on the canvas thread and we + // deadlock. So, we need to call CanvasTranslators before + // ReleaseOnCompositorThread. + CanvasTranslatorSet& canvasTranslators = CanvasTranslators(); + CanvasThreadHolder::ReleaseOnCompositorThread(mCanvasThreadHolder.forget()); + canvasTranslators.Remove(this); +} + +void CanvasTranslator::Deactivate() { + if (mDeactivated) { + return; + } + mDeactivated = true; + + // We need to tell the other side to deactivate. Make sure the stream is + // marked as bad so that the writing side won't wait for space to write. + mStream->SetIsBad(); + mCanvasThreadHolder->DispatchToCanvasThread( + NewRunnableMethod("CanvasTranslator::SendDeactivate", this, + &CanvasTranslator::SendDeactivate)); + + // Unlock all of our textures. + for (auto const& entry : mTextureDatas) { + entry.second->Unlock(); + } + + // Also notify anyone waiting for a surface descriptor. This must be done + // after mDeactivated is set to true. + mSurfaceDescriptorsMonitor.NotifyAll(); +} + +bool CanvasTranslator::TranslateRecording() { + MOZ_ASSERT(CanvasThreadHolder::IsInCanvasWorker()); + + int32_t eventType = mStream->ReadNextEvent(); + while (mStream->good()) { + bool success = RecordedEvent::DoWithEventFromStream( + *mStream, static_cast<RecordedEvent::EventType>(eventType), + [&](RecordedEvent* recordedEvent) -> bool { + // Make sure that the whole event was read from the stream. + if (!mStream->good()) { + if (!GetIPCChannel()->CanSend()) { + // The other side has closed only warn about read failure. + gfxWarning() << "Failed to read event type: " + << recordedEvent->GetType(); + } else { + gfxCriticalNote << "Failed to read event type: " + << recordedEvent->GetType(); + } + return false; + } + + return recordedEvent->PlayEvent(this); + }); + + // Check the stream is good here or we will log the issue twice. + if (!mStream->good()) { + return true; + } + + if (!success && !HandleExtensionEvent(eventType)) { + if (mDeviceResetInProgress) { + // We've notified the recorder of a device change, so we are expecting + // failures. Log as a warning to prevent crash reporting being flooded. + gfxWarning() << "Failed to play canvas event type: " << eventType; + } else { + gfxCriticalNote << "Failed to play canvas event type: " << eventType; + } + if (!mStream->good()) { + return true; + } + } + + if (!mIsInTransaction) { + return mStream->StopIfEmpty(); + } + + if (!mStream->HasDataToRead()) { + // We're going to wait for the next event, so take the opportunity to + // flush the rendering. + Flush(); + if (!mStream->WaitForDataToRead(kReadEventTimeout, 0)) { + return true; + } + } + + eventType = mStream->ReadNextEvent(); + } + + return true; +} + +#define READ_AND_PLAY_CANVAS_EVENT_TYPE(_typeenum, _class) \ + case _typeenum: { \ + auto e = _class(*mStream); \ + if (!mStream->good()) { \ + if (!GetIPCChannel()->CanSend()) { \ + /* The other side has closed only warn about read failure. */ \ + gfxWarning() << "Failed to read event type: " << _typeenum; \ + } else { \ + gfxCriticalNote << "Failed to read event type: " << _typeenum; \ + } \ + return false; \ + } \ + return e.PlayCanvasEvent(this); \ + } + +bool CanvasTranslator::HandleExtensionEvent(int32_t aType) { + // This is where we handle extensions to the Moz2D Recording events to handle + // canvas specific things. + switch (aType) { + FOR_EACH_CANVAS_EVENT(READ_AND_PLAY_CANVAS_EVENT_TYPE) + default: + return false; + } +} + +void CanvasTranslator::BeginTransaction() { mIsInTransaction = true; } + +void CanvasTranslator::Flush() { +#if defined(XP_WIN) + // We can end up without a device, due to a reset and failure to re-create. + if (!mDevice) { + return; + } + + gfx::AutoSerializeWithMoz2D serializeWithMoz2D(mBackendType); + RefPtr<ID3D11DeviceContext> deviceContext; + mDevice->GetImmediateContext(getter_AddRefs(deviceContext)); + deviceContext->Flush(); +#endif +} + +void CanvasTranslator::EndTransaction() { + Flush(); + // At the end of a transaction is a good time to check if a new canvas device + // has been created, even if a reset did not occur. + Unused << CheckForFreshCanvasDevice(__LINE__); + mIsInTransaction = false; +} + +void CanvasTranslator::DeviceChangeAcknowledged() { + mDeviceResetInProgress = false; +} + +bool CanvasTranslator::CreateReferenceTexture() { + if (mReferenceTextureData) { + mReferenceTextureData->Unlock(); + } + + mReferenceTextureData.reset(CreateTextureData( + mTextureType, gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8)); + if (!mReferenceTextureData) { + return false; + } + + mReferenceTextureData->Lock(OpenMode::OPEN_READ_WRITE); + mBaseDT = mReferenceTextureData->BorrowDrawTarget(); + if (!mBaseDT) { + // We might get a null draw target due to a device failure, just return + // false so that we can recover. + return false; + } + + mBackendType = mBaseDT->GetBackendType(); + return true; +} + +bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) { +#if defined(XP_WIN) + // If a new device has already been created, use that one. + RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetCanvasDevice(); + if (device && device != mDevice) { + if (mDevice) { + // We already had a device, notify child of change. + NotifyDeviceChanged(); + } + mDevice = device.forget(); + return CreateReferenceTexture(); + } + + if (mDevice) { + if (mDevice->GetDeviceRemovedReason() == S_OK) { + return false; + } + + gfxCriticalNote << "GFX: CanvasTranslator detected a device reset at " + << aLineNumber; + NotifyDeviceChanged(); + } + + RefPtr<Runnable> runnable = NS_NewRunnableFunction( + "CanvasTranslator NotifyDeviceReset", + []() { gfx::GPUParent::GetSingleton()->NotifyDeviceReset(); }); + + // It is safe to wait here because only the Compositor thread waits on us and + // the main thread doesn't wait on the compositor thread in the GPU process. + SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(), runnable, + /*aForceDispatch*/ true); + + mDevice = gfx::DeviceManagerDx::Get()->GetCanvasDevice(); + if (!mDevice) { + // We don't have a canvas device, we need to deactivate. + Telemetry::ScalarAdd( + Telemetry::ScalarID::GFX_CANVAS_REMOTE_DEACTIVATED_NO_DEVICE, 1); + Deactivate(); + return false; + } + + return CreateReferenceTexture(); +#else + return false; +#endif +} + +void CanvasTranslator::NotifyDeviceChanged() { + mDeviceResetInProgress = true; + mCanvasThreadHolder->DispatchToCanvasThread( + NewRunnableMethod("CanvasTranslator::SendNotifyDeviceChanged", this, + &CanvasTranslator::SendNotifyDeviceChanged)); +} + +void CanvasTranslator::AddSurfaceDescriptor(int64_t aTextureId, + TextureData* aTextureData) { + UniquePtr<SurfaceDescriptor> descriptor = MakeUnique<SurfaceDescriptor>(); + if (!aTextureData->Serialize(*descriptor)) { + MOZ_CRASH("Failed to serialize"); + } + + MonitorAutoLock lock(mSurfaceDescriptorsMonitor); + mSurfaceDescriptors[aTextureId] = std::move(descriptor); + mSurfaceDescriptorsMonitor.Notify(); +} + +already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget( + gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize, + gfx::SurfaceFormat aFormat) { + RefPtr<gfx::DrawTarget> dt; + do { + TextureData* textureData = CreateTextureData(mTextureType, aSize, aFormat); + if (textureData) { + MOZ_DIAGNOSTIC_ASSERT(mNextTextureId >= 0, "No texture ID set"); + textureData->Lock(OpenMode::OPEN_READ_WRITE); + mTextureDatas[mNextTextureId] = UniquePtr<TextureData>(textureData); + AddSurfaceDescriptor(mNextTextureId, textureData); + dt = textureData->BorrowDrawTarget(); + } + } while (!dt && CheckForFreshCanvasDevice(__LINE__)); + AddDrawTarget(aRefPtr, dt); + mNextTextureId = -1; + + return dt.forget(); +} + +void CanvasTranslator::RemoveTexture(int64_t aTextureId) { + mTextureDatas.erase(aTextureId); + + // It is possible that the texture from the content process has never been + // forwarded to the GPU process, so make sure its descriptor is removed. + MonitorAutoLock lock(mSurfaceDescriptorsMonitor); + mSurfaceDescriptors.erase(aTextureId); +} + +TextureData* CanvasTranslator::LookupTextureData(int64_t aTextureId) { + TextureMap::const_iterator result = mTextureDatas.find(aTextureId); + if (result == mTextureDatas.end()) { + return nullptr; + } + return result->second.get(); +} + +UniquePtr<SurfaceDescriptor> CanvasTranslator::WaitForSurfaceDescriptor( + int64_t aTextureId) { + MonitorAutoLock lock(mSurfaceDescriptorsMonitor); + DescriptorMap::iterator result; + while ((result = mSurfaceDescriptors.find(aTextureId)) == + mSurfaceDescriptors.end()) { + // If remote canvas has been deactivated just return null. + if (mDeactivated) { + return nullptr; + } + + CVStatus status = mSurfaceDescriptorsMonitor.Wait(kDescriptorTimeout); + if (status == CVStatus::Timeout) { + // If something has gone wrong and the texture has already been destroyed, + // it will have cleaned up its descriptor. + return nullptr; + } + } + + UniquePtr<SurfaceDescriptor> descriptor = std::move(result->second); + mSurfaceDescriptors.erase(aTextureId); + return descriptor; +} + +already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface( + uint64_t aKey) { + return SharedSurfacesParent::Get(wr::ToExternalImageId(aKey)); +} + +already_AddRefed<gfx::GradientStops> CanvasTranslator::GetOrCreateGradientStops( + gfx::DrawTarget* aDrawTarget, gfx::GradientStop* aRawStops, + uint32_t aNumStops, gfx::ExtendMode aExtendMode) { + MOZ_ASSERT(aDrawTarget); + nsTArray<gfx::GradientStop> rawStopArray(aRawStops, aNumStops); + return gfx::gfxGradientCache::GetOrCreateGradientStops( + aDrawTarget, rawStopArray, aExtendMode); +} + +gfx::DataSourceSurface* CanvasTranslator::LookupDataSurface( + gfx::ReferencePtr aRefPtr) { + return mDataSurfaces.GetWeak(aRefPtr); +} + +void CanvasTranslator::AddDataSurface( + gfx::ReferencePtr aRefPtr, RefPtr<gfx::DataSourceSurface>&& aSurface) { + mDataSurfaces.InsertOrUpdate(aRefPtr, std::move(aSurface)); +} + +void CanvasTranslator::RemoveDataSurface(gfx::ReferencePtr aRefPtr) { + mDataSurfaces.Remove(aRefPtr); +} + +void CanvasTranslator::SetPreparedMap( + gfx::ReferencePtr aSurface, + UniquePtr<gfx::DataSourceSurface::ScopedMap> aMap) { + mMappedSurface = aSurface; + mPreparedMap = std::move(aMap); +} + +UniquePtr<gfx::DataSourceSurface::ScopedMap> CanvasTranslator::GetPreparedMap( + gfx::ReferencePtr aSurface) { + if (!mPreparedMap) { + // We might fail to set the map during, for example, device resets. + return nullptr; + } + + MOZ_RELEASE_ASSERT(mMappedSurface == aSurface, + "aSurface must match previously stored surface."); + + mMappedSurface = nullptr; + return std::move(mPreparedMap); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CanvasTranslator.h b/gfx/layers/ipc/CanvasTranslator.h new file mode 100644 index 0000000000..af2769b0b4 --- /dev/null +++ b/gfx/layers/ipc/CanvasTranslator.h @@ -0,0 +1,306 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 https://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_layers_CanvasTranslator_h +#define mozilla_layers_CanvasTranslator_h + +#include <unordered_map> +#include <vector> + +#include "mozilla/gfx/InlineTranslator.h" +#include "mozilla/layers/CanvasDrawEventRecorder.h" +#include "mozilla/layers/CanvasThread.h" +#include "mozilla/layers/LayersSurfaces.h" +#include "mozilla/layers/PCanvasParent.h" +#include "mozilla/ipc/CrossProcessSemaphore.h" +#include "mozilla/Monitor.h" +#include "mozilla/UniquePtr.h" + +namespace mozilla { +namespace layers { + +class TextureData; + +class CanvasTranslator final : public gfx::InlineTranslator, + public PCanvasParent { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasTranslator) + + friend class PProtocolParent; + + /** + * Create an uninitialized CanvasTranslator and bind it to the given endpoint + * on the CanvasPlaybackLoop. + * + * @param aEndpoint the endpoint to bind to + * @return the new CanvasTranslator + */ + static already_AddRefed<CanvasTranslator> Create( + Endpoint<PCanvasParent>&& aEndpoint); + + /** + * Shutdown all of the CanvasTranslators. + */ + static void Shutdown(); + + /** + * Initialize the canvas translator for a particular TextureType and + * CanvasEventRingBuffer. + * + * @param aTextureType the TextureType the translator will create + * @param aReadHandle handle to the shared memory for the + * CanvasEventRingBuffer + * @param aReaderSem reading blocked semaphore for the CanvasEventRingBuffer + * @param aWriterSem writing blocked semaphore for the CanvasEventRingBuffer + */ + ipc::IPCResult RecvInitTranslator( + const TextureType& aTextureType, + ipc::SharedMemoryBasic::Handle&& aReadHandle, + CrossProcessSemaphoreHandle&& aReaderSem, + CrossProcessSemaphoreHandle&& aWriterSem); + + /** + * Used to tell the CanvasTranslator to start translating again after it has + * stopped due to a timeout waiting for events. + */ + ipc::IPCResult RecvResumeTranslation(); + + void ActorDestroy(ActorDestroyReason why) final; + + /** + * Translates events until no more are available or the end of a transaction + * If this returns false the caller of this is responsible for re-calling + * this function. + * + * @returns true if all events are processed and false otherwise. + */ + bool TranslateRecording(); + + /** + * Marks the beginning of rendering for a transaction. While in a transaction + * the translator will wait for a short time for events before returning. + * When not in a transaction the translator will only translate one event at a + * time. + */ + void BeginTransaction(); + + /** + * Marks the end of a transaction. + */ + void EndTransaction(); + + /** + * Flushes canvas drawing, for example to a device. + */ + void Flush(); + + /** + * Marks that device change processing in the writing process has finished. + */ + void DeviceChangeAcknowledged(); + + /** + * Used to send data back to the writer. This is done through the same shared + * memory so the writer must wait and read the response after it has submitted + * the event that uses this. + * + * @param aData the data to be written back to the writer + * @param aSize the number of chars to write + */ + void ReturnWrite(const char* aData, size_t aSize) { + mStream->ReturnWrite(aData, aSize); + } + + /** + * Set the texture ID that will be used as a lookup for the texture created by + * the next CreateDrawTarget. + */ + void SetNextTextureId(int64_t aNextTextureId) { + mNextTextureId = aNextTextureId; + } + + /** + * Used during playback of events to create DrawTargets. For the + * CanvasTranslator this means creating TextureDatas and getting the + * DrawTargets from those. + * + * @param aRefPtr the key to store the created DrawTarget against + * @param aSize the size of the DrawTarget + * @param aFormat the surface format for the DrawTarget + * @returns the new DrawTarget + */ + already_AddRefed<gfx::DrawTarget> CreateDrawTarget( + gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize, + gfx::SurfaceFormat aFormat) final; + + already_AddRefed<gfx::GradientStops> GetOrCreateGradientStops( + gfx::DrawTarget* aDrawTarget, gfx::GradientStop* aRawStops, + uint32_t aNumStops, gfx::ExtendMode aExtendMode) final; + + /** + * Get the TextureData associated with a TextureData from another process. + * + * @param aTextureId the key used to find the TextureData + * @returns the TextureData found + */ + TextureData* LookupTextureData(int64_t aTextureId); + + /** + * Waits for the SurfaceDescriptor associated with a TextureData from another + * process to be created and then returns it. + * + * @param aTextureId the key used to find the SurfaceDescriptor + * @returns the SurfaceDescriptor found + */ + UniquePtr<SurfaceDescriptor> WaitForSurfaceDescriptor(int64_t aTextureId); + + /** + * Removes the texture and other objects associated with a texture ID. + * + * @param aTextureId the texture ID to remove + */ + void RemoveTexture(int64_t aTextureId); + + /** + * Overriden to remove any DataSourceSurfaces associated with the RefPtr. + * + * @param aRefPtr the key to the surface + * @param aSurface the surface to store + */ + void AddSourceSurface(gfx::ReferencePtr aRefPtr, + gfx::SourceSurface* aSurface) final { + if (mMappedSurface == aRefPtr) { + mPreparedMap = nullptr; + mMappedSurface = nullptr; + } + RemoveDataSurface(aRefPtr); + InlineTranslator::AddSourceSurface(aRefPtr, aSurface); + } + + /** + * Removes the SourceSurface and other objects associated with a SourceSurface + * from another process. + * + * @param aRefPtr the key to the objects to remove + */ + void RemoveSourceSurface(gfx::ReferencePtr aRefPtr) final { + if (mMappedSurface == aRefPtr) { + mPreparedMap = nullptr; + mMappedSurface = nullptr; + } + RemoveDataSurface(aRefPtr); + InlineTranslator::RemoveSourceSurface(aRefPtr); + } + + already_AddRefed<gfx::SourceSurface> LookupExternalSurface( + uint64_t aKey) final; + + /** + * Gets the cached DataSourceSurface, if it exists, associated with a + * SourceSurface from another process. + * + * @param aRefPtr the key used to find the DataSourceSurface + * @returns the DataSourceSurface or nullptr if not found + */ + gfx::DataSourceSurface* LookupDataSurface(gfx::ReferencePtr aRefPtr); + + /** + * Used to cache the DataSourceSurface from a SourceSurface associated with a + * SourceSurface from another process. This is to improve performance if we + * require the data for that SourceSurface. + * + * @param aRefPtr the key used to store the DataSourceSurface + * @param aSurface the DataSourceSurface to store + */ + void AddDataSurface(gfx::ReferencePtr aRefPtr, + RefPtr<gfx::DataSourceSurface>&& aSurface); + + /** + * Gets the cached DataSourceSurface, if it exists, associated with a + * SourceSurface from another process. + * + * @param aRefPtr the key used to find the DataSourceSurface + * @returns the DataSourceSurface or nullptr if not found + */ + void RemoveDataSurface(gfx::ReferencePtr aRefPtr); + + /** + * Sets a ScopedMap, to be used in a later event. + * + * @param aSurface the associated surface in the other process + * @param aMap the ScopedMap to store + */ + void SetPreparedMap(gfx::ReferencePtr aSurface, + UniquePtr<gfx::DataSourceSurface::ScopedMap> aMap); + + /** + * Gets the ScopedMap stored using SetPreparedMap. + * + * @param aSurface must match the surface from the SetPreparedMap call + * @returns the ScopedMap if aSurface matches otherwise nullptr + */ + UniquePtr<gfx::DataSourceSurface::ScopedMap> GetPreparedMap( + gfx::ReferencePtr aSurface); + + private: + explicit CanvasTranslator( + already_AddRefed<CanvasThreadHolder> aCanvasThreadHolder); + + ~CanvasTranslator(); + + void Bind(Endpoint<PCanvasParent>&& aEndpoint); + + void StartTranslation(); + + void FinishShutdown(); + + void Deactivate(); + + TextureData* CreateTextureData(TextureType aTextureType, + const gfx::IntSize& aSize, + gfx::SurfaceFormat aFormat); + + void AddSurfaceDescriptor(int64_t aTextureId, TextureData* atextureData); + + bool HandleExtensionEvent(int32_t aType); + + bool CreateReferenceTexture(); + bool CheckForFreshCanvasDevice(int aLineNumber); + void NotifyDeviceChanged(); + + RefPtr<CanvasThreadHolder> mCanvasThreadHolder; + RefPtr<TaskQueue> mTranslationTaskQueue; +#if defined(XP_WIN) + RefPtr<ID3D11Device> mDevice; +#endif + // We hold the ring buffer as a UniquePtr so we can drop it once + // mTranslationTaskQueue has shutdown to break a RefPtr cycle. + UniquePtr<CanvasEventRingBuffer> mStream; + TextureType mTextureType = TextureType::Unknown; + UniquePtr<TextureData> mReferenceTextureData; + // Sometimes during device reset our reference DrawTarget can be null, so we + // hold the BackendType separately. + gfx::BackendType mBackendType = gfx::BackendType::NONE; + typedef std::unordered_map<int64_t, UniquePtr<TextureData>> TextureMap; + TextureMap mTextureDatas; + int64_t mNextTextureId = -1; + nsRefPtrHashtable<nsPtrHashKey<void>, gfx::DataSourceSurface> mDataSurfaces; + gfx::ReferencePtr mMappedSurface; + UniquePtr<gfx::DataSourceSurface::ScopedMap> mPreparedMap; + typedef std::unordered_map<int64_t, UniquePtr<SurfaceDescriptor>> + DescriptorMap; + DescriptorMap mSurfaceDescriptors MOZ_GUARDED_BY(mSurfaceDescriptorsMonitor); + Monitor mSurfaceDescriptorsMonitor{ + "CanvasTranslator::mSurfaceDescriptorsMonitor"}; + Atomic<bool> mDeactivated{false}; + bool mIsInTransaction = false; + bool mDeviceResetInProgress = false; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CanvasTranslator_h diff --git a/gfx/layers/ipc/CompositableForwarder.cpp b/gfx/layers/ipc/CompositableForwarder.cpp new file mode 100644 index 0000000000..369dff086a --- /dev/null +++ b/gfx/layers/ipc/CompositableForwarder.cpp @@ -0,0 +1,15 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "CompositableForwarder.h" + +#include "mozilla/layers/CompositableClient.h" +#include "mozilla/layers/TextureClient.h" + +namespace mozilla::layers { +CompositableForwarder::CompositableForwarder() = default; +CompositableForwarder::~CompositableForwarder() = default; +} // namespace mozilla::layers diff --git a/gfx/layers/ipc/CompositableForwarder.h b/gfx/layers/ipc/CompositableForwarder.h new file mode 100644 index 0000000000..da74a142e6 --- /dev/null +++ b/gfx/layers/ipc/CompositableForwarder.h @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_LAYERS_COMPOSITABLEFORWARDER +#define MOZILLA_LAYERS_COMPOSITABLEFORWARDER + +#include <stdint.h> // for int32_t, uint32_t, uint64_t +#include "mozilla/Assertions.h" // for AssertionConditionType, MOZ_ASSERT, MOZ_ASSERT_HELPER1 +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/TimeStamp.h" // for TimeStamp +#include "mozilla/layers/KnowsCompositor.h" // for KnowsCompositor +#include "nsRect.h" // for nsIntRect +#include "nsRegion.h" // for nsIntRegion +#include "nsTArray.h" // for nsTArray + +namespace mozilla { +namespace layers { +class CompositableClient; +class CompositableHandle; +class ImageContainer; +class PTextureChild; +class SurfaceDescriptorTiles; +class TextureClient; + +/** + * A transaction is a set of changes that happenned on the content side, that + * should be sent to the compositor side. + * CompositableForwarder is an interface to manage a transaction of + * compositable objetcs. + * + * ShadowLayerForwarder is an example of a CompositableForwarder (that can + * additionally forward modifications of the Layer tree). + * ImageBridgeChild is another CompositableForwarder. + * + * CompositableForwarder implements KnowsCompositor for simplicity as all + * implementations of CompositableForwarder currently also implement + * KnowsCompositor. This dependency could be split if we add new use cases. + */ +class CompositableForwarder : public KnowsCompositor { + public: + CompositableForwarder(); + ~CompositableForwarder(); + + /** + * Setup the IPDL actor for aCompositable to be part of layers + * transactions. + */ + virtual void Connect(CompositableClient* aCompositable, + ImageContainer* aImageContainer = nullptr) = 0; + + virtual void ReleaseCompositable(const CompositableHandle& aHandle) = 0; + virtual bool DestroyInTransaction(PTextureChild* aTexture) = 0; + + /** + * Tell the CompositableHost on the compositor side to remove the texture + * from the CompositableHost. + * This function does not delete the TextureHost corresponding to the + * TextureClient passed in parameter. + * When the TextureClient has TEXTURE_DEALLOCATE_CLIENT flag, + * the transaction becomes synchronous. + */ + virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable, + TextureClient* aTexture) = 0; + + struct TimedTextureClient { + TimedTextureClient() + : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {} + + TextureClient* mTextureClient; + TimeStamp mTimeStamp; + nsIntRect mPictureRect; + int32_t mFrameID; + int32_t mProducerID; + }; + /** + * Tell the CompositableHost on the compositor side what textures to use for + * the next composition. + */ + virtual void UseTextures(CompositableClient* aCompositable, + const nsTArray<TimedTextureClient>& aTextures) = 0; + + virtual void UseRemoteTexture(CompositableClient* aCompositable, + const RemoteTextureId aTextureId, + const RemoteTextureOwnerId aOwnerId, + const gfx::IntSize aSize, + const TextureFlags aFlags) = 0; + + virtual void EnableRemoteTexturePushCallback( + CompositableClient* aCompositable, const RemoteTextureOwnerId aOwnerId, + const gfx::IntSize aSize, const TextureFlags aFlags) = 0; + + virtual void UpdateFwdTransactionId() = 0; + virtual uint64_t GetFwdTransactionId() = 0; + + virtual bool InForwarderThread() = 0; + + void AssertInForwarderThread() { MOZ_ASSERT(InForwarderThread()); } + + protected: + nsTArray<RefPtr<TextureClient>> mTexturesToRemove; + nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/CompositableTransactionParent.cpp b/gfx/layers/ipc/CompositableTransactionParent.cpp new file mode 100644 index 0000000000..cd8deb2c73 --- /dev/null +++ b/gfx/layers/ipc/CompositableTransactionParent.cpp @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "CompositableTransactionParent.h" +#include "CompositableHost.h" // for CompositableParent, etc +#include "CompositorBridgeParent.h" // for CompositorBridgeParent +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor +#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG +#include "mozilla/layers/TextureHost.h" // for TextureHost +#include "mozilla/layers/WebRenderImageHost.h" +#include "mozilla/mozalloc.h" // for operator delete +#include "mozilla/Unused.h" +#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION +#include "nsRegion.h" // for nsIntRegion + +namespace mozilla { +namespace layers { + +bool CompositableParentManager::ReceiveCompositableUpdate( + const CompositableOperation& aEdit) { + // Ignore all operations on compositables created on stale compositors. We + // return true because the child is unable to handle errors. + RefPtr<CompositableHost> compositable = + FindCompositable(aEdit.compositable()); + if (!compositable) { + return false; + } + return ReceiveCompositableUpdate(aEdit.detail(), WrapNotNull(compositable), + aEdit.compositable()); +} + +bool CompositableParentManager::ReceiveCompositableUpdate( + const CompositableOperationDetail& aDetail, + NotNull<CompositableHost*> aCompositable, + const CompositableHandle& aHandle) { + switch (aDetail.type()) { + case CompositableOperationDetail::TOpRemoveTexture: { + const OpRemoveTexture& op = aDetail.get_OpRemoveTexture(); + + RefPtr<TextureHost> tex = + TextureHost::AsTextureHost(op.texture().AsParent()); + + MOZ_ASSERT(tex.get()); + aCompositable->RemoveTextureHost(tex); + break; + } + case CompositableOperationDetail::TOpUseTexture: { + const OpUseTexture& op = aDetail.get_OpUseTexture(); + + AutoTArray<CompositableHost::TimedTexture, 4> textures; + for (auto& timedTexture : op.textures()) { + CompositableHost::TimedTexture* t = textures.AppendElement(); + t->mTexture = + TextureHost::AsTextureHost(timedTexture.texture().AsParent()); + MOZ_ASSERT(t->mTexture); + t->mTimeStamp = timedTexture.timeStamp(); + t->mPictureRect = timedTexture.picture(); + t->mFrameID = timedTexture.frameID(); + t->mProducerID = timedTexture.producerID(); + if (timedTexture.readLocked()) { + t->mTexture->SetReadLocked(); + } + } + if (textures.Length() > 0) { + aCompositable->UseTextureHost(textures); + + for (auto& timedTexture : op.textures()) { + RefPtr<TextureHost> texture = + TextureHost::AsTextureHost(timedTexture.texture().AsParent()); + if (texture) { + texture->SetLastFwdTransactionId(mFwdTransactionId); + // Make sure that each texture was handled by the compositable + // because the recycling logic depends on it. + MOZ_ASSERT(texture->NumCompositableRefs() > 0); + } + } + } + break; + } + case CompositableOperationDetail::TOpUseRemoteTexture: { + const OpUseRemoteTexture& op = aDetail.get_OpUseRemoteTexture(); + auto* host = aCompositable->AsWebRenderImageHost(); + MOZ_ASSERT(host); + + host->PushPendingRemoteTexture(op.textureId(), op.ownerId(), + GetChildProcessId(), op.size(), + op.textureFlags()); + host->UseRemoteTexture(); + break; + } + case CompositableOperationDetail::TOpEnableRemoteTexturePushCallback: { + const OpEnableRemoteTexturePushCallback& op = + aDetail.get_OpEnableRemoteTexturePushCallback(); + + aCompositable->SetAsyncRef( + AsyncCompositableRef(GetChildProcessId(), aHandle)); + aCompositable->EnableRemoteTexturePushCallback( + op.ownerId(), GetChildProcessId(), op.size(), op.textureFlags()); + break; + } + default: { + MOZ_ASSERT(false, "bad type"); + } + } + + return true; +} + +void CompositableParentManager::DestroyActor(const OpDestroy& aOp) { + switch (aOp.type()) { + case OpDestroy::TPTexture: { + auto actor = aOp.get_PTexture().AsParent(); + TextureHost::ReceivedDestroy(actor); + break; + } + case OpDestroy::TCompositableHandle: { + ReleaseCompositable(aOp.get_CompositableHandle()); + break; + } + default: { + MOZ_ASSERT(false, "unsupported type"); + } + } +} + +RefPtr<CompositableHost> CompositableParentManager::AddCompositable( + const CompositableHandle& aHandle, const TextureInfo& aInfo) { + if (mCompositables.find(aHandle.Value()) != mCompositables.end()) { + NS_ERROR("Client should not allocate duplicate handles"); + return nullptr; + } + if (!aHandle) { + NS_ERROR("Client should not allocate 0 as a handle"); + return nullptr; + } + + RefPtr<CompositableHost> host = CompositableHost::Create(aInfo); + if (!host) { + return nullptr; + } + + mCompositables[aHandle.Value()] = host; + return host; +} + +RefPtr<CompositableHost> CompositableParentManager::FindCompositable( + const CompositableHandle& aHandle) { + auto iter = mCompositables.find(aHandle.Value()); + if (iter == mCompositables.end()) { + return nullptr; + } + + return iter->second; +} + +void CompositableParentManager::ReleaseCompositable( + const CompositableHandle& aHandle) { + auto iter = mCompositables.find(aHandle.Value()); + if (iter == mCompositables.end()) { + return; + } + iter->second->OnReleased(); + mCompositables.erase(iter); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositableTransactionParent.h b/gfx/layers/ipc/CompositableTransactionParent.h new file mode 100644 index 0000000000..172c6bb035 --- /dev/null +++ b/gfx/layers/ipc/CompositableTransactionParent.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H +#define MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H + +#include <vector> // for vector +#include "mozilla/Attributes.h" // for override +#include "mozilla/NotNull.h" +#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator +#include "mozilla/layers/LayersMessages.h" // for EditReply, etc +#include "mozilla/layers/TextureClient.h" +#include "CompositableHost.h" + +namespace mozilla { +namespace layers { + +// Since PCompositble has two potential manager protocols, we can't just call +// the Manager() method usually generated when there's one manager protocol, +// so both manager protocols implement this and we keep a reference to them +// through this interface. +class CompositableParentManager : public HostIPCAllocator { + public: + CompositableParentManager() = default; + + void DestroyActor(const OpDestroy& aOp); + + void UpdateFwdTransactionId(uint64_t aTransactionId) { + MOZ_ASSERT(mFwdTransactionId < aTransactionId); + mFwdTransactionId = aTransactionId; + } + + uint64_t GetFwdTransactionId() { return mFwdTransactionId; } + + RefPtr<CompositableHost> AddCompositable(const CompositableHandle& aHandle, + const TextureInfo& aInfo); + RefPtr<CompositableHost> FindCompositable(const CompositableHandle& aHandle); + + protected: + /** + * Handle the IPDL messages that affect PCompositable actors. + */ + bool ReceiveCompositableUpdate(const CompositableOperation& aEdit); + bool ReceiveCompositableUpdate(const CompositableOperationDetail& aDetail, + NotNull<CompositableHost*> aCompositable, + const CompositableHandle& aHandle); + + void ReleaseCompositable(const CompositableHandle& aHandle); + + uint64_t mFwdTransactionId = 0; + + /** + * Mapping form IDs to CompositableHosts. + */ + std::map<uint64_t, RefPtr<CompositableHost>> mCompositables; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/CompositorBench.cpp b/gfx/layers/ipc/CompositorBench.cpp new file mode 100644 index 0000000000..0fb4d8012b --- /dev/null +++ b/gfx/layers/ipc/CompositorBench.cpp @@ -0,0 +1,352 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "CompositorBench.h" + +#ifdef MOZ_COMPOSITOR_BENCH +# include "mozilla/gfx/2D.h" +# include "mozilla/layers/Compositor.h" +# include "mozilla/layers/Effects.h" +# include "mozilla/ProfilerMarkers.h" +# include "mozilla/StaticPrefs_layers.h" +# include "mozilla/TimeStamp.h" +# include <math.h> + +# define TEST_STEPS 1000 +# define DURATION_THRESHOLD 30 +# define THRESHOLD_ABORT_COUNT 5 + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +static float SimplePseudoRandom(int aStep, int aCount) { + srand(aStep * 1000 + aCount); + return static_cast<float>(rand()) / static_cast<float>(RAND_MAX); +} + +class BenchTest { + public: + BenchTest(const char* aTestName) : mTestName(aTestName) {} + + virtual ~BenchTest() = default; + + virtual void Setup(Compositor* aCompositor, size_t aStep) {} + virtual void Teardown(Compositor* aCompositor) {} + virtual void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, + size_t aStep) = 0; + + const char* ToString() { return mTestName; } + + private: + const char* mTestName; +}; + +static void DrawFrameTrivialQuad(Compositor* aCompositor, + const gfx::Rect& aScreenRect, size_t aStep, + const EffectChain& effects) { + for (size_t i = 0; i < aStep * 10; i++) { + const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width, + (int)(i / aScreenRect.height), 1, 1); + const gfx::Rect& clipRect = aScreenRect; + + float opacity = 1.f; + + gfx::Matrix transform2d; + + gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d); + + aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform); + } +} + +static void DrawFrameStressQuad(Compositor* aCompositor, + const gfx::Rect& aScreenRect, size_t aStep, + const EffectChain& effects) { + for (size_t i = 0; i < aStep * 10; i++) { + const gfx::Rect& rect = + gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0), + aScreenRect.height * SimplePseudoRandom(i, 1), + aScreenRect.width * SimplePseudoRandom(i, 2), + aScreenRect.height * SimplePseudoRandom(i, 3)); + const gfx::Rect& clipRect = aScreenRect; + + float opacity = 1.f; + + gfx::Matrix transform2d; + transform2d = transform2d.PreRotate(SimplePseudoRandom(i, 4) * 70.f); + + gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d); + + aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform); + } +} + +class EffectSolidColorBench : public BenchTest { + public: + EffectSolidColorBench() + : BenchTest("EffectSolidColorBench (clear frame with EffectSolidColor)") { + } + + void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, + size_t aStep) { + float tmp; + float red = modff(aStep * 0.03f, &tmp); + EffectChain effects; + effects.mPrimaryEffect = + new EffectSolidColor(gfx::DeviceColor(red, 0.4f, 0.4f, 1.0f)); + + const gfx::Rect& rect = aScreenRect; + const gfx::Rect& clipRect = aScreenRect; + + float opacity = 1.f; + + gfx::Matrix4x4 transform; + aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform); + } +}; + +class EffectSolidColorTrivialBench : public BenchTest { + public: + EffectSolidColorTrivialBench() + : BenchTest("EffectSolidColorTrivialBench (10s 1x1 EffectSolidColor)") {} + + void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, + size_t aStep) { + EffectChain effects; + effects.mPrimaryEffect = CreateEffect(aStep); + + DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects); + } + + already_AddRefed<Effect> CreateEffect(size_t i) { + float tmp; + float red = modff(i * 0.03f, &tmp); + EffectChain effects; + return MakeAndAddRef<EffectSolidColor>( + gfx::DeviceColor(red, 0.4f, 0.4f, 1.0f)); + } +}; + +class EffectSolidColorStressBench : public BenchTest { + public: + EffectSolidColorStressBench() + : BenchTest( + "EffectSolidColorStressBench (10s various EffectSolidColor)") {} + + void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, + size_t aStep) { + EffectChain effects; + effects.mPrimaryEffect = CreateEffect(aStep); + + DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects); + } + + already_AddRefed<Effect> CreateEffect(size_t i) { + float tmp; + float red = modff(i * 0.03f, &tmp); + EffectChain effects; + return MakeAndAddRef<EffectSolidColor>( + gfx::DeviceColor(red, 0.4f, 0.4f, 1.0f)); + } +}; + +class UploadBench : public BenchTest { + public: + UploadBench() : BenchTest("Upload Bench (10s 256x256 upload)") {} + + uint32_t* mBuf; + RefPtr<DataSourceSurface> mSurface; + RefPtr<DataTextureSource> mTexture; + + virtual void Setup(Compositor* aCompositor, size_t aStep) { + int bytesPerPixel = 4; + int w = 256; + int h = 256; + mBuf = (uint32_t*)malloc(w * h * sizeof(uint32_t)); + + mSurface = Factory::CreateWrappingDataSourceSurface( + reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), + SurfaceFormat::B8G8R8A8); + mTexture = aCompositor->CreateDataTextureSource(); + } + + virtual void Teardown(Compositor* aCompositor) { + mSurface = nullptr; + mTexture = nullptr; + free(mBuf); + } + + void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, + size_t aStep) { + for (size_t i = 0; i < aStep * 10; i++) { + mTexture->Update(mSurface); + } + } +}; + +class TrivialTexturedQuadBench : public BenchTest { + public: + TrivialTexturedQuadBench() + : BenchTest("Trvial Textured Quad (10s 256x256 quads)") {} + + uint32_t* mBuf; + RefPtr<DataSourceSurface> mSurface; + RefPtr<DataTextureSource> mTexture; + + virtual void Setup(Compositor* aCompositor, size_t aStep) { + int bytesPerPixel = 4; + size_t w = 256; + size_t h = 256; + mBuf = (uint32_t*)malloc(w * h * sizeof(uint32_t)); + for (size_t i = 0; i < w * h; i++) { + mBuf[i] = 0xFF00008F; + } + + mSurface = Factory::CreateWrappingDataSourceSurface( + reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), + SurfaceFormat::B8G8R8A8); + mTexture = aCompositor->CreateDataTextureSource(); + mTexture->Update(mSurface); + } + + void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, + size_t aStep) { + EffectChain effects; + effects.mPrimaryEffect = CreateEffect(aStep); + + DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects); + } + + virtual void Teardown(Compositor* aCompositor) { + mSurface = nullptr; + mTexture = nullptr; + free(mBuf); + } + + already_AddRefed<Effect> CreateEffect(size_t i) { + return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, + SamplingFilter::POINT, true); + } +}; + +class StressTexturedQuadBench : public BenchTest { + public: + StressTexturedQuadBench() + : BenchTest("Stress Textured Quad (10s 256x256 quads)") {} + + uint32_t* mBuf; + RefPtr<DataSourceSurface> mSurface; + RefPtr<DataTextureSource> mTexture; + + virtual void Setup(Compositor* aCompositor, size_t aStep) { + int bytesPerPixel = 4; + size_t w = 256; + size_t h = 256; + mBuf = (uint32_t*)malloc(w * h * sizeof(uint32_t)); + for (size_t i = 0; i < w * h; i++) { + mBuf[i] = 0xFF00008F; + } + + mSurface = Factory::CreateWrappingDataSourceSurface( + reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), + SurfaceFormat::B8G8R8A8); + mTexture = aCompositor->CreateDataTextureSource(); + mTexture->Update(mSurface); + } + + void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, + size_t aStep) { + EffectChain effects; + effects.mPrimaryEffect = CreateEffect(aStep); + + DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects); + } + + virtual void Teardown(Compositor* aCompositor) { + mSurface = nullptr; + mTexture = nullptr; + free(mBuf); + } + + virtual already_AddRefed<Effect> CreateEffect(size_t i) { + return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, + SamplingFilter::POINT, true); + } +}; + +static void RunCompositorBench(Compositor* aCompositor, + const gfx::Rect& aScreenRect) { + std::vector<BenchTest*> tests; + + tests.push_back(new EffectSolidColorBench()); + tests.push_back(new UploadBench()); + tests.push_back(new EffectSolidColorTrivialBench()); + tests.push_back(new EffectSolidColorStressBench()); + tests.push_back(new TrivialTexturedQuadBench()); + tests.push_back(new StressTexturedQuadBench()); + + for (size_t i = 0; i < tests.size(); i++) { + BenchTest* test = tests[i]; + std::vector<TimeDuration> results; + int testsOverThreshold = 0; + PROFILER_MARKER_UNTYPED( + ProfilerString8View::WrapNullTerminatedString(test->ToString()), + GRAPHICS); + for (size_t j = 0; j < TEST_STEPS; j++) { + test->Setup(aCompositor, j); + + TimeStamp start = TimeStamp::Now(); + IntRect screenRect(aScreenRect.x, aScreenRect.y, aScreenRect.width, + aScreenRect.height); + aCompositor->BeginFrame(IntRect(screenRect.x, screenRect.y, + screenRect.width, screenRect.height), + nullptr, aScreenRect, nullptr, nullptr); + + test->DrawFrame(aCompositor, aScreenRect, j); + + aCompositor->EndFrame(); + results.push_back(TimeStamp::Now() - start); + + if (results[j].ToMilliseconds() > DURATION_THRESHOLD) { + testsOverThreshold++; + if (testsOverThreshold == THRESHOLD_ABORT_COUNT) { + test->Teardown(aCompositor); + break; + } + } else { + testsOverThreshold = 0; + } + test->Teardown(aCompositor); + } + + printf_stderr("%s\n", test->ToString()); + printf_stderr("Run step, Time (ms)\n"); + for (size_t j = 0; j < results.size(); j++) { + printf_stderr("%i,%f\n", j, (float)results[j].ToMilliseconds()); + } + printf_stderr("\n", test->ToString()); + } + + for (size_t i = 0; i < tests.size(); i++) { + delete tests[i]; + } +} + +void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect) { + static bool sRanBenchmark = false; + bool wantBenchmark = StaticPrefs::layers_bench_enabled(); + if (wantBenchmark && !sRanBenchmark) { + RunCompositorBench(aCompositor, aScreenRect); + } + sRanBenchmark = wantBenchmark; +} + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/CompositorBench.h b/gfx/layers/ipc/CompositorBench.h new file mode 100644 index 0000000000..4a3c5874e9 --- /dev/null +++ b/gfx/layers/ipc/CompositorBench.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_CompositorBench_h +#define mozilla_layers_CompositorBench_h + +#include "mozilla/gfx/Rect.h" // for Rect + +namespace mozilla { +namespace layers { + +class Compositor; + +// Uncomment this line to rebuild with compositor bench. +// #define MOZ_COMPOSITOR_BENCH + +#ifdef MOZ_COMPOSITOR_BENCH +void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect); +#else +static inline void CompositorBench(Compositor* aCompositor, + const gfx::IntRect& aScreenRect) {} +#endif + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp new file mode 100644 index 0000000000..72210607e2 --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -0,0 +1,648 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" +#include <stddef.h> // for size_t +#include "base/task.h" // for NewRunnableMethod, etc +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/layers/CompositorManagerChild.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/APZChild.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "mozilla/layers/APZCTreeManagerChild.h" +#include "mozilla/layers/CanvasChild.h" +#include "mozilla/layers/WebRenderLayerManager.h" +#include "mozilla/layers/PTextureChild.h" +#include "mozilla/layers/TextureClient.h" // for TextureClient +#include "mozilla/layers/TextureClientPool.h" // for TextureClientPool +#include "mozilla/layers/WebRenderBridgeChild.h" +#include "mozilla/layers/SyncObject.h" // for SyncObjectClient +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/mozalloc.h" // for operator new, etc +#include "mozilla/Telemetry.h" +#include "gfxConfig.h" +#include "nsDebug.h" // for NS_WARNING +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "nsTArray.h" // for nsTArray, nsTArray_Impl +#include "mozilla/dom/BrowserChild.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/Unused.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "nsThreadUtils.h" +#if defined(XP_WIN) +# include "WinUtils.h" +#endif +#include "mozilla/widget/CompositorWidget.h" +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING +# include "mozilla/widget/CompositorWidgetChild.h" +#endif +#include "VsyncSource.h" + +using mozilla::Unused; +using mozilla::gfx::GPUProcessManager; + +namespace mozilla { +namespace layers { + +static int sShmemCreationCounter = 0; + +static void ResetShmemCounter() { sShmemCreationCounter = 0; } + +static void ShmemAllocated(CompositorBridgeChild* aProtocol) { + sShmemCreationCounter++; + if (sShmemCreationCounter > 256) { + aProtocol->SendSyncWithCompositor(); + ResetShmemCounter(); + MOZ_PERFORMANCE_WARNING( + "gfx", "The number of shmem allocations is too damn high!"); + } +} + +static StaticRefPtr<CompositorBridgeChild> sCompositorBridge; + +Atomic<int32_t> KnowsCompositor::sSerialCounter(0); + +CompositorBridgeChild::CompositorBridgeChild(CompositorManagerChild* aManager) + : mCompositorManager(aManager), + mIdNamespace(0), + mResourceId(0), + mCanSend(false), + mActorDestroyed(false), + mFwdTransactionId(0), + mThread(NS_GetCurrentThread()), + mProcessToken(0), + mSectionAllocator(nullptr) { + MOZ_ASSERT(NS_IsMainThread()); +} + +CompositorBridgeChild::~CompositorBridgeChild() { + if (mCanSend) { + gfxCriticalError() << "CompositorBridgeChild was not deinitialized"; + } +} + +bool CompositorBridgeChild::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void CompositorBridgeChild::PrepareFinalDestroy() { + // Because of medium high priority DidComposite, we need to repost to + // medium high priority queue to ensure the actor is destroyed after possible + // pending DidComposite message. + nsCOMPtr<nsIRunnable> runnable = + NewRunnableMethod("CompositorBridgeChild::AfterDestroy", this, + &CompositorBridgeChild::AfterDestroy); + NS_DispatchToCurrentThreadQueue(runnable.forget(), + EventQueuePriority::MediumHigh); +} + +void CompositorBridgeChild::AfterDestroy() { + // Note that we cannot rely upon mCanSend here because we already set that to + // false to prevent normal IPDL calls from being made after SendWillClose. + // The only time we should not issue Send__delete__ is if the actor is already + // destroyed, e.g. the compositor process crashed. + if (!mActorDestroyed) { + // We saw this send fail quite often with "Channel closing", probably a race + // with the other side closing or some event scheduling order. + if (GetIPCChannel()->CanSend()) { + Send__delete__(this); + } + mActorDestroyed = true; + } + + if (mCanvasChild) { + mCanvasChild->Destroy(); + } + + if (sCompositorBridge == this) { + sCompositorBridge = nullptr; + } +} + +void CompositorBridgeChild::Destroy() { + // This must not be called from the destructor! + mTexturesWaitingNotifyNotUsed.clear(); + + // Destroying the layer manager may cause all sorts of things to happen, so + // let's make sure there is still a reference to keep this alive whatever + // happens. + RefPtr<CompositorBridgeChild> selfRef = this; + + for (size_t i = 0; i < mTexturePools.Length(); i++) { + mTexturePools[i]->Destroy(); + } + + if (mSectionAllocator) { + delete mSectionAllocator; + mSectionAllocator = nullptr; + } + + if (mLayerManager) { + mLayerManager->Destroy(); + mLayerManager = nullptr; + } + + if (!mCanSend) { + // We may have already called destroy but still have lingering references + // or CompositorBridgeChild::ActorDestroy was called. Ensure that we do our + // post destroy clean up no matter what. It is safe to call multiple times. + NS_GetCurrentThread()->Dispatch( + NewRunnableMethod("CompositorBridgeChild::PrepareFinalDestroy", selfRef, + &CompositorBridgeChild::PrepareFinalDestroy)); + return; + } + + AutoTArray<PWebRenderBridgeChild*, 16> wrBridges; + ManagedPWebRenderBridgeChild(wrBridges); + for (int i = wrBridges.Length() - 1; i >= 0; --i) { + RefPtr<WebRenderBridgeChild> wrBridge = + static_cast<WebRenderBridgeChild*>(wrBridges[i]); + wrBridge->Destroy(/* aIsSync */ false); + } + + AutoTArray<PAPZChild*, 16> apzChildren; + ManagedPAPZChild(apzChildren); + for (PAPZChild* child : apzChildren) { + Unused << child->SendDestroy(); + } + + const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild(); + for (const auto& key : textures) { + RefPtr<TextureClient> texture = TextureClient::AsTextureClient(key); + + if (texture) { + texture->Destroy(); + } + } + + // The WillClose message is synchronous, so we know that after it returns + // any messages sent by the above code will have been processed on the + // other side. + SendWillClose(); + mCanSend = false; + + // We no longer care about unexpected shutdowns, in the remote process case. + mProcessToken = 0; + + // The call just made to SendWillClose can result in IPC from the + // CompositorBridgeParent to the CompositorBridgeChild (e.g. caused by the + // destruction of shared memory). We need to ensure this gets processed by the + // CompositorBridgeChild before it gets destroyed. It suffices to ensure that + // events already in the thread get processed before the + // CompositorBridgeChild is destroyed, so we add a task to the thread to + // handle compositor destruction. + + // From now on we can't send any message message. + NS_GetCurrentThread()->Dispatch( + NewRunnableMethod("CompositorBridgeChild::PrepareFinalDestroy", selfRef, + &CompositorBridgeChild::PrepareFinalDestroy)); +} + +// static +void CompositorBridgeChild::ShutDown() { + if (sCompositorBridge) { + sCompositorBridge->Destroy(); + SpinEventLoopUntil("CompositorBridgeChild::ShutDown"_ns, + [&]() { return !sCompositorBridge; }); + } +} + +void CompositorBridgeChild::InitForContent(uint32_t aNamespace) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aNamespace); + + if (RefPtr<CompositorBridgeChild> old = sCompositorBridge.forget()) { + // Note that at this point, ActorDestroy may not have been called yet, + // meaning mCanSend is still true. In this case we will try to send a + // synchronous WillClose message to the parent, and will certainly get + // a false result and a MsgDropped processing error. This is okay. + old->Destroy(); + } + + mCanSend = true; + mIdNamespace = aNamespace; + + sCompositorBridge = this; +} + +void CompositorBridgeChild::InitForWidget(uint64_t aProcessToken, + WebRenderLayerManager* aLayerManager, + uint32_t aNamespace) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aProcessToken); + MOZ_ASSERT(aLayerManager); + MOZ_ASSERT(aNamespace); + + mCanSend = true; + mProcessToken = aProcessToken; + mLayerManager = aLayerManager; + mIdNamespace = aNamespace; +} + +/*static*/ +CompositorBridgeChild* CompositorBridgeChild::Get() { + // This is only expected to be used in child processes. While the parent + // process does have CompositorBridgeChild instances, it has _multiple_ (one + // per window), and therefore there is no global singleton available. + MOZ_ASSERT(!XRE_IsParentProcess()); + return sCompositorBridge; +} + +/* static */ +bool CompositorBridgeChild::CompositorIsInGPUProcess() { + MOZ_ASSERT(NS_IsMainThread()); + + if (XRE_IsParentProcess()) { + return !!GPUProcessManager::Get()->GetGPUChild(); + } + + MOZ_ASSERT(XRE_IsContentProcess()); + CompositorBridgeChild* bridge = CompositorBridgeChild::Get(); + if (!bridge) { + return false; + } + + return bridge->OtherPid() != dom::ContentChild::GetSingleton()->OtherPid(); +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvDidComposite( + const LayersId& aId, const nsTArray<TransactionId>& aTransactionIds, + const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd) { + // Hold a reference to keep texture pools alive. See bug 1387799 + const auto texturePools = mTexturePools.Clone(); + + for (const auto& id : aTransactionIds) { + if (mLayerManager) { + MOZ_ASSERT(!aId.IsValid()); + MOZ_ASSERT(mLayerManager->GetBackendType() == LayersBackend::LAYERS_WR); + // Hold a reference to keep LayerManager alive. See Bug 1242668. + RefPtr<WebRenderLayerManager> m = mLayerManager; + m->DidComposite(id, aCompositeStart, aCompositeEnd); + } else if (aId.IsValid()) { + RefPtr<dom::BrowserChild> child = dom::BrowserChild::GetFrom(aId); + if (child) { + child->DidComposite(id, aCompositeStart, aCompositeEnd); + } + } + } + + for (size_t i = 0; i < texturePools.Length(); i++) { + texturePools[i]->ReturnDeferredClients(); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvNotifyFrameStats( + nsTArray<FrameStats>&& aFrameStats) { + gfxPlatform::GetPlatform()->NotifyFrameStats(std::move(aFrameStats)); + return IPC_OK(); +} + +void CompositorBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { + if (aWhy == AbnormalShutdown) { + // If the parent side runs into a problem then the actor will be destroyed. + // There is nothing we can do in the child side, here sets mCanSend as + // false. + gfxCriticalNote << "CompositorBridgeChild receives IPC close with " + "reason=AbnormalShutdown"; + } + + mCanSend = false; + mActorDestroyed = true; + + if (mProcessToken && XRE_IsParentProcess()) { + GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); + } +} + +bool CompositorBridgeChild::SendWillClose() { + MOZ_RELEASE_ASSERT(mCanSend); + return PCompositorBridgeChild::SendWillClose(); +} + +bool CompositorBridgeChild::SendPause() { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendPause(); +} + +bool CompositorBridgeChild::SendResume() { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendResume(); +} + +bool CompositorBridgeChild::SendResumeAsync() { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendResumeAsync(); +} + +bool CompositorBridgeChild::SendAdoptChild(const LayersId& id) { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendAdoptChild(id); +} + +bool CompositorBridgeChild::SendFlushRendering( + const wr::RenderReasons& aReasons) { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendFlushRendering(aReasons); +} + +bool CompositorBridgeChild::SendStartFrameTimeRecording( + const int32_t& bufferSize, uint32_t* startIndex) { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendStartFrameTimeRecording(bufferSize, + startIndex); +} + +bool CompositorBridgeChild::SendStopFrameTimeRecording( + const uint32_t& startIndex, nsTArray<float>* intervals) { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendStopFrameTimeRecording(startIndex, + intervals); +} + +PTextureChild* CompositorBridgeChild::AllocPTextureChild( + const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&, + const TextureFlags&, const LayersId&, const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId) { + return TextureClient::CreateIPDLActor(); +} + +bool CompositorBridgeChild::DeallocPTextureChild(PTextureChild* actor) { + return TextureClient::DestroyIPDLActor(actor); +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvParentAsyncMessages( + nsTArray<AsyncParentMessageData>&& aMessages) { + for (AsyncParentMessageArray::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 IPC_FAIL_NO_REASON(this); + } + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvObserveLayersUpdate( + const LayersId& aLayersId, const LayersObserverEpoch& aEpoch, + const bool& aActive) { + // This message is sent via the window compositor, not the tab compositor - + // however it still has a layers id. + MOZ_ASSERT(aLayersId.IsValid()); + MOZ_ASSERT(XRE_IsParentProcess()); + + if (RefPtr<dom::BrowserParent> tab = + dom::BrowserParent::GetBrowserParentFromLayersId(aLayersId)) { + tab->LayerTreeUpdate(aEpoch, aActive); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvCompositorOptionsChanged( + const LayersId& aLayersId, const CompositorOptions& aNewOptions) { + MOZ_ASSERT(aLayersId.IsValid()); + MOZ_ASSERT(XRE_IsParentProcess()); + + if (RefPtr<dom::BrowserParent> tab = + dom::BrowserParent::GetBrowserParentFromLayersId(aLayersId)) { + Unused << tab->SendCompositorOptionsChanged(aNewOptions); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvNotifyJankedAnimations( + const LayersId& aLayersId, nsTArray<uint64_t>&& aJankedAnimations) { + if (mLayerManager) { + MOZ_ASSERT(!aLayersId.IsValid()); + mLayerManager->UpdatePartialPrerenderedAnimations(aJankedAnimations); + } else if (aLayersId.IsValid()) { + RefPtr<dom::BrowserChild> child = dom::BrowserChild::GetFrom(aLayersId); + if (child) { + child->NotifyJankedAnimations(aJankedAnimations); + } + } + + return IPC_OK(); +} + +void CompositorBridgeChild::HoldUntilCompositableRefReleasedIfNecessary( + TextureClient* aClient) { + if (!aClient) { + return; + } + + bool waitNotifyNotUsed = + aClient->GetFlags() & TextureFlags::RECYCLE || + aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END; + if (!waitNotifyNotUsed) { + return; + } + + aClient->SetLastFwdTransactionId(GetFwdTransactionId()); + mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient); +} + +void CompositorBridgeChild::NotifyNotUsed(uint64_t aTextureId, + uint64_t aFwdTransactionId) { + auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId); + if (it != mTexturesWaitingNotifyNotUsed.end()) { + if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) { + // Released on host side, but client already requested newer use texture. + return; + } + mTexturesWaitingNotifyNotUsed.erase(it); + } +} + +void CompositorBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) { + mTexturesWaitingNotifyNotUsed.erase(aTextureId); +} + +FixedSizeSmallShmemSectionAllocator* +CompositorBridgeChild::GetTileLockAllocator() { + if (!IPCOpen()) { + return nullptr; + } + + if (!mSectionAllocator) { + mSectionAllocator = new FixedSizeSmallShmemSectionAllocator(this); + } + return mSectionAllocator; +} + +PTextureChild* CompositorBridgeChild::CreateTexture( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, + const dom::ContentParentId& aContentId, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId) { + PTextureChild* textureChild = + AllocPTextureChild(aSharedData, aReadLock, aLayersBackend, aFlags, + LayersId{0} /* FIXME */, aSerial, aExternalImageId); + + return SendPTextureConstructor( + textureChild, aSharedData, std::move(aReadLock), aLayersBackend, aFlags, + LayersId{0} /* FIXME? */, aSerial, aExternalImageId); +} + +already_AddRefed<CanvasChild> CompositorBridgeChild::GetCanvasChild() { + MOZ_ASSERT(gfx::gfxVars::RemoteCanvasEnabled()); + + if (CanvasChild::Deactivated()) { + return nullptr; + } + + if (!mCanvasChild) { + ipc::Endpoint<PCanvasParent> parentEndpoint; + ipc::Endpoint<PCanvasChild> childEndpoint; + nsresult rv = PCanvas::CreateEndpoints(OtherPid(), base::GetCurrentProcId(), + &parentEndpoint, &childEndpoint); + if (NS_SUCCEEDED(rv)) { + Unused << SendInitPCanvasParent(std::move(parentEndpoint)); + mCanvasChild = new CanvasChild(std::move(childEndpoint)); + } + } + + return do_AddRef(mCanvasChild); +} + +void CompositorBridgeChild::EndCanvasTransaction() { + if (mCanvasChild) { + mCanvasChild->EndTransaction(); + if (mCanvasChild->ShouldBeCleanedUp()) { + mCanvasChild->Destroy(); + Unused << SendReleasePCanvasParent(); + mCanvasChild = nullptr; + } + } +} + +bool CompositorBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) { + ShmemAllocated(this); + return PCompositorBridgeChild::AllocUnsafeShmem(aSize, aShmem); +} + +bool CompositorBridgeChild::AllocShmem(size_t aSize, ipc::Shmem* aShmem) { + ShmemAllocated(this); + return PCompositorBridgeChild::AllocShmem(aSize, aShmem); +} + +bool CompositorBridgeChild::DeallocShmem(ipc::Shmem& aShmem) { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::DeallocShmem(aShmem); +} + +widget::PCompositorWidgetChild* +CompositorBridgeChild::AllocPCompositorWidgetChild( + const CompositorWidgetInitData& aInitData) { + // We send the constructor manually. + MOZ_CRASH("Should not be called"); + return nullptr; +} + +bool CompositorBridgeChild::DeallocPCompositorWidgetChild( + PCompositorWidgetChild* aActor) { +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING + delete aActor; + return true; +#else + return false; +#endif +} + +PAPZCTreeManagerChild* CompositorBridgeChild::AllocPAPZCTreeManagerChild( + const LayersId& aLayersId) { + APZCTreeManagerChild* child = new APZCTreeManagerChild(); + child->AddIPDLReference(); + + return child; +} + +PAPZChild* CompositorBridgeChild::AllocPAPZChild(const LayersId& aLayersId) { + // We send the constructor manually. + MOZ_CRASH("Should not be called"); + return nullptr; +} + +bool CompositorBridgeChild::DeallocPAPZChild(PAPZChild* aActor) { + delete aActor; + return true; +} + +bool CompositorBridgeChild::DeallocPAPZCTreeManagerChild( + PAPZCTreeManagerChild* aActor) { + APZCTreeManagerChild* child = static_cast<APZCTreeManagerChild*>(aActor); + child->ReleaseIPDLReference(); + return true; +} + +// - + +PWebRenderBridgeChild* CompositorBridgeChild::AllocPWebRenderBridgeChild( + const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize&, + const WindowKind&) { + WebRenderBridgeChild* child = new WebRenderBridgeChild(aPipelineId); + child->AddIPDLReference(); + return child; +} + +bool CompositorBridgeChild::DeallocPWebRenderBridgeChild( + PWebRenderBridgeChild* aActor) { + WebRenderBridgeChild* child = static_cast<WebRenderBridgeChild*>(aActor); + child->ReleaseIPDLReference(); + return true; +} + +uint64_t CompositorBridgeChild::GetNextResourceId() { + ++mResourceId; + MOZ_RELEASE_ASSERT(mResourceId != UINT32_MAX); + + uint64_t id = mIdNamespace; + id = (id << 32) | mResourceId; + + return id; +} + +wr::MaybeExternalImageId CompositorBridgeChild::GetNextExternalImageId() { + return Some(wr::ToExternalImageId(GetNextResourceId())); +} + +wr::PipelineId CompositorBridgeChild::GetNextPipelineId() { + return wr::AsPipelineId(GetNextResourceId()); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h new file mode 100644 index 0000000000..723cbc5705 --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeChild.h @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_CompositorBridgeChild_h +#define mozilla_layers_CompositorBridgeChild_h + +#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS +#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 +#include "mozilla/Attributes.h" // for override +#include "mozilla/Monitor.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/layers/PCompositorBridgeChild.h" +#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder +#include "mozilla/webrender/WebRenderTypes.h" +#include "nsClassHashtable.h" // for nsClassHashtable +#include "nsCOMPtr.h" // for nsCOMPtr +#include "nsHashKeys.h" // for nsUint64HashKey +#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING +#include "nsIWeakReferenceUtils.h" + +#include <unordered_map> + +namespace mozilla { + +namespace dom { +class BrowserChild; +} // namespace dom + +namespace widget { +class CompositorWidget; +} // namespace widget + +namespace layers { + +using mozilla::dom::BrowserChild; + +class IAPZCTreeManager; +class APZCTreeManagerChild; +class CanvasChild; +class CompositorBridgeParent; +class CompositorManagerChild; +class CompositorOptions; +class WebRenderLayerManager; +class TextureClient; +class TextureClientPool; +struct FrameMetrics; + +class CompositorBridgeChild final : public PCompositorBridgeChild, + public TextureForwarder { + typedef nsTArray<AsyncParentMessageData> AsyncParentMessageArray; + + friend class PCompositorBridgeChild; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorBridgeChild, override); + + explicit CompositorBridgeChild(CompositorManagerChild* aManager); + + /** + * Initialize the singleton compositor bridge for a content process. + */ + void InitForContent(uint32_t aNamespace); + + void InitForWidget(uint64_t aProcessToken, + WebRenderLayerManager* aLayerManager, uint32_t aNamespace); + + void Destroy(); + + static CompositorBridgeChild* Get(); + + // Returns whether the compositor is in the GPU process (false if in the UI + // process). This may only be called on the main thread. + static bool CompositorIsInGPUProcess(); + + mozilla::ipc::IPCResult RecvDidComposite( + const LayersId& aId, const nsTArray<TransactionId>& aTransactionIds, + const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd); + + mozilla::ipc::IPCResult RecvNotifyFrameStats( + nsTArray<FrameStats>&& aFrameStats); + + mozilla::ipc::IPCResult RecvNotifyJankedAnimations( + const LayersId& aLayersId, nsTArray<uint64_t>&& aJankedAnimations); + + PTextureChild* AllocPTextureChild( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const LayersId& aId, const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId); + + bool DeallocPTextureChild(PTextureChild* actor); + + mozilla::ipc::IPCResult RecvParentAsyncMessages( + nsTArray<AsyncParentMessageData>&& aMessages); + PTextureChild* CreateTexture( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, + const dom::ContentParentId& aContentId, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId) override; + + already_AddRefed<CanvasChild> GetCanvasChild() final; + + void EndCanvasTransaction(); + + // Beware that these methods don't override their super-class equivalent + // (which are not virtual), they just overload them. All of these Send* + // methods just add a sanity check (that it is not too late send a message) + // and forward the call to the super-class's equivalent method. This means + // that it is correct to call directly the super-class methods, but you won't + // get the extra safety provided here. + bool SendWillClose(); + bool SendPause(); + bool SendResume(); + bool SendResumeAsync(); + bool SendAdoptChild(const LayersId& id); + bool SendFlushRendering(const wr::RenderReasons& aReasons); + bool SendStartFrameTimeRecording(const int32_t& bufferSize, + uint32_t* startIndex); + bool SendStopFrameTimeRecording(const uint32_t& startIndex, + nsTArray<float>* intervals); + bool IsSameProcess() const override; + + bool IPCOpen() const override { return mCanSend; } + + static void ShutDown(); + + void UpdateFwdTransactionId() { ++mFwdTransactionId; } + uint64_t GetFwdTransactionId() { return mFwdTransactionId; } + + /** + * Hold TextureClient ref until end of usage on host side if + * TextureFlags::RECYCLE is set. Host side's usage is checked via + * CompositableRef. + */ + void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient); + + /** + * Notify id of Texture When host side end its use. Transaction id is used to + * make sure if there is no newer usage. + */ + void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId); + + void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override; + + FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() override; + + nsISerialEventTarget* GetThread() const override { return mThread; } + + base::ProcessId GetParentPid() const override { return OtherPid(); } + + bool AllocUnsafeShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override; + bool AllocShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override; + bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + + PCompositorWidgetChild* AllocPCompositorWidgetChild( + const CompositorWidgetInitData& aInitData); + bool DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor); + + PAPZCTreeManagerChild* AllocPAPZCTreeManagerChild(const LayersId& aLayersId); + bool DeallocPAPZCTreeManagerChild(PAPZCTreeManagerChild* aActor); + + PAPZChild* AllocPAPZChild(const LayersId& aLayersId); + bool DeallocPAPZChild(PAPZChild* aActor); + + PWebRenderBridgeChild* AllocPWebRenderBridgeChild( + const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize&, + const WindowKind&); + bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor); + + wr::MaybeExternalImageId GetNextExternalImageId() override; + + wr::PipelineId GetNextPipelineId(); + + private: + // Private destructor, to discourage deletion outside of Release(): + virtual ~CompositorBridgeChild(); + + // Must only be called from the paint thread. If the main thread is delaying + // IPC messages, this forwards all such delayed IPC messages to the I/O thread + // and resumes IPC. + void ResumeIPCAfterAsyncPaint(); + + void PrepareFinalDestroy(); + void AfterDestroy(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + mozilla::ipc::IPCResult RecvObserveLayersUpdate( + const LayersId& aLayersId, const LayersObserverEpoch& aEpoch, + const bool& aActive); + + mozilla::ipc::IPCResult RecvCompositorOptionsChanged( + const LayersId& aLayersId, const CompositorOptions& aNewOptions); + + uint64_t GetNextResourceId(); + + RefPtr<CompositorManagerChild> mCompositorManager; + + RefPtr<WebRenderLayerManager> mLayerManager; + + uint32_t mIdNamespace; + uint32_t mResourceId; + + // When not multi-process, hold a reference to the CompositorBridgeParent to + // keep it alive. This reference should be null in multi-process. + RefPtr<CompositorBridgeParent> mCompositorBridgeParent; + + DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeChild); + + // True until the beginning of the two-step shutdown sequence of this actor. + bool mCanSend; + + // False until the actor is destroyed. + bool mActorDestroyed; + + /** + * Transaction id of ShadowLayerForwarder. + * It is incremented by UpdateFwdTransactionId() in each BeginTransaction() + * call. + */ + uint64_t mFwdTransactionId; + + /** + * Hold TextureClients refs until end of their usages on host side. + * It defer calling of TextureClient recycle callback. + */ + std::unordered_map<uint64_t, RefPtr<TextureClient>> + mTexturesWaitingNotifyNotUsed; + + nsCOMPtr<nsISerialEventTarget> mThread; + + AutoTArray<RefPtr<TextureClientPool>, 2> mTexturePools; + + uint64_t mProcessToken; + + FixedSizeSmallShmemSectionAllocator* mSectionAllocator; + + // TextureClients that must be kept alive during async painting. This + // is only accessed on the main thread. + nsTArray<RefPtr<TextureClient>> mTextureClientsForAsyncPaint; + + RefPtr<CanvasChild> mCanvasChild; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CompositorBrigedChild_h diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp new file mode 100644 index 0000000000..0e9cf4fd21 --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -0,0 +1,1988 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/CompositorBridgeParent.h" + +#include <stdio.h> // for fprintf, stdout +#include <stdint.h> // for uint64_t +#include <map> // for _Rb_tree_iterator, etc +#include <utility> // for pair + +#include "apz/src/APZCTreeManager.h" // for APZCTreeManager +#include "base/process.h" // for ProcessId +#include "gfxContext.h" // for gfxContext +#include "gfxPlatform.h" // for gfxPlatform +#include "TreeTraversal.h" // for ForEachNode +#ifdef MOZ_WIDGET_GTK +# include "gfxPlatformGtk.h" // for gfxPlatform +#endif +#include "mozilla/AutoRestore.h" // for AutoRestore +#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown +#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/gfx/2D.h" // for DrawTarget +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/gfx/Rect.h" // for IntSize +#include "mozilla/gfx/gfxVars.h" // for gfxVars +#include "mozilla/gfx/GPUParent.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent +#include "mozilla/layers/APZSampler.h" // for APZSampler +#include "mozilla/layers/APZThreadUtils.h" // for APZThreadUtils +#include "mozilla/layers/APZUpdater.h" // for APZUpdater +#include "mozilla/layers/CompositionRecorder.h" // for CompositionRecorder +#include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorAnimationStorage.h" // for CompositorAnimationStorage +#include "mozilla/layers/CompositorManagerParent.h" // for CompositorManagerParent +#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/CompositorVsyncScheduler.h" +#include "mozilla/layers/ContentCompositorBridgeParent.h" +#include "mozilla/layers/FrameUniformityData.h" +#include "mozilla/layers/GeckoContentController.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/OMTASampler.h" +#include "mozilla/layers/RemoteContentController.h" +#include "mozilla/layers/UiCompositorControllerParent.h" +#include "mozilla/layers/WebRenderBridgeParent.h" +#include "mozilla/layers/AsyncImagePipelineManager.h" +#include "mozilla/webrender/WebRenderAPI.h" +#include "mozilla/webrender/RenderThread.h" +#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService +#include "mozilla/mozalloc.h" // for operator new, etc +#include "mozilla/PodOperations.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/ProfilerMarkers.h" +#include "mozilla/Telemetry.h" +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsDebug.h" // for NS_ASSERTION, etc +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "nsIWidget.h" // for nsIWidget +#include "nsTArray.h" // for nsTArray +#include "nsThreadUtils.h" // for NS_IsMainThread +#ifdef XP_WIN +# include "mozilla/layers/CompositorD3D11.h" +# include "mozilla/widget/WinCompositorWidget.h" +# include "mozilla/WindowsVersion.h" +#endif +#include "mozilla/ipc/ProtocolTypes.h" +#include "mozilla/Unused.h" +#include "mozilla/Hal.h" +#include "mozilla/HalTypes.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Telemetry.h" +#include "mozilla/VsyncDispatcher.h" +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +# include "VsyncSource.h" +#endif +#include "mozilla/widget/CompositorWidget.h" +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING +# include "mozilla/widget/CompositorWidgetParent.h" +#endif +#ifdef XP_WIN +# include "mozilla/gfx/DeviceManagerDx.h" +#endif + +namespace mozilla { + +namespace layers { + +using namespace mozilla::ipc; +using namespace mozilla::gfx; + +using base::ProcessId; + +using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON; + +/// Equivalent to asserting CompositorThreadHolder::IsInCompositorThread with +/// the addition that it doesn't assert if the compositor thread holder is +/// already gone during late shutdown. +static void AssertIsInCompositorThread() { + MOZ_RELEASE_ASSERT(!CompositorThread() || + CompositorThreadHolder::IsInCompositorThread()); +} + +CompositorBridgeParentBase::CompositorBridgeParentBase( + CompositorManagerParent* aManager) + : mCanSend(true), mCompositorManager(aManager) {} + +CompositorBridgeParentBase::~CompositorBridgeParentBase() = default; + +ProcessId CompositorBridgeParentBase::GetChildProcessId() { return OtherPid(); } + +dom::ContentParentId CompositorBridgeParentBase::GetContentId() { + return mCompositorManager->GetContentId(); +} + +void CompositorBridgeParentBase::NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) { + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); + if (!texture) { + return; + } + + if (!(texture->GetFlags() & TextureFlags::RECYCLE) && + !(texture->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END)) { + return; + } + + uint64_t textureId = TextureHost::GetTextureSerial(aTexture); + mPendingAsyncMessage.push_back(OpNotifyNotUsed(textureId, aTransactionId)); +} + +void CompositorBridgeParentBase::SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) { + Unused << SendParentAsyncMessages(aMessage); +} + +bool CompositorBridgeParentBase::AllocShmem(size_t aSize, ipc::Shmem* aShmem) { + return PCompositorBridgeParent::AllocShmem(aSize, aShmem); +} + +bool CompositorBridgeParentBase::AllocUnsafeShmem(size_t aSize, + ipc::Shmem* aShmem) { + return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aShmem); +} + +bool CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem) { + return PCompositorBridgeParent::DeallocShmem(aShmem); +} + +CompositorBridgeParent::LayerTreeState::LayerTreeState() + : mApzcTreeManagerParent(nullptr), + mParent(nullptr), + mContentCompositorBridgeParent(nullptr) {} + +CompositorBridgeParent::LayerTreeState::~LayerTreeState() { + if (mController) { + mController->Destroy(); + } +} + +typedef std::map<LayersId, CompositorBridgeParent::LayerTreeState> LayerTreeMap; +LayerTreeMap sIndirectLayerTrees; +StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock; + +static void EnsureLayerTreeMapReady() { + MOZ_ASSERT(NS_IsMainThread()); + if (!sIndirectLayerTreesLock) { + sIndirectLayerTreesLock = new Monitor("IndirectLayerTree"); + mozilla::ClearOnShutdown(&sIndirectLayerTreesLock); + } +} + +template <typename Lambda> +inline void CompositorBridgeParent::ForEachIndirectLayerTree( + const Lambda& aCallback) { + sIndirectLayerTreesLock->AssertCurrentThreadOwns(); + for (auto it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end(); + it++) { + LayerTreeState* state = &it->second; + if (state->mParent == this) { + aCallback(state, it->first); + } + } +} + +/*static*/ template <typename Lambda> +inline void CompositorBridgeParent::ForEachWebRenderBridgeParent( + const Lambda& aCallback) { + sIndirectLayerTreesLock->AssertCurrentThreadOwns(); + for (auto& it : sIndirectLayerTrees) { + LayerTreeState* state = &it.second; + if (state->mWrBridge) { + aCallback(state->mWrBridge); + } + } +} + +/** + * A global map referencing each compositor by ID. + * + * This map is used by the ImageBridge protocol to trigger + * compositions without having to keep references to the + * compositor + */ +typedef std::map<uint64_t, CompositorBridgeParent*> CompositorMap; +static StaticAutoPtr<CompositorMap> sCompositorMap; + +void CompositorBridgeParent::Setup() { + EnsureLayerTreeMapReady(); + + MOZ_ASSERT(!sCompositorMap); + sCompositorMap = new CompositorMap; +} + +void CompositorBridgeParent::FinishShutdown() { + MOZ_ASSERT(NS_IsMainThread()); + + if (sCompositorMap) { + MOZ_ASSERT(sCompositorMap->empty()); + sCompositorMap = nullptr; + } + + // TODO: this should be empty by now... + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees.clear(); +} + +CompositorBridgeParent::CompositorBridgeParent( + CompositorManagerParent* aManager, CSSToLayoutDeviceScale aScale, + const TimeDuration& aVsyncRate, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, + uint64_t aInnerWindowId) + : CompositorBridgeParentBase(aManager), + mWidget(nullptr), + mScale(aScale), + mVsyncRate(aVsyncRate), + mPaused(false), + mHaveCompositionRecorder(false), + mIsForcedFirstPaint(false), + mUseExternalSurfaceSize(aUseExternalSurfaceSize), + mEGLSurfaceSize(aSurfaceSize), + mOptions(aOptions), + mCompositorBridgeID(0), + mRootLayerTreeID{0}, + mInnerWindowId(aInnerWindowId), + mCompositorScheduler(nullptr), + mAnimationStorage(nullptr) {} + +void CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget, + const LayersId& aLayerTreeId) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + mWidget = aWidget; + mRootLayerTreeID = aLayerTreeId; +#if defined(XP_WIN) + // when run in headless mode, no WinCompositorWidget is created + if (widget::WinCompositorWidget* windows = mWidget->AsWindows()) { + windows->SetRootLayerTreeID(mRootLayerTreeID); + } +#endif + + Initialize(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvInitialize( + const LayersId& aRootLayerTreeId) { + MOZ_ASSERT(XRE_IsGPUProcess()); + + mRootLayerTreeID = aRootLayerTreeId; +#ifdef XP_WIN + // headless mode is probably always same-process; but just in case... + if (widget::WinCompositorWidget* windows = mWidget->AsWindows()) { + windows->SetRootLayerTreeID(mRootLayerTreeID); + } +#endif + + Initialize(); + return IPC_OK(); +} + +void CompositorBridgeParent::Initialize() { + MOZ_ASSERT(CompositorThread(), + "The compositor thread must be Initialized before instanciating a " + "CompositorBridgeParent."); + + if (mOptions.UseAPZ()) { + MOZ_ASSERT(!mApzcTreeManager); + MOZ_ASSERT(!mApzSampler); + MOZ_ASSERT(!mApzUpdater); + mApzcTreeManager = new APZCTreeManager(mRootLayerTreeID); + mApzSampler = new APZSampler(mApzcTreeManager, true); + mApzUpdater = new APZUpdater(mApzcTreeManager, true); + } + + CompositorAnimationStorage* animationStorage = GetAnimationStorage(); + mOMTASampler = new OMTASampler(animationStorage, mRootLayerTreeID); + + mPaused = mOptions.InitiallyPaused(); + + mCompositorBridgeID = 0; + // FIXME: This holds on the the fact that right now the only thing that + // can destroy this instance is initialized on the compositor thread after + // this task has been processed. + MOZ_ASSERT(CompositorThread()); + CompositorThread()->Dispatch(NewRunnableFunction( + "AddCompositorRunnable", &AddCompositor, this, &mCompositorBridgeID)); + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[mRootLayerTreeID].mParent = this; + } +} + +LayersId CompositorBridgeParent::RootLayerTreeId() { + MOZ_ASSERT(mRootLayerTreeID.IsValid()); + return mRootLayerTreeID; +} + +CompositorBridgeParent::~CompositorBridgeParent() { + MOZ_DIAGNOSTIC_ASSERT( + !mCanSend, + "ActorDestroy or RecvWillClose should have been called first."); + MOZ_DIAGNOSTIC_ASSERT(mRefCnt == 0, + "ActorDealloc should have been called first."); + nsTArray<PTextureParent*> textures; + ManagedPTextureParent(textures); + // We expect all textures to be destroyed by now. + MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0); + for (unsigned int i = 0; i < textures.Length(); ++i) { + RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]); + tex->DeallocateDeviceData(); + } + // Check if WebRender/Compositor was shutdown. + if (mWrBridge) { + gfxCriticalNote << "CompositorBridgeParent destroyed without shutdown"; + } +} + +void CompositorBridgeParent::ForceIsFirstPaint() { + if (mWrBridge) { + mIsForcedFirstPaint = true; + } +} + +void CompositorBridgeParent::StopAndClearResources() { + mPaused = true; + + // We need to clear the APZ tree before we destroy the WebRender API below, + // because in the case of async scene building that will shut down the updater + // thread and we need to run the task before that happens. + MOZ_ASSERT((mApzSampler != nullptr) == (mApzcTreeManager != nullptr)); + MOZ_ASSERT((mApzUpdater != nullptr) == (mApzcTreeManager != nullptr)); + if (mApzUpdater) { + mApzSampler->Destroy(); + mApzSampler = nullptr; + mApzUpdater->ClearTree(mRootLayerTreeID); + mApzUpdater = nullptr; + mApzcTreeManager = nullptr; + } + + if (mWrBridge) { + // Ensure we are not holding the sIndirectLayerTreesLock when destroying + // the WebRenderBridgeParent instances because it may block on WR. + std::vector<RefPtr<WebRenderBridgeParent>> indirectBridgeParents; + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([&](LayerTreeState* lts, LayersId) -> void { + if (lts->mWrBridge) { + indirectBridgeParents.emplace_back(lts->mWrBridge.forget()); + } + lts->mParent = nullptr; + }); + } + for (const RefPtr<WebRenderBridgeParent>& bridge : indirectBridgeParents) { + bridge->Destroy(); + } + indirectBridgeParents.clear(); + + RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI(); + // Ensure we are not holding the sIndirectLayerTreesLock here because we + // are going to block on WR threads in order to shut it down properly. + mWrBridge->Destroy(); + mWrBridge = nullptr; + + if (api) { + // Make extra sure we are done cleaning WebRender up before continuing. + // After that we wont have a way to talk to a lot of the webrender parts. + api->FlushSceneBuilder(); + api = nullptr; + } + + if (mAsyncImageManager) { + mAsyncImageManager->Destroy(); + // WebRenderAPI should be already destructed + mAsyncImageManager = nullptr; + } + } + + // This must be destroyed now since it accesses the widget. + if (mCompositorScheduler) { + mCompositorScheduler->Destroy(); + mCompositorScheduler = nullptr; + } + + if (mOMTASampler) { + mOMTASampler->Destroy(); + mOMTASampler = nullptr; + } + + // After this point, it is no longer legal to access the widget. + mWidget = nullptr; + + // Clear mAnimationStorage here to ensure that the compositor thread + // still exists when we destroy it. + mAnimationStorage = nullptr; +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvWillClose() { + StopAndClearResources(); + // Once we get the WillClose message, the client side is going to go away + // soon and we can't be guaranteed that sending messages will work. + mCanSend = false; + return IPC_OK(); +} + +void CompositorBridgeParent::DeferredDestroy() { + MOZ_ASSERT(!NS_IsMainThread()); + mSelfRef = nullptr; +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvPause() { + PauseComposition(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvRequestFxrOutput() { +#ifdef XP_WIN + // Continue forwarding the request to the Widget + SwapChain + mWidget->AsWindows()->RequestFxrOutput(); +#endif + + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvResume() { + ResumeComposition(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvResumeAsync() { + ResumeComposition(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +CompositorBridgeParent::RecvWaitOnTransactionProcessed() { + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRendering( + const wr::RenderReasons& aReasons) { + if (mWrBridge) { + mWrBridge->FlushRendering(aReasons); + return IPC_OK(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyMemoryPressure() { + NotifyMemoryPressure(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRenderingAsync( + const wr::RenderReasons& aReasons) { + if (mWrBridge) { + mWrBridge->FlushRendering(aReasons, false); + return IPC_OK(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvForcePresent( + const wr::RenderReasons& aReasons) { + if (mWrBridge) { + mWrBridge->ScheduleForcedGenerateFrame(aReasons); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvStartFrameTimeRecording( + const int32_t& aBufferSize, uint32_t* aOutStartIndex) { + if (mWrBridge) { + *aOutStartIndex = mWrBridge->StartFrameTimeRecording(aBufferSize); + } else { + *aOutStartIndex = 0; + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvStopFrameTimeRecording( + const uint32_t& aStartIndex, nsTArray<float>* intervals) { + if (mWrBridge) { + mWrBridge->StopFrameTimeRecording(aStartIndex, *intervals); + } + return IPC_OK(); +} + +void CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) { + mCanSend = false; + + StopAndClearResources(); + + RemoveCompositor(mCompositorBridgeID); + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees.erase(mRootLayerTreeID); + } + + // There are chances that the ref count reaches zero on the main thread + // shortly after this function returns while some ipdl code still needs to run + // on this thread. We must keep the compositor parent alive untill the code + // handling message reception is finished on this thread. + mSelfRef = this; + NS_GetCurrentThread()->Dispatch( + NewRunnableMethod("layers::CompositorBridgeParent::DeferredDestroy", this, + &CompositorBridgeParent::DeferredDestroy)); +} + +void CompositorBridgeParent::ScheduleRenderOnCompositorThread( + wr::RenderReasons aReasons) { + MOZ_ASSERT(CompositorThread()); + CompositorThread()->Dispatch(NewRunnableMethod<wr::RenderReasons>( + "layers::CompositorBridgeParent::ScheduleComposition", this, + &CompositorBridgeParent::ScheduleComposition, aReasons)); +} + +void CompositorBridgeParent::PauseComposition() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "PauseComposition() can only be called on the compositor thread"); + + if (!mPaused) { + mPaused = true; + + TimeStamp now = TimeStamp::Now(); + if (mWrBridge) { + mWrBridge->Pause(); + NotifyPipelineRendered(mWrBridge->PipelineId(), + mWrBridge->GetCurrentEpoch(), VsyncId(), now, now, + now); + } + } +} + +bool CompositorBridgeParent::ResumeComposition() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "ResumeComposition() can only be called on the compositor thread"); + + bool resumed = mWidget->OnResumeComposition(); + resumed = resumed && mWrBridge->Resume(); + + if (!resumed) { +#ifdef MOZ_WIDGET_ANDROID + // We can't get a surface. This could be because the activity changed + // between the time resume was scheduled and now. + __android_log_print( + ANDROID_LOG_INFO, "CompositorBridgeParent", + "Unable to renew compositor surface; remaining in paused state"); +#endif + return false; + } + + mPaused = false; + + mCompositorScheduler->ForceComposeToTarget(wr::RenderReasons::WIDGET, nullptr, + nullptr); + return true; +} + +void CompositorBridgeParent::SetEGLSurfaceRect(int x, int y, int width, + int height) { + NS_ASSERTION(mUseExternalSurfaceSize, + "Compositor created without UseExternalSurfaceSize provided"); + mEGLSurfaceSize.SizeTo(width, height); +} + +bool CompositorBridgeParent::ResumeCompositionAndResize(int x, int y, int width, + int height) { + SetEGLSurfaceRect(x, y, width, height); + return ResumeComposition(); +} + +void CompositorBridgeParent::ScheduleComposition(wr::RenderReasons aReasons) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mPaused) { + return; + } + + if (mWrBridge) { + mWrBridge->ScheduleGenerateFrame(aReasons); + } +} + +PAPZCTreeManagerParent* CompositorBridgeParent::AllocPAPZCTreeManagerParent( + const LayersId& aLayersId) { + // This should only ever get called in the GPU process. + MOZ_ASSERT(XRE_IsGPUProcess()); + // We should only ever get this if APZ is enabled in this compositor. + MOZ_ASSERT(mOptions.UseAPZ()); + // The mApzcTreeManager and mApzUpdater should have been created via + // RecvInitialize() + MOZ_ASSERT(mApzcTreeManager); + MOZ_ASSERT(mApzUpdater); + // The main process should pass in 0 because we assume mRootLayerTreeID + MOZ_ASSERT(!aLayersId.IsValid()); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& state = + sIndirectLayerTrees[mRootLayerTreeID]; + MOZ_ASSERT(state.mParent.get() == this); + MOZ_ASSERT(!state.mApzcTreeManagerParent); + state.mApzcTreeManagerParent = new APZCTreeManagerParent( + mRootLayerTreeID, mApzcTreeManager, mApzUpdater); + + return state.mApzcTreeManagerParent; +} + +bool CompositorBridgeParent::DeallocPAPZCTreeManagerParent( + PAPZCTreeManagerParent* aActor) { + delete aActor; + return true; +} + +void CompositorBridgeParent::AllocateAPZCTreeManagerParent( + const MonitorAutoLock& aProofOfLayerTreeStateLock, + const LayersId& aLayersId, LayerTreeState& aState) { + MOZ_ASSERT(aState.mParent == this); + MOZ_ASSERT(mApzcTreeManager); + MOZ_ASSERT(mApzUpdater); + MOZ_ASSERT(!aState.mApzcTreeManagerParent); + aState.mApzcTreeManagerParent = + new APZCTreeManagerParent(aLayersId, mApzcTreeManager, mApzUpdater); +} + +PAPZParent* CompositorBridgeParent::AllocPAPZParent(const LayersId& aLayersId) { + // This is the CompositorBridgeParent for a window, and so should only be + // creating a PAPZ instance if it lives in the GPU process. Instances that + // live in the UI process should going through SetControllerForLayerTree. + MOZ_RELEASE_ASSERT(XRE_IsGPUProcess()); + + // We should only ever get this if APZ is enabled on this compositor. + MOZ_RELEASE_ASSERT(mOptions.UseAPZ()); + + // The main process should pass in 0 because we assume mRootLayerTreeID + MOZ_RELEASE_ASSERT(!aLayersId.IsValid()); + + RemoteContentController* controller = new RemoteContentController(); + + // Increment the controller's refcount before we return it. This will keep the + // controller alive until it is released by IPDL in DeallocPAPZParent. + controller->AddRef(); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& state = + sIndirectLayerTrees[mRootLayerTreeID]; + MOZ_RELEASE_ASSERT(!state.mController); + state.mController = controller; + + return controller; +} + +bool CompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor) { + RemoteContentController* controller = + static_cast<RemoteContentController*>(aActor); + controller->Release(); + return true; +} + +RefPtr<APZSampler> CompositorBridgeParent::GetAPZSampler() const { + return mApzSampler; +} + +RefPtr<APZUpdater> CompositorBridgeParent::GetAPZUpdater() const { + return mApzUpdater; +} + +RefPtr<OMTASampler> CompositorBridgeParent::GetOMTASampler() const { + return mOMTASampler; +} + +CompositorBridgeParent* +CompositorBridgeParent::GetCompositorBridgeParentFromLayersId( + const LayersId& aLayersId) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + return sIndirectLayerTrees[aLayersId].mParent; +} + +/*static*/ +RefPtr<CompositorBridgeParent> +CompositorBridgeParent::GetCompositorBridgeParentFromWindowId( + const wr::WindowId& aWindowId) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + for (auto it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end(); + it++) { + LayerTreeState* state = &it->second; + if (!state->mWrBridge) { + continue; + } + // state->mWrBridge might be a root WebRenderBridgeParent or one of a + // content process, but in either case the state->mParent will be the same. + // So we don't need to distinguish between the two. + if (RefPtr<wr::WebRenderAPI> api = state->mWrBridge->GetWebRenderAPI()) { + if (api->GetId() == aWindowId) { + return state->mParent; + } + } + } + return nullptr; +} + +bool CompositorBridgeParent::SetTestSampleTime(const LayersId& aId, + const TimeStamp& aTime) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + if (aTime.IsNull()) { + return false; + } + + mTestTime = Some(aTime); + if (mApzcTreeManager) { + mApzcTreeManager->SetTestSampleTime(mTestTime); + } + + if (mWrBridge) { + mWrBridge->FlushRendering(wr::RenderReasons::TESTING); + return true; + } + + return true; +} + +void CompositorBridgeParent::LeaveTestMode(const LayersId& aId) { + mTestTime = Nothing(); + if (mApzcTreeManager) { + mApzcTreeManager->SetTestSampleTime(mTestTime); + } +} + +CompositorAnimationStorage* CompositorBridgeParent::GetAnimationStorage() { + if (!mAnimationStorage) { + mAnimationStorage = new CompositorAnimationStorage(this); + } + return mAnimationStorage; +} + +void CompositorBridgeParent::NotifyJankedAnimations( + const JankedAnimations& aJankedAnimations) { + MOZ_ASSERT(!aJankedAnimations.empty()); + + if (StaticPrefs::layout_animation_prerender_partial_jank()) { + return; + } + + for (const auto& entry : aJankedAnimations) { + const LayersId& layersId = entry.first; + const nsTArray<uint64_t>& animations = entry.second; + if (layersId == mRootLayerTreeID) { + if (mWrBridge) { + Unused << SendNotifyJankedAnimations(LayersId{0}, animations); + } + // It unlikely happens multiple processes have janked animations at same + // time, so it should be fine with enumerating sIndirectLayerTrees every + // time. + } else if (const LayerTreeState* state = GetIndirectShadowTree(layersId)) { + if (ContentCompositorBridgeParent* cpcp = + state->mContentCompositorBridgeParent) { + Unused << cpcp->SendNotifyJankedAnimations(layersId, animations); + } + } + } +} + +void CompositorBridgeParent::SetTestAsyncScrollOffset( + const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId, + const CSSPoint& aPoint) { + if (mApzUpdater) { + MOZ_ASSERT(aLayersId.IsValid()); + mApzUpdater->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint); + } +} + +void CompositorBridgeParent::SetTestAsyncZoom( + const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId, + const LayerToParentLayerScale& aZoom) { + if (mApzUpdater) { + MOZ_ASSERT(aLayersId.IsValid()); + mApzUpdater->SetTestAsyncZoom(aLayersId, aScrollId, aZoom); + } +} + +void CompositorBridgeParent::FlushApzRepaints(const LayersId& aLayersId) { + MOZ_ASSERT(mApzUpdater); + MOZ_ASSERT(aLayersId.IsValid()); + mApzUpdater->RunOnControllerThread( + aLayersId, NS_NewRunnableFunction( + "layers::CompositorBridgeParent::FlushApzRepaints", + [=]() { APZCTreeManager::FlushApzRepaints(aLayersId); })); +} + +void CompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId, + APZTestData* aOutData) { + if (mApzUpdater) { + MOZ_ASSERT(aLayersId.IsValid()); + mApzUpdater->GetAPZTestData(aLayersId, aOutData); + } +} + +void CompositorBridgeParent::GetFrameUniformity(const LayersId& aLayersId, + FrameUniformityData* aOutData) { +} + +void CompositorBridgeParent::SetConfirmedTargetAPZC( + const LayersId& aLayersId, const uint64_t& aInputBlockId, + nsTArray<ScrollableLayerGuid>&& aTargets) { + if (!mApzcTreeManager || !mApzUpdater) { + return; + } + // Need to specifically bind this since it's overloaded. + void (APZCTreeManager::*setTargetApzcFunc)( + uint64_t, const nsTArray<ScrollableLayerGuid>&) = + &APZCTreeManager::SetTargetAPZC; + RefPtr<Runnable> task = + NewRunnableMethod<uint64_t, + StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>( + "layers::CompositorBridgeParent::SetConfirmedTargetAPZC", + mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, + std::move(aTargets)); + mApzUpdater->RunOnUpdaterThread(aLayersId, task.forget()); +} + +void CompositorBridgeParent::SetFixedLayerMargins(ScreenIntCoord aTop, + ScreenIntCoord aBottom) { + if (mApzcTreeManager) { + mApzcTreeManager->SetFixedLayerMargins(aTop, aBottom); + } + + ScheduleComposition(wr::RenderReasons::RESIZE); +} + +void CompositorBridgeParent::AddCompositor(CompositorBridgeParent* compositor, + uint64_t* outID) { + AssertIsInCompositorThread(); + + static uint64_t sNextID = 1; + + ++sNextID; + (*sCompositorMap)[sNextID] = compositor; + *outID = sNextID; +} + +CompositorBridgeParent* CompositorBridgeParent::RemoveCompositor(uint64_t id) { + AssertIsInCompositorThread(); + + CompositorMap::iterator it = sCompositorMap->find(id); + if (it == sCompositorMap->end()) { + return nullptr; + } + CompositorBridgeParent* retval = it->second; + sCompositorMap->erase(it); + return retval; +} + +void CompositorBridgeParent::NotifyVsync(const VsyncEvent& aVsync, + const LayersId& aLayersId) { + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + auto it = sIndirectLayerTrees.find(aLayersId); + if (it == sIndirectLayerTrees.end()) return; + + CompositorBridgeParent* cbp = it->second.mParent; + if (!cbp || !cbp->mWidget) return; + + RefPtr<VsyncObserver> obs = cbp->mWidget->GetVsyncObserver(); + if (!obs) return; + + obs->NotifyVsync(aVsync); +} + +/* static */ +void CompositorBridgeParent::ScheduleForcedComposition( + const LayersId& aLayersId, wr::RenderReasons aReasons) { + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + auto it = sIndirectLayerTrees.find(aLayersId); + if (it == sIndirectLayerTrees.end()) { + return; + } + + CompositorBridgeParent* cbp = it->second.mParent; + if (!cbp || !cbp->mWidget) { + return; + } + + if (cbp->mWrBridge) { + cbp->mWrBridge->ScheduleForcedGenerateFrame(aReasons); + } +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyChildCreated( + const LayersId& child, CompositorOptions* aOptions) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + NotifyChildCreated(child); + *aOptions = mOptions; + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyChildRecreated( + const LayersId& aChild, CompositorOptions* aOptions) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + + if (sIndirectLayerTrees.find(aChild) != sIndirectLayerTrees.end()) { + NS_WARNING("Invalid to register the same layer tree twice"); + return IPC_FAIL_NO_REASON(this); + } + + NotifyChildCreated(aChild); + *aOptions = mOptions; + return IPC_OK(); +} + +void CompositorBridgeParent::NotifyChildCreated(LayersId aChild) { + sIndirectLayerTreesLock->AssertCurrentThreadOwns(); + sIndirectLayerTrees[aChild].mParent = this; +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvMapAndNotifyChildCreated( + const LayersId& aChild, const base::ProcessId& aOwnerPid, + CompositorOptions* aOptions) { + // We only use this message when the remote compositor is in the GPU process. + // It is harmless to call it, though. + MOZ_ASSERT(XRE_IsGPUProcess()); + + LayerTreeOwnerTracker::Get()->Map(aChild, aOwnerPid); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + NotifyChildCreated(aChild); + *aOptions = mOptions; + return IPC_OK(); +} + +enum class CompositorOptionsChangeKind { + eSupported, + eBestEffort, + eUnsupported +}; + +static CompositorOptionsChangeKind ClassifyCompositorOptionsChange( + const CompositorOptions& aOld, const CompositorOptions& aNew) { + if (aOld == aNew) { + return CompositorOptionsChangeKind::eSupported; + } + if (aOld.EqualsIgnoringApzEnablement(aNew)) { + return CompositorOptionsChangeKind::eBestEffort; + } + return CompositorOptionsChangeKind::eUnsupported; +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvAdoptChild( + const LayersId& child) { + RefPtr<APZUpdater> oldApzUpdater; + APZCTreeManagerParent* parent; + bool apzEnablementChanged = false; + RefPtr<WebRenderBridgeParent> childWrBridge; + + // Before adopting the child, save the old compositor's root content + // controller. We may need this to clear old layer transforms associated + // with the child. + // This is outside the lock because GetGeckoContentControllerForRoot() + // does its own locking. + RefPtr<GeckoContentController> oldRootController = + GetGeckoContentControllerForRoot(child); + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + // If child is already belong to this CompositorBridgeParent, + // no need to handle adopting child. + if (sIndirectLayerTrees[child].mParent == this) { + return IPC_OK(); + } + + if (sIndirectLayerTrees[child].mParent) { + switch (ClassifyCompositorOptionsChange( + sIndirectLayerTrees[child].mParent->mOptions, mOptions)) { + case CompositorOptionsChangeKind::eUnsupported: { + MOZ_ASSERT(false, + "Moving tab between windows whose compositor options" + "differ in unsupported ways. Things may break in " + "unexpected ways"); + break; + } + case CompositorOptionsChangeKind::eBestEffort: { + NS_WARNING( + "Moving tab between windows with different APZ enablement. " + "This is supported on a best-effort basis, but some things may " + "break."); + apzEnablementChanged = true; + break; + } + case CompositorOptionsChangeKind::eSupported: { + // The common case, no action required. + break; + } + } + oldApzUpdater = sIndirectLayerTrees[child].mParent->mApzUpdater; + } + if (mWrBridge) { + childWrBridge = sIndirectLayerTrees[child].mWrBridge; + } + parent = sIndirectLayerTrees[child].mApzcTreeManagerParent; + } + + if (childWrBridge) { + MOZ_ASSERT(mWrBridge); + RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI(); + api = api->Clone(); + wr::Epoch newEpoch = childWrBridge->UpdateWebRender( + mWrBridge->CompositorScheduler(), std::move(api), + mWrBridge->AsyncImageManager(), + mWrBridge->GetTextureFactoryIdentifier()); + // Pretend we composited, since parent CompositorBridgeParent was replaced. + TimeStamp now = TimeStamp::Now(); + NotifyPipelineRendered(childWrBridge->PipelineId(), newEpoch, VsyncId(), + now, now, now); + } + + { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + // Update sIndirectLayerTrees[child].mParent after + // WebRenderBridgeParent::UpdateWebRender(). + NotifyChildCreated(child); + } + + if (oldApzUpdater) { + // If we are moving a child from an APZ-enabled window to an APZ-disabled + // window (which can happen if e.g. a WebExtension moves a tab into a + // popup window), try to handle it gracefully by clearing the old layer + // transforms associated with the child. (Since the new compositor is + // APZ-disabled, there will be nothing to update the transforms going + // forward.) + if (!mApzUpdater && oldRootController) { + // Tell the old APZCTreeManager not to send any more layer transforms + // for this layers ids. + oldApzUpdater->MarkAsDetached(child); + + // Clear the current transforms. + nsTArray<MatrixMessage> clear; + clear.AppendElement(MatrixMessage(Nothing(), ScreenRect(), child)); + oldRootController->NotifyLayerTransforms(std::move(clear)); + } + } + if (mApzUpdater) { + if (parent) { + MOZ_ASSERT(mApzcTreeManager); + parent->ChildAdopted(mApzcTreeManager, mApzUpdater); + } + mApzUpdater->NotifyLayerTreeAdopted(child, oldApzUpdater); + } + if (apzEnablementChanged) { + Unused << SendCompositorOptionsChanged(child, mOptions); + } + return IPC_OK(); +} + +PWebRenderBridgeParent* CompositorBridgeParent::AllocPWebRenderBridgeParent( + const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize, + const WindowKind& aWindowKind) { + MOZ_ASSERT(wr::AsLayersId(aPipelineId) == mRootLayerTreeID); + MOZ_ASSERT(!mWrBridge); + MOZ_ASSERT(!mCompositorScheduler); + MOZ_ASSERT(mWidget); + +#ifdef XP_WIN + if (mWidget && mWidget->AsWindows()) { + const auto options = mWidget->GetCompositorOptions(); + if (!options.UseSoftwareWebRender() && + (DeviceManagerDx::Get()->CanUseDComp() || + gfxVars::UseWebRenderFlipSequentialWin())) { + mWidget->AsWindows()->EnsureCompositorWindow(); + } else if (options.UseSoftwareWebRender() && + mWidget->AsWindows()->GetCompositorHwnd()) { + mWidget->AsWindows()->DestroyCompositorWindow(); + } + } +#endif + + RefPtr<widget::CompositorWidget> widget = mWidget; + wr::WrWindowId windowId = wr::NewWindowId(); + if (mApzUpdater) { + // If APZ is enabled, we need to register the APZ updater with the window id + // before the updater thread is created in WebRenderAPI::Create, so + // that the callback from the updater thread can find the right APZUpdater. + mApzUpdater->SetWebRenderWindowId(windowId); + } + if (mApzSampler) { + // Same as for mApzUpdater, but for the sampler thread. + mApzSampler->SetWebRenderWindowId(windowId); + } + if (mOMTASampler) { + // Same, but for the OMTA sampler. + mOMTASampler->SetWebRenderWindowId(windowId); + } + + nsCString error("FEATURE_FAILURE_WEBRENDER_INITIALIZE_UNSPECIFIED"); + RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create( + this, std::move(widget), windowId, aSize, aWindowKind, error); + if (!api) { + mWrBridge = + WebRenderBridgeParent::CreateDestroyed(aPipelineId, std::move(error)); + mWrBridge.get()->AddRef(); // IPDL reference + return mWrBridge; + } + +#ifdef MOZ_WIDGET_ANDROID + // On Android, WebRenderAPI::Resume() call is triggered from Java side. But + // Java side does not know about fallback to RenderCompositorOGLSWGL. In this + // fallback case, RenderCompositor::Resume() needs to be called from gfx code. + if (!mPaused && mWidget->GetCompositorOptions().UseSoftwareWebRender() && + mWidget->GetCompositorOptions().AllowSoftwareWebRenderOGL()) { + api->Resume(); + } +#endif + + wr::TransactionBuilder txn(api); + txn.SetRootPipeline(aPipelineId); + api->SendTransaction(txn); + + bool useCompositorWnd = false; +#ifdef XP_WIN + // Headless mode uses HeadlessWidget. + if (mWidget->AsWindows()) { + useCompositorWnd = !!mWidget->AsWindows()->GetCompositorHwnd(); + } +#endif + mAsyncImageManager = + new AsyncImagePipelineManager(api->Clone(), useCompositorWnd); + RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager; + mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, + std::move(api), std::move(asyncMgr), + mVsyncRate); + mWrBridge.get()->AddRef(); // IPDL reference + + mCompositorScheduler = mWrBridge->CompositorScheduler(); + MOZ_ASSERT(mCompositorScheduler); + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + MOZ_ASSERT(sIndirectLayerTrees[mRootLayerTreeID].mWrBridge == nullptr); + sIndirectLayerTrees[mRootLayerTreeID].mWrBridge = mWrBridge; + } + return mWrBridge; +} + +bool CompositorBridgeParent::DeallocPWebRenderBridgeParent( + PWebRenderBridgeParent* aActor) { + WebRenderBridgeParent* parent = static_cast<WebRenderBridgeParent*>(aActor); + { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + auto it = sIndirectLayerTrees.find(wr::AsLayersId(parent->PipelineId())); + if (it != sIndirectLayerTrees.end()) { + it->second.mWrBridge = nullptr; + } + } + parent->Release(); // IPDL reference + return true; +} + +void CompositorBridgeParent::NotifyMemoryPressure() { + if (mWrBridge) { + RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI(); + if (api) { + api->NotifyMemoryPressure(); + } + } +} + +void CompositorBridgeParent::AccumulateMemoryReport(wr::MemoryReport* aReport) { + if (mWrBridge) { + RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI(); + if (api) { + api->AccumulateMemoryReport(aReport); + } + } +} + +/*static*/ +void CompositorBridgeParent::InitializeStatics() { + gfxVars::SetForceSubpixelAAWherePossibleListener(&UpdateQualitySettings); + gfxVars::SetWebRenderDebugFlagsListener(&UpdateDebugFlags); + gfxVars::SetWebRenderBoolParametersListener(&UpdateWebRenderBoolParameters); + gfxVars::SetWebRenderBatchingLookbackListener(&UpdateWebRenderParameters); + gfxVars::SetWebRenderBlobTileSizeListener(&UpdateWebRenderParameters); + gfxVars::SetWebRenderBatchedUploadThresholdListener( + &UpdateWebRenderParameters); + + gfxVars::SetWebRenderProfilerUIListener(&UpdateWebRenderProfilerUI); +} + +/*static*/ +void CompositorBridgeParent::UpdateQualitySettings() { + if (!CompositorThreadHolder::IsInCompositorThread()) { + if (CompositorThread()) { + CompositorThread()->Dispatch( + NewRunnableFunction("CompositorBridgeParent::UpdateQualitySettings", + &CompositorBridgeParent::UpdateQualitySettings)); + } + + // If there is no compositor thread, e.g. due to shutdown, then we can + // safefully just ignore this request. + return; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void { + if (!wrBridge->IsRootWebRenderBridgeParent()) { + return; + } + wrBridge->UpdateQualitySettings(); + }); +} + +/*static*/ +void CompositorBridgeParent::UpdateDebugFlags() { + if (!CompositorThreadHolder::IsInCompositorThread()) { + if (CompositorThread()) { + CompositorThread()->Dispatch( + NewRunnableFunction("CompositorBridgeParent::UpdateDebugFlags", + &CompositorBridgeParent::UpdateDebugFlags)); + } + + // If there is no compositor thread, e.g. due to shutdown, then we can + // safefully just ignore this request. + return; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void { + if (!wrBridge->IsRootWebRenderBridgeParent()) { + return; + } + wrBridge->UpdateDebugFlags(); + }); +} + +/*static*/ +void CompositorBridgeParent::UpdateWebRenderBoolParameters() { + if (!CompositorThreadHolder::IsInCompositorThread()) { + if (CompositorThread()) { + CompositorThread()->Dispatch(NewRunnableFunction( + "CompositorBridgeParent::UpdateWebRenderBoolParameters", + &CompositorBridgeParent::UpdateWebRenderBoolParameters)); + } + + return; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void { + if (!wrBridge->IsRootWebRenderBridgeParent()) { + return; + } + wrBridge->UpdateBoolParameters(); + }); +} + +/*static*/ +void CompositorBridgeParent::UpdateWebRenderParameters() { + if (!CompositorThreadHolder::IsInCompositorThread()) { + if (CompositorThread()) { + CompositorThread()->Dispatch(NewRunnableFunction( + "CompositorBridgeParent::UpdateWebRenderParameters", + &CompositorBridgeParent::UpdateWebRenderParameters)); + } + + return; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void { + if (!wrBridge->IsRootWebRenderBridgeParent()) { + return; + } + wrBridge->UpdateParameters(); + }); +} + +/*static*/ +void CompositorBridgeParent::UpdateWebRenderProfilerUI() { + if (!sIndirectLayerTreesLock) { + return; + } + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void { + if (!wrBridge->IsRootWebRenderBridgeParent()) { + return; + } + wrBridge->UpdateProfilerUI(); + }); +} + +RefPtr<WebRenderBridgeParent> CompositorBridgeParent::GetWebRenderBridgeParent() + const { + return mWrBridge; +} + +Maybe<TimeStamp> CompositorBridgeParent::GetTestingTimeStamp() const { + return mTestTime; +} + +void EraseLayerState(LayersId aId) { + RefPtr<APZUpdater> apz; + RefPtr<WebRenderBridgeParent> wrBridge; + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + auto iter = sIndirectLayerTrees.find(aId); + if (iter != sIndirectLayerTrees.end()) { + CompositorBridgeParent* parent = iter->second.mParent; + if (parent) { + apz = parent->GetAPZUpdater(); + } + wrBridge = iter->second.mWrBridge; + sIndirectLayerTrees.erase(iter); + } + } + + if (apz) { + apz->NotifyLayerTreeRemoved(aId); + } + + if (wrBridge) { + wrBridge->Destroy(); + } +} + +/*static*/ +void CompositorBridgeParent::DeallocateLayerTreeId(LayersId aId) { + MOZ_ASSERT(NS_IsMainThread()); + // Here main thread notifies compositor to remove an element from + // sIndirectLayerTrees. This removed element might be queried soon. + // Checking the elements of sIndirectLayerTrees exist or not before using. + if (!CompositorThread()) { + gfxCriticalError() << "Attempting to post to an invalid Compositor Thread"; + return; + } + CompositorThread()->Dispatch( + NewRunnableFunction("EraseLayerStateRunnable", &EraseLayerState, aId)); +} + +static void UpdateControllerForLayersId(LayersId aLayersId, + GeckoContentController* aController) { + // Adopt ref given to us by SetControllerForLayerTree() + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[aLayersId].mController = + already_AddRefed<GeckoContentController>(aController); +} + +ScopedLayerTreeRegistration::ScopedLayerTreeRegistration( + LayersId aLayersId, GeckoContentController* aController) + : mLayersId(aLayersId) { + EnsureLayerTreeMapReady(); + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[aLayersId].mController = aController; +} + +ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration() { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees.erase(mLayersId); +} + +/*static*/ +void CompositorBridgeParent::SetControllerForLayerTree( + LayersId aLayersId, GeckoContentController* aController) { + // This ref is adopted by UpdateControllerForLayersId(). + aController->AddRef(); + CompositorThread()->Dispatch(NewRunnableFunction( + "UpdateControllerForLayersIdRunnable", &UpdateControllerForLayersId, + aLayersId, aController)); +} + +/*static*/ +already_AddRefed<IAPZCTreeManager> CompositorBridgeParent::GetAPZCTreeManager( + LayersId aLayersId) { + EnsureLayerTreeMapReady(); + MonitorAutoLock lock(*sIndirectLayerTreesLock); + LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aLayersId); + if (sIndirectLayerTrees.end() == cit) { + return nullptr; + } + LayerTreeState* lts = &cit->second; + + RefPtr<IAPZCTreeManager> apzctm = + lts->mParent ? lts->mParent->mApzcTreeManager.get() : nullptr; + return apzctm.forget(); +} + +static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (profiler_thread_is_being_profiled_for_markers()) { + // Tracks when a vsync occurs according to the HardwareComposer. + struct VsyncMarker { + static constexpr mozilla::Span<const char> MarkerTypeName() { + return mozilla::MakeStringSpan("VsyncTimestamp"); + } + static void StreamJSONMarkerData( + baseprofiler::SpliceableJSONWriter& aWriter) {} + static MarkerSchema MarkerTypeDisplay() { + using MS = MarkerSchema; + MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable}; + // Nothing outside the defaults. + return schema; + } + }; + profiler_add_marker("VsyncTimestamp", geckoprofiler::category::GRAPHICS, + MarkerTiming::InstantAt(aVsyncTimestamp), + VsyncMarker{}); + } +} + +/*static */ +void CompositorBridgeParent::PostInsertVsyncProfilerMarker( + TimeStamp aVsyncTimestamp) { + // Called in the vsync thread + if (profiler_is_active() && CompositorThreadHolder::IsActive()) { + CompositorThread()->Dispatch( + NewRunnableFunction("InsertVsyncProfilerMarkerRunnable", + InsertVsyncProfilerMarker, aVsyncTimestamp)); + } +} + +widget::PCompositorWidgetParent* +CompositorBridgeParent::AllocPCompositorWidgetParent( + const CompositorWidgetInitData& aInitData) { +#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING) + if (mWidget) { + // Should not create two widgets on the same compositor. + return nullptr; + } + + widget::CompositorWidgetParent* widget = + new widget::CompositorWidgetParent(aInitData, mOptions); + widget->AddRef(); + + // Sending the constructor acts as initialization as well. + mWidget = widget; + return widget; +#else + return nullptr; +#endif +} + +bool CompositorBridgeParent::DeallocPCompositorWidgetParent( + PCompositorWidgetParent* aActor) { +#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING) + static_cast<widget::CompositorWidgetParent*>(aActor)->Release(); + return true; +#else + return false; +#endif +} + +CompositorController* +CompositorBridgeParent::LayerTreeState::GetCompositorController() const { + return mParent; +} + +void CompositorBridgeParent::NotifyDidSceneBuild( + RefPtr<const wr::WebRenderPipelineInfo> aInfo) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mPaused) { + return; + } + + if (mWrBridge) { + mWrBridge->NotifyDidSceneBuild(aInfo); + } +} + +void CompositorBridgeParent::NotifyDidRender(const VsyncId& aCompositeStartId, + TimeStamp& aCompositeStart, + TimeStamp& aRenderStart, + TimeStamp& aCompositeEnd, + wr::RendererStats* aStats) { + if (!mWrBridge) { + return; + } + + MOZ_RELEASE_ASSERT(mWrBridge->IsRootWebRenderBridgeParent()); + + RefPtr<UiCompositorControllerParent> uiController = + UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeID); + + if (uiController && mIsForcedFirstPaint) { + uiController->NotifyFirstPaint(); + mIsForcedFirstPaint = false; + } + + nsTArray<CompositionPayload> payload = + mWrBridge->TakePendingScrollPayload(aCompositeStartId); + if (!payload.IsEmpty()) { + RecordCompositionPayloadsPresented(aCompositeEnd, payload); + } + + nsTArray<ImageCompositeNotificationInfo> notifications; + mWrBridge->ExtractImageCompositeNotifications(¬ifications); + if (!notifications.IsEmpty()) { + Unused << ImageBridgeParent::NotifyImageComposites(notifications); + } +} + +bool CompositorBridgeParent::sStable = false; +uint32_t CompositorBridgeParent::sFramesComposited = 0; + +/* static */ void CompositorBridgeParent::ResetStable() { + if (!CompositorThreadHolder::IsInCompositorThread()) { + if (CompositorThread()) { + CompositorThread()->Dispatch( + NewRunnableFunction("CompositorBridgeParent::ResetStable", + &CompositorBridgeParent::ResetStable)); + } + + // If there is no compositor thread, e.g. due to shutdown, then we can + // safefully just ignore this request. + return; + } + + sStable = false; + sFramesComposited = 0; +} + +void CompositorBridgeParent::MaybeDeclareStable() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + if (sStable) { + return; + } + + // Once we render as many frames as the threshold, we declare this instance of + // the GPU process 'stable'. This causes the parent process to always respawn + // the GPU process if it crashes. + if (++sFramesComposited >= + StaticPrefs::layers_gpu_process_stable_frame_threshold()) { + sStable = true; + + NS_DispatchToMainThread(NS_NewRunnableFunction( + "CompositorBridgeParent::MaybeDeclareStable", []() -> void { + if (XRE_IsParentProcess()) { + GPUProcessManager* gpm = GPUProcessManager::Get(); + if (gpm) { + gpm->OnProcessDeclaredStable(); + } + } else { + gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton(); + if (gpu && gpu->CanSend()) { + Unused << gpu->SendDeclareStable(); + } + } + })); + } +} + +void CompositorBridgeParent::NotifyPipelineRendered( + const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, + const VsyncId& aCompositeStartId, TimeStamp& aCompositeStart, + TimeStamp& aRenderStart, TimeStamp& aCompositeEnd, + wr::RendererStats* aStats) { + if (!mWrBridge || !mAsyncImageManager) { + return; + } + + bool isRoot = mWrBridge->PipelineId() == aPipelineId; + RefPtr<WebRenderBridgeParent> wrBridge = + isRoot ? mWrBridge + : RefPtr<WebRenderBridgeParent>( + mAsyncImageManager->GetWrBridge(aPipelineId)); + if (!wrBridge) { + return; + } + + CompositorBridgeParentBase* compBridge = + isRoot ? this : wrBridge->GetCompositorBridge(); + if (!compBridge) { + return; + } + + MOZ_RELEASE_ASSERT(isRoot == wrBridge->IsRootWebRenderBridgeParent()); + + wrBridge->RemoveEpochDataPriorTo(aEpoch); + + nsTArray<FrameStats> stats; + nsTArray<TransactionId> transactions; + + RefPtr<UiCompositorControllerParent> uiController = + UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeID); + + wrBridge->FlushTransactionIdsForEpoch( + aEpoch, aCompositeStartId, aCompositeStart, aRenderStart, aCompositeEnd, + uiController, aStats, stats, transactions); + if (transactions.IsEmpty()) { + MOZ_ASSERT(stats.IsEmpty()); + return; + } + + MaybeDeclareStable(); + + LayersId layersId = isRoot ? LayersId{0} : wrBridge->GetLayersId(); + Unused << compBridge->SendDidComposite(layersId, transactions, + aCompositeStart, aCompositeEnd); + + if (!stats.IsEmpty()) { + Unused << SendNotifyFrameStats(stats); + } +} + +RefPtr<AsyncImagePipelineManager> +CompositorBridgeParent::GetAsyncImagePipelineManager() const { + return mAsyncImageManager; +} + +/* static */ CompositorBridgeParent::LayerTreeState* +CompositorBridgeParent::GetIndirectShadowTree(LayersId aId) { + // Only the compositor thread should use this method variant + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId); + if (sIndirectLayerTrees.end() == cit) { + return nullptr; + } + return &cit->second; +} + +/* static */ +bool CompositorBridgeParent::CallWithIndirectShadowTree( + LayersId aId, + const std::function<void(CompositorBridgeParent::LayerTreeState&)>& aFunc) { + if (!sIndirectLayerTreesLock) { + // Can hapen during shutdown + return false; + } + // Note that this does not make things universally threadsafe just because the + // sIndirectLayerTreesLock mutex is held. This is because the compositor + // thread can mutate the LayerTreeState outside the lock. It does however + // ensure that the *storage* for the LayerTreeState remains stable, since we + // should always hold the lock when adding/removing entries to the map. + MonitorAutoLock lock(*sIndirectLayerTreesLock); + LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId); + if (sIndirectLayerTrees.end() == cit) { + return false; + } + aFunc(cit->second); + return true; +} + +static CompositorBridgeParent::LayerTreeState* GetStateForRoot( + LayersId aContentLayersId, const MonitorAutoLock& aProofOfLock) { + CompositorBridgeParent::LayerTreeState* contentState = nullptr; + LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aContentLayersId); + if (sIndirectLayerTrees.end() != itr) { + contentState = &itr->second; + } + + // |contentState| is the state for the content process, but we want the + // APZCTMParent for the parent process owning that content process. So we have + // to jump to the LayerTreeState for the root layer tree id for that layer + // tree, and use the mApzcTreeManagerParent from that. This should also work + // with nested content processes, because RootLayerTreeId() will bypass any + // intermediate processes' ids and go straight to the root. + if (contentState && contentState->mParent) { + LayersId rootLayersId = contentState->mParent->RootLayerTreeId(); + itr = sIndirectLayerTrees.find(rootLayersId); + CompositorBridgeParent::LayerTreeState* rootState = + (sIndirectLayerTrees.end() != itr) ? &itr->second : nullptr; + return rootState; + } + + // Don't return contentState, that would be a lie! + return nullptr; +} + +/* static */ +APZCTreeManagerParent* CompositorBridgeParent::GetApzcTreeManagerParentForRoot( + LayersId aContentLayersId) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState* state = + GetStateForRoot(aContentLayersId, lock); + return state ? state->mApzcTreeManagerParent : nullptr; +} + +/* static */ +GeckoContentController* +CompositorBridgeParent::GetGeckoContentControllerForRoot( + LayersId aContentLayersId) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState* state = + GetStateForRoot(aContentLayersId, lock); + return state ? state->mController.get() : nullptr; +} + +PTextureParent* CompositorBridgeParent::AllocPTextureParent( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const LayersId& aId, const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId) { + return TextureHost::CreateIPDLActor( + this, aSharedData, std::move(aReadLock), aLayersBackend, aFlags, + mCompositorManager->GetContentId(), aSerial, aExternalImageId); +} + +bool CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) { + return TextureHost::DestroyIPDLActor(actor); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvInitPCanvasParent( + Endpoint<PCanvasParent>&& aEndpoint) { + MOZ_CRASH("PCanvasParent shouldn't be created via CompositorBridgeParent."); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvReleasePCanvasParent() { + MOZ_CRASH("PCanvasParent shouldn't be released via CompositorBridgeParent."); +} + +bool CompositorBridgeParent::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void CompositorBridgeParent::NotifyWebRenderDisableNativeCompositor() { + MOZ_ASSERT(CompositorThread()->IsOnCurrentThread()); + if (mWrBridge) { + mWrBridge->DisableNativeCompositor(); + } +} + +int32_t RecordContentFrameTime( + const VsyncId& aTxnId, const TimeStamp& aVsyncStart, + const TimeStamp& aTxnStart, const VsyncId& aCompositeId, + const TimeStamp& aCompositeEnd, const TimeDuration& aFullPaintTime, + const TimeDuration& aVsyncRate, bool aContainsSVGGroup, + bool aRecordUploadStats, wr::RendererStats* aStats /* = nullptr */) { + double latencyMs = (aCompositeEnd - aTxnStart).ToMilliseconds(); + double latencyNorm = latencyMs / aVsyncRate.ToMilliseconds(); + int32_t fracLatencyNorm = lround(latencyNorm * 100.0); + + if (profiler_thread_is_being_profiled_for_markers()) { + struct ContentFrameMarker { + static constexpr Span<const char> MarkerTypeName() { + return MakeStringSpan("CONTENT_FRAME_TIME"); + } + static void StreamJSONMarkerData( + baseprofiler::SpliceableJSONWriter& aWriter) {} + static MarkerSchema MarkerTypeDisplay() { + using MS = MarkerSchema; + MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable}; + // Nothing outside the defaults. + return schema; + } + }; + + profiler_add_marker("CONTENT_FRAME_TIME", geckoprofiler::category::GRAPHICS, + MarkerTiming::Interval(aTxnStart, aCompositeEnd), + ContentFrameMarker{}); + } + + Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm); + + if (!(aTxnId == VsyncId()) && aVsyncStart) { + latencyMs = (aCompositeEnd - aVsyncStart).ToMilliseconds(); + latencyNorm = latencyMs / aVsyncRate.ToMilliseconds(); + fracLatencyNorm = lround(latencyNorm * 100.0); + int32_t result = fracLatencyNorm; + Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME_VSYNC, fracLatencyNorm); + + if (aContainsSVGGroup) { + Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME_WITH_SVG, + fracLatencyNorm); + } + + // Record CONTENT_FRAME_TIME_REASON. + // + // Note that deseralizing a layers update (RecvUpdate) can delay the receipt + // of the composite vsync message + // (CompositorBridgeParent::CompositeToTarget), since they're using the same + // thread. This can mean that compositing might start significantly late, + // but this code will still detect it as having successfully started on the + // right vsync (which is somewhat correct). We'd now have reduced time left + // in the vsync interval to finish compositing, so the chances of a missed + // frame increases. This is effectively including the RecvUpdate work as + // part of the 'compositing' phase for this metric, but it isn't included in + // COMPOSITE_TIME, and *is* included in CONTENT_FULL_PAINT_TIME. + // + // Also of note is that when the root WebRenderBridgeParent decides to + // skip a composite (due to the Renderer being busy), that won't notify + // child WebRenderBridgeParents. That failure will show up as the + // composite starting late (since it did), but it's really a fault of a + // slow composite on the previous frame, not a slow + // CONTENT_FULL_PAINT_TIME. It would be nice to have a separate bucket for + // this category (scene was ready on the next vsync, but we chose not to + // composite), but I can't find a way to locate the right child + // WebRenderBridgeParents from the root. WebRender notifies us of the + // child pipelines contained within a render, after it finishes, but I + // can't see how to query what child pipeline would have been rendered, + // when we choose to not do it. + if (fracLatencyNorm < 200) { + // Success + Telemetry::AccumulateCategorical( + LABELS_CONTENT_FRAME_TIME_REASON::OnTime); + } else { + if (aCompositeId == VsyncId()) { + // aCompositeId is 0, possibly something got trigged from + // outside vsync? + Telemetry::AccumulateCategorical( + LABELS_CONTENT_FRAME_TIME_REASON::NoVsyncNoId); + } else if (aTxnId >= aCompositeId) { + // Vsync ids are nonsensical, maybe we're trying to catch up? + Telemetry::AccumulateCategorical( + LABELS_CONTENT_FRAME_TIME_REASON::NoVsync); + } else if (aCompositeId - aTxnId > 1) { + // Composite started late (and maybe took too long as well) + if (aFullPaintTime >= TimeDuration::FromMilliseconds(20)) { + Telemetry::AccumulateCategorical( + LABELS_CONTENT_FRAME_TIME_REASON::MissedCompositeLong); + } else if (aFullPaintTime >= TimeDuration::FromMilliseconds(10)) { + Telemetry::AccumulateCategorical( + LABELS_CONTENT_FRAME_TIME_REASON::MissedCompositeMid); + } else if (aFullPaintTime >= TimeDuration::FromMilliseconds(5)) { + Telemetry::AccumulateCategorical( + LABELS_CONTENT_FRAME_TIME_REASON::MissedCompositeLow); + } else { + Telemetry::AccumulateCategorical( + LABELS_CONTENT_FRAME_TIME_REASON::MissedComposite); + } + } else { + // Composite started on time, but must have taken too long. + Telemetry::AccumulateCategorical( + LABELS_CONTENT_FRAME_TIME_REASON::SlowComposite); + } + } + + if (aRecordUploadStats) { + if (aStats) { + latencyMs -= (double(aStats->resource_upload_time) / 1000000.0); + latencyNorm = latencyMs / aVsyncRate.ToMilliseconds(); + fracLatencyNorm = lround(latencyNorm * 100.0); + } + Telemetry::Accumulate( + Telemetry::CONTENT_FRAME_TIME_WITHOUT_RESOURCE_UPLOAD, + fracLatencyNorm); + + if (aStats) { + latencyMs -= (double(aStats->gpu_cache_upload_time) / 1000000.0); + latencyNorm = latencyMs / aVsyncRate.ToMilliseconds(); + fracLatencyNorm = lround(latencyNorm * 100.0); + } + Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME_WITHOUT_UPLOAD, + fracLatencyNorm); + } + return result; + } + + return 0; +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvBeginRecording( + const TimeStamp& aRecordingStart, BeginRecordingResolver&& aResolve) { + if (mHaveCompositionRecorder) { + aResolve(false); + return IPC_OK(); + } + + if (mWrBridge) { + mWrBridge->BeginRecording(aRecordingStart); + } + + mHaveCompositionRecorder = true; + aResolve(true); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvEndRecording( + EndRecordingResolver&& aResolve) { + if (!mHaveCompositionRecorder) { + aResolve(Nothing()); + return IPC_OK(); + } + + if (mWrBridge) { + mWrBridge->EndRecording()->Then( + NS_GetCurrentThread(), __func__, + [resolve{aResolve}](FrameRecording&& recording) { + resolve(Some(std::move(recording))); + }, + [resolve{aResolve}]() { resolve(Nothing()); }); + } else { + aResolve(Nothing()); + } + + mHaveCompositionRecorder = false; + + return IPC_OK(); +} + +void CompositorBridgeParent::FlushPendingWrTransactionEventsWithWait() { + if (!mWrBridge) { + return; + } + + std::vector<RefPtr<WebRenderBridgeParent>> bridgeParents; + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([&](LayerTreeState* lts, LayersId) -> void { + if (lts->mWrBridge) { + bridgeParents.emplace_back(lts->mWrBridge); + } + }); + } + + for (auto& bridge : bridgeParents) { + bridge->FlushPendingWrTransactionEventsWithWait(); + } +} + +void RecordCompositionPayloadsPresented( + const TimeStamp& aCompositionEndTime, + const nsTArray<CompositionPayload>& aPayloads) { + if (aPayloads.Length()) { + TimeStamp presented = aCompositionEndTime; + for (const CompositionPayload& payload : aPayloads) { + if (profiler_thread_is_being_profiled_for_markers()) { + MOZ_RELEASE_ASSERT(payload.mType <= kHighestCompositionPayloadType); + nsAutoCString name( + kCompositionPayloadTypeNames[uint8_t(payload.mType)]); + name.AppendLiteral(" Payload Presented"); + // This doesn't really need to be a text marker. Once we have a version + // of profiler_add_marker that accepts both a start time and an end + // time, we could use that here. + nsPrintfCString text( + "Latency: %dms", + int32_t((presented - payload.mTimeStamp).ToMilliseconds())); + PROFILER_MARKER_TEXT( + name, GRAPHICS, + MarkerTiming::Interval(payload.mTimeStamp, presented), text); + } + + if (payload.mType == CompositionPayloadType::eKeyPress) { + Telemetry::AccumulateTimeDelta( + mozilla::Telemetry::KEYPRESS_PRESENT_LATENCY, payload.mTimeStamp, + presented); + } else if (payload.mType == CompositionPayloadType::eAPZScroll) { + Telemetry::AccumulateTimeDelta( + mozilla::Telemetry::SCROLL_PRESENT_LATENCY, payload.mTimeStamp, + presented); + } else if (payload.mType == + CompositionPayloadType::eMouseUpFollowedByClick) { + Telemetry::AccumulateTimeDelta( + mozilla::Telemetry::MOUSEUP_FOLLOWED_BY_CLICK_PRESENT_LATENCY, + payload.mTimeStamp, presented); + } + } + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h new file mode 100644 index 0000000000..6b15b535f1 --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeParent.h @@ -0,0 +1,656 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_CompositorBridgeParent_h +#define mozilla_layers_CompositorBridgeParent_h + +#include <stdint.h> // for uint64_t +#include <unordered_map> +#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 +#include "mozilla/Maybe.h" +#include "mozilla/Monitor.h" // for Monitor +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/TimeStamp.h" // for TimeStamp +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/SharedMemory.h" +#include "mozilla/layers/CompositorController.h" +#include "mozilla/layers/CompositorVsyncSchedulerOwner.h" +#include "mozilla/layers/FocusTarget.h" +#include "mozilla/layers/ISurfaceAllocator.h" // for IShmemAllocator +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/PCompositorBridgeParent.h" +#include "mozilla/webrender/WebRenderTypes.h" + +namespace mozilla { + +namespace gfx { +class GPUProcessManager; +class GPUParent; +} // namespace gfx + +namespace ipc { +class Shmem; +} // namespace ipc + +namespace widget { +class CompositorWidget; +} + +namespace wr { +class WebRenderPipelineInfo; +struct Epoch; +struct MemoryReport; +struct PipelineId; +struct RendererStats; +} // namespace wr + +namespace layers { + +class APZCTreeManager; +class APZCTreeManagerParent; +class APZSampler; +class APZTestData; +class APZUpdater; +class AsyncImagePipelineManager; +class CompositorAnimationStorage; +class CompositorBridgeParent; +class CompositorManagerParent; +class CompositorVsyncScheduler; +class FrameUniformityData; +class GeckoContentController; +class IAPZCTreeManager; +class OMTASampler; +class ContentCompositorBridgeParent; +class CompositorThreadHolder; +class InProcessCompositorSession; +class UiCompositorControllerParent; +class WebRenderBridgeParent; +class WebRenderScrollDataWrapper; +struct CollectedFrames; + +struct ScopedLayerTreeRegistration { + // For WebRender + ScopedLayerTreeRegistration(LayersId aLayersId, + GeckoContentController* aController); + ~ScopedLayerTreeRegistration(); + + private: + LayersId mLayersId; +}; + +class CompositorBridgeParentBase : public PCompositorBridgeParent, + public HostIPCAllocator, + public mozilla::ipc::IShmemAllocator { + friend class PCompositorBridgeParent; + + public: + explicit CompositorBridgeParentBase(CompositorManagerParent* aManager); + + virtual bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) { + return true; + } + virtual void LeaveTestMode(const LayersId& aId) {} + enum class TransformsToSkip : uint8_t { NoneOfThem = 0, APZ = 1 }; + virtual void SetTestAsyncScrollOffset( + const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId, + const CSSPoint& aPoint) = 0; + virtual void SetTestAsyncZoom(const LayersId& aLayersId, + const ScrollableLayerGuid::ViewID& aScrollId, + const LayerToParentLayerScale& aZoom) = 0; + virtual void FlushApzRepaints(const LayersId& aLayersId) = 0; + virtual void GetAPZTestData(const LayersId& aLayersId, + APZTestData* aOutData) {} + virtual void GetFrameUniformity(const LayersId& aLayersId, + FrameUniformityData* data) = 0; + virtual void SetConfirmedTargetAPZC( + const LayersId& aLayersId, const uint64_t& aInputBlockId, + nsTArray<ScrollableLayerGuid>&& aTargets) = 0; + + IShmemAllocator* AsShmemAllocator() override { return this; } + + CompositorBridgeParentBase* AsCompositorBridgeParentBase() override { + return this; + } + + mozilla::ipc::IPCResult RecvSyncWithCompositor() { return IPC_OK(); } + + mozilla::ipc::IPCResult Recv__delete__() override { return IPC_OK(); } + + virtual void ObserveLayersUpdate(LayersId aLayersId, + LayersObserverEpoch aEpoch, + bool aActive) = 0; + + // HostIPCAllocator + base::ProcessId GetChildProcessId() override; + dom::ContentParentId GetContentId() override; + void NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) override; + void SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) override; + + // IShmemAllocator + bool AllocShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override; + bool AllocUnsafeShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override; + bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + + NS_IMETHOD_(MozExternalRefCountType) AddRef() override { + return HostIPCAllocator::AddRef(); + } + NS_IMETHOD_(MozExternalRefCountType) Release() override { + return HostIPCAllocator::Release(); + } + virtual bool IsRemote() const { return false; } + + virtual UniquePtr<SurfaceDescriptor> LookupSurfaceDescriptorForClientTexture( + const int64_t aTextureId) { + MOZ_CRASH("Should only be called on ContentCompositorBridgeParent."); + } + + virtual void NotifyMemoryPressure() {} + virtual void AccumulateMemoryReport(wr::MemoryReport*) {} + + protected: + virtual ~CompositorBridgeParentBase(); + + virtual PAPZParent* AllocPAPZParent(const LayersId& layersId) = 0; + virtual bool DeallocPAPZParent(PAPZParent* aActor) = 0; + + virtual PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent( + const LayersId& layersId) = 0; + virtual bool DeallocPAPZCTreeManagerParent( + PAPZCTreeManagerParent* aActor) = 0; + + virtual PTextureParent* AllocPTextureParent( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aBackend, const TextureFlags& aTextureFlags, + const LayersId& id, const uint64_t& aSerial, + const MaybeExternalImageId& aExternalImageId) = 0; + virtual bool DeallocPTextureParent(PTextureParent* aActor) = 0; + + virtual PWebRenderBridgeParent* AllocPWebRenderBridgeParent( + const PipelineId& pipelineId, const LayoutDeviceIntSize& aSize, + const WindowKind& aWindowKind) = 0; + virtual bool DeallocPWebRenderBridgeParent( + PWebRenderBridgeParent* aActor) = 0; + + virtual PCompositorWidgetParent* AllocPCompositorWidgetParent( + const CompositorWidgetInitData& aInitData) = 0; + virtual bool DeallocPCompositorWidgetParent( + PCompositorWidgetParent* aActor) = 0; + + virtual mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& id) = 0; + virtual mozilla::ipc::IPCResult RecvFlushRenderingAsync( + const wr::RenderReasons& aReasons) = 0; + virtual mozilla::ipc::IPCResult RecvForcePresent( + const wr::RenderReasons& aReasons) = 0; + virtual mozilla::ipc::IPCResult RecvBeginRecording( + const TimeStamp& aRecordingStart, BeginRecordingResolver&& aResolve) = 0; + virtual mozilla::ipc::IPCResult RecvEndRecording( + EndRecordingResolver&& aResolve) = 0; + virtual mozilla::ipc::IPCResult RecvInitialize( + const LayersId& rootLayerTreeId) = 0; + virtual mozilla::ipc::IPCResult RecvWillClose() = 0; + virtual mozilla::ipc::IPCResult RecvPause() = 0; + virtual mozilla::ipc::IPCResult RecvRequestFxrOutput() = 0; + virtual mozilla::ipc::IPCResult RecvResume() = 0; + virtual mozilla::ipc::IPCResult RecvResumeAsync() = 0; + virtual mozilla::ipc::IPCResult RecvNotifyChildCreated( + const LayersId& id, CompositorOptions* compositorOptions) = 0; + virtual mozilla::ipc::IPCResult RecvMapAndNotifyChildCreated( + const LayersId& id, const ProcessId& owner, + CompositorOptions* compositorOptions) = 0; + virtual mozilla::ipc::IPCResult RecvNotifyChildRecreated( + const LayersId& id, CompositorOptions* compositorOptions) = 0; + virtual mozilla::ipc::IPCResult RecvFlushRendering( + const wr::RenderReasons& aReasons) = 0; + virtual mozilla::ipc::IPCResult RecvNotifyMemoryPressure() = 0; + virtual mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() = 0; + virtual mozilla::ipc::IPCResult RecvStartFrameTimeRecording( + const int32_t& bufferSize, uint32_t* startIndex) = 0; + virtual mozilla::ipc::IPCResult RecvStopFrameTimeRecording( + const uint32_t& startIndex, nsTArray<float>* intervals) = 0; + virtual mozilla::ipc::IPCResult RecvCheckContentOnlyTDR( + const uint32_t& sequenceNum, bool* isContentOnlyTDR) = 0; + virtual mozilla::ipc::IPCResult RecvInitPCanvasParent( + Endpoint<PCanvasParent>&& aEndpoint) = 0; + virtual mozilla::ipc::IPCResult RecvReleasePCanvasParent() = 0; + + bool mCanSend; + + protected: + RefPtr<CompositorManagerParent> mCompositorManager; +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS( + CompositorBridgeParentBase::TransformsToSkip) + +class CompositorBridgeParent final : public CompositorBridgeParentBase, + public CompositorController { + friend class CompositorThreadHolder; + friend class InProcessCompositorSession; + friend class gfx::GPUProcessManager; + friend class gfx::GPUParent; + friend class PCompositorBridgeParent; + + public: + NS_IMETHOD_(MozExternalRefCountType) AddRef() override { + return CompositorBridgeParentBase::AddRef(); + } + NS_IMETHOD_(MozExternalRefCountType) Release() override { + return CompositorBridgeParentBase::Release(); + } + + explicit CompositorBridgeParent(CompositorManagerParent* aManager, + CSSToLayoutDeviceScale aScale, + const TimeDuration& aVsyncRate, + const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, + uint64_t aInnerWindowId); + + void InitSameProcess(widget::CompositorWidget* aWidget, + const LayersId& aLayerTreeId); + + mozilla::ipc::IPCResult RecvInitialize( + const LayersId& aRootLayerTreeId) override; + mozilla::ipc::IPCResult RecvWillClose() override; + mozilla::ipc::IPCResult RecvPause() override; + mozilla::ipc::IPCResult RecvRequestFxrOutput() override; + mozilla::ipc::IPCResult RecvResume() override; + mozilla::ipc::IPCResult RecvResumeAsync() override; + mozilla::ipc::IPCResult RecvNotifyChildCreated( + const LayersId& child, CompositorOptions* aOptions) override; + mozilla::ipc::IPCResult RecvMapAndNotifyChildCreated( + const LayersId& child, const base::ProcessId& pid, + CompositorOptions* aOptions) override; + mozilla::ipc::IPCResult RecvNotifyChildRecreated( + const LayersId& child, CompositorOptions* aOptions) override; + mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& child) override; + mozilla::ipc::IPCResult RecvFlushRendering( + const wr::RenderReasons& aReasons) override; + mozilla::ipc::IPCResult RecvFlushRenderingAsync( + const wr::RenderReasons& aReasons) override; + mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override; + mozilla::ipc::IPCResult RecvForcePresent( + const wr::RenderReasons& aReasons) override; + + mozilla::ipc::IPCResult RecvStartFrameTimeRecording( + const int32_t& aBufferSize, uint32_t* aOutStartIndex) override; + mozilla::ipc::IPCResult RecvStopFrameTimeRecording( + const uint32_t& aStartIndex, nsTArray<float>* intervals) override; + + mozilla::ipc::IPCResult RecvCheckContentOnlyTDR( + const uint32_t& sequenceNum, bool* isContentOnlyTDR) override { + return IPC_OK(); + } + + mozilla::ipc::IPCResult RecvNotifyMemoryPressure() override; + mozilla::ipc::IPCResult RecvBeginRecording( + const TimeStamp& aRecordingStart, + BeginRecordingResolver&& aResolve) override; + mozilla::ipc::IPCResult RecvEndRecording( + EndRecordingResolver&& aResolve) override; + + void NotifyMemoryPressure() override; + void AccumulateMemoryReport(wr::MemoryReport*) override; + + void ActorDestroy(ActorDestroyReason why) override; + + bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override; + void LeaveTestMode(const LayersId& aId) override; + CompositorAnimationStorage* GetAnimationStorage(); + using JankedAnimations = + std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn>; + void NotifyJankedAnimations(const JankedAnimations& aJankedAnimations); + void SetTestAsyncScrollOffset(const LayersId& aLayersId, + const ScrollableLayerGuid::ViewID& aScrollId, + const CSSPoint& aPoint) override; + void SetTestAsyncZoom(const LayersId& aLayersId, + const ScrollableLayerGuid::ViewID& aScrollId, + const LayerToParentLayerScale& aZoom) override; + void FlushApzRepaints(const LayersId& aLayersId) override; + void GetAPZTestData(const LayersId& aLayersId, + APZTestData* aOutData) override; + void GetFrameUniformity(const LayersId& aLayersId, + FrameUniformityData* data) override; + void SetConfirmedTargetAPZC( + const LayersId& aLayersId, const uint64_t& aInputBlockId, + nsTArray<ScrollableLayerGuid>&& aTargets) override; + void SetFixedLayerMargins(ScreenIntCoord aTop, ScreenIntCoord aBottom); + + PTextureParent* AllocPTextureParent( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const LayersId& aId, const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId) override; + bool DeallocPTextureParent(PTextureParent* actor) override; + + mozilla::ipc::IPCResult RecvInitPCanvasParent( + Endpoint<PCanvasParent>&& aEndpoint) final; + + mozilla::ipc::IPCResult RecvReleasePCanvasParent() final; + + bool IsSameProcess() const override; + + void NotifyWebRenderDisableNativeCompositor(); + + void NotifyDidRender(const VsyncId& aCompositeStartId, + TimeStamp& aCompositeStart, TimeStamp& aRenderStart, + TimeStamp& aCompositeEnd, + wr::RendererStats* aStats = nullptr); + void NotifyPipelineRendered(const wr::PipelineId& aPipelineId, + const wr::Epoch& aEpoch, + const VsyncId& aCompositeStartId, + TimeStamp& aCompositeStart, + TimeStamp& aRenderStart, TimeStamp& aCompositeEnd, + wr::RendererStats* aStats = nullptr); + void NotifyDidSceneBuild(RefPtr<const wr::WebRenderPipelineInfo> aInfo); + RefPtr<AsyncImagePipelineManager> GetAsyncImagePipelineManager() const; + + PCompositorWidgetParent* AllocPCompositorWidgetParent( + const CompositorWidgetInitData& aInitData) override; + bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override; + + void ObserveLayersUpdate(LayersId aLayersId, LayersObserverEpoch aEpoch, + bool aActive) override {} + + /** + * This forces the is-first-paint flag to true. This is intended to + * be called by the widget code when it loses its viewport information + * (or for whatever reason wants to refresh the viewport information). + * The information refresh happens because the compositor will call + * SetFirstPaintViewport on the next frame of composition. + */ + void ForceIsFirstPaint(); + + void NotifyChildCreated(LayersId aChild); + + void AsyncRender(); + + // Can be called from any thread + void ScheduleRenderOnCompositorThread(wr::RenderReasons aReasons) override; + + void ScheduleComposition(wr::RenderReasons aReasons); + + static void ScheduleForcedComposition(const LayersId& aLayersId, + wr::RenderReasons aReasons); + + /** + * Returns the unique layer tree identifier that corresponds to the root + * tree of this compositor. + */ + LayersId RootLayerTreeId(); + + /** + * Initialize statics. + */ + static void InitializeStatics(); + + /** + * Notify the compositor for the given layer tree that vsync has occurred. + */ + static void NotifyVsync(const VsyncEvent& aVsync, const LayersId& aLayersId); + + /** + * Set aController as the pan/zoom callback for the subtree referred + * to by aLayersId. + * + * Must run on content main thread. + */ + static void SetControllerForLayerTree(LayersId aLayersId, + GeckoContentController* aController); + + struct LayerTreeState { + LayerTreeState(); + ~LayerTreeState(); + RefPtr<GeckoContentController> mController; + APZCTreeManagerParent* mApzcTreeManagerParent; + RefPtr<CompositorBridgeParent> mParent; + RefPtr<WebRenderBridgeParent> mWrBridge; + // Pointer to the ContentCompositorBridgeParent. Used by APZCs to share + // their FrameMetrics with the corresponding child process that holds + // the PCompositorBridgeChild + ContentCompositorBridgeParent* mContentCompositorBridgeParent; + + CompositorController* GetCompositorController() const; + RefPtr<UiCompositorControllerParent> mUiControllerParent; + }; + + /** + * Lookup the indirect shadow tree for |aId| and return it if it + * exists. Otherwise null is returned. This must only be called on + * the compositor thread. + */ + static LayerTreeState* GetIndirectShadowTree(LayersId aId); + + /** + * Lookup the indirect shadow tree for |aId|, call the function object and + * return true if found. If not found, return false. + */ + static bool CallWithIndirectShadowTree( + LayersId aId, const std::function<void(LayerTreeState&)>& aFunc); + + /** + * Given the layers id for a content process, get the APZCTreeManagerParent + * for the corresponding *root* layers id. That is, the APZCTreeManagerParent, + * if one is found, will always be connected to the parent process rather + * than a content process. Note that unless the compositor process is + * separated this is expected to return null, because if the compositor is + * living in the gecko parent process then there is no APZCTreeManagerParent + * for the parent process. + */ + static APZCTreeManagerParent* GetApzcTreeManagerParentForRoot( + LayersId aContentLayersId); + /** + * Same as the GetApzcTreeManagerParentForRoot function, but returns + * the GeckoContentController for the parent process. + */ + static GeckoContentController* GetGeckoContentControllerForRoot( + LayersId aContentLayersId); + + /** + * Used by the profiler to denote when a vsync occured + */ + static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp); + + widget::CompositorWidget* GetWidget() { return mWidget; } + + PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent( + const LayersId& aLayersId) override; + bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override; + + // Helper method so that we don't have to expose mApzcTreeManager to + // ContentCompositorBridgeParent. + void AllocateAPZCTreeManagerParent( + const MonitorAutoLock& aProofOfLayerTreeStateLock, + const LayersId& aLayersId, LayerTreeState& aLayerTreeStateToUpdate); + + PAPZParent* AllocPAPZParent(const LayersId& aLayersId) override; + bool DeallocPAPZParent(PAPZParent* aActor) override; + + RefPtr<APZSampler> GetAPZSampler() const; + RefPtr<APZUpdater> GetAPZUpdater() const; + RefPtr<OMTASampler> GetOMTASampler() const; + + uint64_t GetInnerWindowId() const { return mInnerWindowId; } + + CompositorOptions GetOptions() const { return mOptions; } + + TimeDuration GetVsyncInterval() const { + // the variable is called "rate" but really it's an interval + return mVsyncRate; + } + + PWebRenderBridgeParent* AllocPWebRenderBridgeParent( + const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize, + const WindowKind& aWindowKind) override; + bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override; + RefPtr<WebRenderBridgeParent> GetWebRenderBridgeParent() const; + Maybe<TimeStamp> GetTestingTimeStamp() const; + + static CompositorBridgeParent* GetCompositorBridgeParentFromLayersId( + const LayersId& aLayersId); + static RefPtr<CompositorBridgeParent> GetCompositorBridgeParentFromWindowId( + const wr::WindowId& aWindowId); + + /** + * This returns a reference to the IAPZCTreeManager "controller subinterface" + * to which pan/zoom-related events can be sent. The controller subinterface + * doesn't expose any sampler-thread APZCTreeManager methods. + */ + static already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager( + LayersId aLayersId); + + WebRenderBridgeParent* GetWrBridge() { return mWrBridge; } + + void FlushPendingWrTransactionEventsWithWait(); + + private: + void Initialize(); + + /** + * Called during destruction in order to release resources as early as + * possible. + */ + void StopAndClearResources(); + + /** + * Release compositor-thread resources referred to by |aID|. + * + * Must run on the content main thread. + */ + static void DeallocateLayerTreeId(LayersId aId); + + /** + * Notify the compositor the quality settings have been updated. + */ + static void UpdateQualitySettings(); + + /** + * Notify the compositor the debug flags have been updated. + */ + static void UpdateDebugFlags(); + + /** + * Notify the compositor some webrender parameters have been updated. + */ + static void UpdateWebRenderParameters(); + + /** + * Notify the compositor some webrender parameters have been updated. + */ + static void UpdateWebRenderBoolParameters(); + + /** + * Notify the compositor webrender profiler UI string has been updated. + */ + static void UpdateWebRenderProfilerUI(); + + static void ResetStable(); + + void MaybeDeclareStable(); + + protected: + // Protected destructor, to discourage deletion outside of Release(): + virtual ~CompositorBridgeParent(); + + void DeferredDestroy(); + + void SetEGLSurfaceRect(int x, int y, int width, int height); + + public: + void PauseComposition(); + bool ResumeComposition(); + bool ResumeCompositionAndResize(int x, int y, int width, int height); + bool IsPaused() { return mPaused; } + + protected: + /** + * Add a compositor to the global compositor map. + */ + static void AddCompositor(CompositorBridgeParent* compositor, uint64_t* id); + /** + * Remove a compositor from the global compositor map. + */ + static CompositorBridgeParent* RemoveCompositor(uint64_t id); + + /** + * Creates the global compositor map. + */ + static void Setup(); + + /** + * Remaning cleanups after the compositore thread is gone. + */ + static void FinishShutdown(); + + // The indirect layer tree lock must be held before calling this function. + // Callback should take (LayerTreeState* aState, const LayersId& aLayersId) + template <typename Lambda> + inline void ForEachIndirectLayerTree(const Lambda& aCallback); + + // The indirect layer tree lock must be held before calling this function. + // Callback should take (LayerTreeState* aState, const LayersId& aLayersId) + template <typename Lambda> + static inline void ForEachWebRenderBridgeParent(const Lambda& aCallback); + + static bool sStable; + static uint32_t sFramesComposited; + + RefPtr<AsyncImagePipelineManager> mAsyncImageManager; + RefPtr<WebRenderBridgeParent> mWrBridge; + widget::CompositorWidget* mWidget; + Maybe<TimeStamp> mTestTime; + CSSToLayoutDeviceScale mScale; + TimeDuration mVsyncRate; + + bool mPaused; + bool mHaveCompositionRecorder; + bool mIsForcedFirstPaint; + + bool mUseExternalSurfaceSize; + gfx::IntSize mEGLSurfaceSize; + + CompositorOptions mOptions; + + uint64_t mCompositorBridgeID; + LayersId mRootLayerTreeID; + + RefPtr<APZCTreeManager> mApzcTreeManager; + RefPtr<APZSampler> mApzSampler; + RefPtr<APZUpdater> mApzUpdater; + RefPtr<OMTASampler> mOMTASampler; + + // Store the inner window id of the browser window, to use it in + // profiler markers. + uint64_t mInnerWindowId; + + RefPtr<CompositorVsyncScheduler> mCompositorScheduler; + // This makes sure the compositorParent is not destroyed before receiving + // confirmation that the channel is closed. + // mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy. + RefPtr<CompositorBridgeParent> mSelfRef; + RefPtr<CompositorAnimationStorage> mAnimationStorage; + + DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeParent); +}; + +int32_t RecordContentFrameTime( + const VsyncId& aTxnId, const TimeStamp& aVsyncStart, + const TimeStamp& aTxnStart, const VsyncId& aCompositeId, + const TimeStamp& aCompositeEnd, const TimeDuration& aFullPaintTime, + const TimeDuration& aVsyncRate, bool aContainsSVGGroup, + bool aRecordUploadStats, wr::RendererStats* aStats = nullptr); + +void RecordCompositionPayloadsPresented( + const TimeStamp& aCompositionEndTime, + const nsTArray<CompositionPayload>& aPayloads); + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CompositorBridgeParent_h diff --git a/gfx/layers/ipc/CompositorManagerChild.cpp b/gfx/layers/ipc/CompositorManagerChild.cpp new file mode 100644 index 0000000000..89e72ddc01 --- /dev/null +++ b/gfx/layers/ipc/CompositorManagerChild.cpp @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/CompositorManagerChild.h" + +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/dom/ContentChild.h" // for ContentChild +#include "mozilla/dom/BrowserChild.h" // for BrowserChild +#include "mozilla/ipc/Endpoint.h" +#include "VsyncSource.h" + +namespace mozilla { +namespace layers { + +using gfx::GPUProcessManager; + +StaticRefPtr<CompositorManagerChild> CompositorManagerChild::sInstance; +Atomic<base::ProcessId> CompositorManagerChild::sOtherPid(0); + +/* static */ +bool CompositorManagerChild::IsInitialized(uint64_t aProcessToken) { + MOZ_ASSERT(NS_IsMainThread()); + return sInstance && sInstance->CanSend() && + sInstance->mProcessToken == aProcessToken; +} + +/* static */ +void CompositorManagerChild::InitSameProcess(uint32_t aNamespace, + uint64_t aProcessToken) { + MOZ_ASSERT(NS_IsMainThread()); + if (NS_WARN_IF(IsInitialized(aProcessToken))) { + MOZ_ASSERT_UNREACHABLE("Already initialized same process"); + return; + } + + RefPtr<CompositorManagerParent> parent = + CompositorManagerParent::CreateSameProcess(); + RefPtr<CompositorManagerChild> child = + new CompositorManagerChild(parent, aProcessToken, aNamespace); + if (NS_WARN_IF(!child->CanSend())) { + MOZ_DIAGNOSTIC_ASSERT(false, "Failed to open same process protocol"); + return; + } + + parent->BindComplete(/* aIsRoot */ true); + sInstance = std::move(child); + sOtherPid = sInstance->OtherPid(); +} + +/* static */ +bool CompositorManagerChild::Init(Endpoint<PCompositorManagerChild>&& aEndpoint, + uint32_t aNamespace, + uint64_t aProcessToken /* = 0 */) { + MOZ_ASSERT(NS_IsMainThread()); + if (sInstance) { + MOZ_ASSERT(sInstance->mNamespace != aNamespace); + } + + sInstance = new CompositorManagerChild(std::move(aEndpoint), aProcessToken, + aNamespace); + sOtherPid = sInstance->OtherPid(); + return sInstance->CanSend(); +} + +/* static */ +void CompositorManagerChild::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + CompositorBridgeChild::ShutDown(); + + if (!sInstance) { + return; + } + + sInstance->Close(); + sInstance = nullptr; + sOtherPid = 0; +} + +/* static */ +void CompositorManagerChild::OnGPUProcessLost(uint64_t aProcessToken) { + MOZ_ASSERT(NS_IsMainThread()); + + // Since GPUChild and CompositorManagerChild will race on ActorDestroy, we + // cannot know if the CompositorManagerChild is about to be released but has + // yet to be. As such, we want to pre-emptively set mCanSend to false. + if (sInstance && sInstance->mProcessToken == aProcessToken) { + sInstance->mCanSend = false; + sOtherPid = 0; + } +} + +/* static */ +bool CompositorManagerChild::CreateContentCompositorBridge( + uint32_t aNamespace) { + MOZ_ASSERT(NS_IsMainThread()); + if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { + return false; + } + + CompositorBridgeOptions options = ContentCompositorOptions(); + + RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance); + if (NS_WARN_IF( + !sInstance->SendPCompositorBridgeConstructor(bridge, options))) { + return false; + } + + bridge->InitForContent(aNamespace); + return true; +} + +/* static */ +already_AddRefed<CompositorBridgeChild> +CompositorManagerChild::CreateWidgetCompositorBridge( + uint64_t aProcessToken, WebRenderLayerManager* aLayerManager, + uint32_t aNamespace, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { + return nullptr; + } + + TimeDuration vsyncRate = + gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher()->GetVsyncRate(); + + CompositorBridgeOptions options = WidgetCompositorOptions( + aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, aSurfaceSize, + aInnerWindowId); + + RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance); + if (NS_WARN_IF( + !sInstance->SendPCompositorBridgeConstructor(bridge, options))) { + return nullptr; + } + + bridge->InitForWidget(aProcessToken, aLayerManager, aNamespace); + return bridge.forget(); +} + +/* static */ +already_AddRefed<CompositorBridgeChild> +CompositorManagerChild::CreateSameProcessWidgetCompositorBridge( + WebRenderLayerManager* aLayerManager, uint32_t aNamespace) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { + return nullptr; + } + + CompositorBridgeOptions options = SameProcessWidgetCompositorOptions(); + + RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance); + if (NS_WARN_IF( + !sInstance->SendPCompositorBridgeConstructor(bridge, options))) { + return nullptr; + } + + bridge->InitForWidget(1, aLayerManager, aNamespace); + return bridge.forget(); +} + +CompositorManagerChild::CompositorManagerChild(CompositorManagerParent* aParent, + uint64_t aProcessToken, + uint32_t aNamespace) + : mProcessToken(aProcessToken), + mNamespace(aNamespace), + mResourceId(0), + mCanSend(false), + mSameProcess(true) { + MOZ_ASSERT(aParent); + + SetOtherProcessId(base::GetCurrentProcId()); + if (NS_WARN_IF(!Open(aParent, CompositorThread(), ipc::ChildSide))) { + return; + } + + mCanSend = true; + SetReplyTimeout(); +} + +CompositorManagerChild::CompositorManagerChild( + Endpoint<PCompositorManagerChild>&& aEndpoint, uint64_t aProcessToken, + uint32_t aNamespace) + : mProcessToken(aProcessToken), + mNamespace(aNamespace), + mResourceId(0), + mCanSend(false), + mSameProcess(false) { + if (NS_WARN_IF(!aEndpoint.Bind(this))) { + return; + } + + mCanSend = true; + SetReplyTimeout(); +} + +void CompositorManagerChild::ActorDestroy(ActorDestroyReason aReason) { + mCanSend = false; + if (sInstance == this) { + sInstance = nullptr; + } +} + +void CompositorManagerChild::HandleFatalError(const char* aMsg) { + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); +} + +void CompositorManagerChild::ProcessingError(Result aCode, + const char* aReason) { + if (aCode != MsgDropped) { + gfxDevCrash(gfx::LogReason::ProcessingError) + << "Processing error in CompositorBridgeChild: " << int(aCode); + } +} + +void CompositorManagerChild::SetReplyTimeout() { +#ifndef DEBUG + // Add a timeout for release builds to kill GPU process when it hangs. + if (XRE_IsParentProcess() && GPUProcessManager::Get()->GetGPUChild()) { + int32_t timeout = + StaticPrefs::layers_gpu_process_ipc_reply_timeout_ms_AtStartup(); + SetReplyTimeoutMs(timeout); + } +#endif +} + +bool CompositorManagerChild::ShouldContinueFromReplyTimeout() { + if (XRE_IsParentProcess()) { + gfxCriticalNote << "Killing GPU process due to IPC reply timeout"; + MOZ_DIAGNOSTIC_ASSERT(GPUProcessManager::Get()->GetGPUChild()); + GPUProcessManager::Get()->KillProcess(); + } + return false; +} + +mozilla::ipc::IPCResult CompositorManagerChild::RecvNotifyWebRenderError( + const WebRenderError&& aError) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + GPUProcessManager::Get()->NotifyWebRenderError(aError); + return IPC_OK(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositorManagerChild.h b/gfx/layers/ipc/CompositorManagerChild.h new file mode 100644 index 0000000000..f92ebde460 --- /dev/null +++ b/gfx/layers/ipc/CompositorManagerChild.h @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_GFX_COMPOSITORMANAGERCHILD_H +#define MOZILLA_GFX_COMPOSITORMANAGERCHILD_H + +#include <stddef.h> // for size_t +#include <stdint.h> // for uint32_t, uint64_t +#include "mozilla/Atomics.h" +#include "mozilla/Attributes.h" // for override +#include "mozilla/RefPtr.h" // for already_AddRefed +#include "mozilla/StaticPtr.h" // for StaticRefPtr +#include "mozilla/layers/PCompositorManagerChild.h" + +namespace mozilla { +namespace layers { + +class CompositorBridgeChild; +class CompositorManagerParent; +class WebRenderLayerManager; + +class CompositorManagerChild : public PCompositorManagerChild { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerChild, override) + + public: + static bool IsInitialized(uint64_t aProcessToken); + static void InitSameProcess(uint32_t aNamespace, uint64_t aProcessToken); + static bool Init(Endpoint<PCompositorManagerChild>&& aEndpoint, + uint32_t aNamespace, uint64_t aProcessToken = 0); + static void Shutdown(); + static void OnGPUProcessLost(uint64_t aProcessToken); + + static bool CreateContentCompositorBridge(uint32_t aNamespace); + + static already_AddRefed<CompositorBridgeChild> CreateWidgetCompositorBridge( + uint64_t aProcessToken, WebRenderLayerManager* aLayerManager, + uint32_t aNamespace, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId); + + static already_AddRefed<CompositorBridgeChild> + CreateSameProcessWidgetCompositorBridge(WebRenderLayerManager* aLayerManager, + uint32_t aNamespace); + + static CompositorManagerChild* GetInstance() { + MOZ_ASSERT(NS_IsMainThread()); + return sInstance; + } + + // Threadsafe way to get the compositor process ID. + static base::ProcessId GetOtherPid() { return sOtherPid; } + + bool CanSend() const { + MOZ_ASSERT(NS_IsMainThread()); + return mCanSend; + } + + bool SameProcess() const { + MOZ_ASSERT(NS_IsMainThread()); + return mSameProcess; + } + + uint32_t GetNextResourceId() { + MOZ_ASSERT(NS_IsMainThread()); + return ++mResourceId; + } + + uint32_t GetNamespace() const { return mNamespace; } + + bool OwnsExternalImageId(const wr::ExternalImageId& aId) const { + return mNamespace == static_cast<uint32_t>(wr::AsUint64(aId) >> 32); + } + + wr::ExternalImageId GetNextExternalImageId() { + uint64_t id = GetNextResourceId(); + MOZ_RELEASE_ASSERT(id != 0); + id |= (static_cast<uint64_t>(mNamespace) << 32); + return wr::ToExternalImageId(id); + } + + void ActorDestroy(ActorDestroyReason aReason) override; + + void HandleFatalError(const char* aMsg) override; + + void ProcessingError(Result aCode, const char* aReason) override; + + bool ShouldContinueFromReplyTimeout() override; + + mozilla::ipc::IPCResult RecvNotifyWebRenderError( + const WebRenderError&& aError); + + private: + static StaticRefPtr<CompositorManagerChild> sInstance; + static Atomic<base::ProcessId> sOtherPid; + + CompositorManagerChild(CompositorManagerParent* aParent, + uint64_t aProcessToken, uint32_t aNamespace); + + CompositorManagerChild(Endpoint<PCompositorManagerChild>&& aEndpoint, + uint64_t aProcessToken, uint32_t aNamespace); + + virtual ~CompositorManagerChild() = default; + + void SetReplyTimeout(); + + uint64_t mProcessToken; + uint32_t mNamespace; + uint32_t mResourceId; + bool mCanSend; + bool mSameProcess; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/CompositorManagerParent.cpp b/gfx/layers/ipc/CompositorManagerParent.cpp new file mode 100644 index 0000000000..d0d7bb862b --- /dev/null +++ b/gfx/layers/ipc/CompositorManagerParent.cpp @@ -0,0 +1,338 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/gfx/GPUParent.h" +#include "mozilla/gfx/CanvasManagerParent.h" +#include "mozilla/webrender/RenderThread.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/ContentCompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/SharedSurfacesParent.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "gfxPlatform.h" +#include "VsyncSource.h" + +namespace mozilla { +namespace layers { + +StaticRefPtr<CompositorManagerParent> CompositorManagerParent::sInstance; +StaticMutex CompositorManagerParent::sMutex; + +#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN +StaticAutoPtr<nsTArray<CompositorManagerParent*>> + CompositorManagerParent::sActiveActors; +#endif + +/* static */ +already_AddRefed<CompositorManagerParent> +CompositorManagerParent::CreateSameProcess() { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + StaticMutexAutoLock lock(sMutex); + + // We are creating a manager for the UI process, inside the combined GPU/UI + // process. It is created more-or-less the same but we retain a reference to + // the parent to access state. + if (NS_WARN_IF(sInstance)) { + MOZ_ASSERT_UNREACHABLE("Already initialized"); + return nullptr; + } + + // The child is responsible for setting up the IPC channel in the same + // process case because if we open from the child perspective, we can do it + // on the main thread and complete before we return the manager handles. + RefPtr<CompositorManagerParent> parent = + new CompositorManagerParent(dom::ContentParentId()); + parent->SetOtherProcessId(base::GetCurrentProcId()); + return parent.forget(); +} + +/* static */ +bool CompositorManagerParent::Create( + Endpoint<PCompositorManagerParent>&& aEndpoint, + dom::ContentParentId aChildId, bool aIsRoot) { + MOZ_ASSERT(NS_IsMainThread()); + + // We are creating a manager for the another process, inside the GPU process + // (or UI process if it subsumbed the GPU process). + MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId()); + + if (!CompositorThreadHolder::IsActive()) { + return false; + } + + RefPtr<CompositorManagerParent> bridge = + new CompositorManagerParent(aChildId); + + RefPtr<Runnable> runnable = + NewRunnableMethod<Endpoint<PCompositorManagerParent>&&, bool>( + "CompositorManagerParent::Bind", bridge, + &CompositorManagerParent::Bind, std::move(aEndpoint), aIsRoot); + CompositorThread()->Dispatch(runnable.forget()); + return true; +} + +/* static */ +already_AddRefed<CompositorBridgeParent> +CompositorManagerParent::CreateSameProcessWidgetCompositorBridge( + CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, + uint64_t aInnerWindowId) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + // When we are in a combined UI / GPU process, InProcessCompositorSession + // requires both the parent and child PCompositorBridge actors for its own + // construction, which is done on the main thread. Normally + // CompositorBridgeParent is created on the compositor thread via the IPDL + // plumbing (CompositorManagerParent::AllocPCompositorBridgeParent). Thus to + // actually get a reference to the parent, we would need to block on the + // compositor thread until it handles our constructor message. Because only + // one one IPDL constructor is permitted per parent and child protocol, we + // cannot make the normal case async and this case sync. Instead what we do + // is leave the constructor async (a boon to the content process setup) and + // create the parent ahead of time. It will pull the preinitialized parent + // from the queue when it receives the message and give that to IPDL. + + // Note that the static mutex not only is used to protect sInstance, but also + // mPendingCompositorBridges. + StaticMutexAutoLock lock(sMutex); + if (NS_WARN_IF(!sInstance)) { + return nullptr; + } + + TimeDuration vsyncRate = + gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher()->GetVsyncRate(); + + RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent( + sInstance, aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, + aSurfaceSize, aInnerWindowId); + + sInstance->mPendingCompositorBridges.AppendElement(bridge); + return bridge.forget(); +} + +CompositorManagerParent::CompositorManagerParent( + dom::ContentParentId aContentId) + : mContentId(aContentId), + mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) {} + +CompositorManagerParent::~CompositorManagerParent() = default; + +void CompositorManagerParent::Bind( + Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (NS_WARN_IF(!aEndpoint.Bind(this))) { + return; + } + + BindComplete(aIsRoot); +} + +void CompositorManagerParent::BindComplete(bool aIsRoot) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || + NS_IsMainThread()); + + StaticMutexAutoLock lock(sMutex); + if (aIsRoot) { + sInstance = this; + } + +#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN + if (!sActiveActors) { + sActiveActors = new nsTArray<CompositorManagerParent*>(); + } + sActiveActors->AppendElement(this); +#endif +} + +void CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason) { + SharedSurfacesParent::DestroyProcess(OtherPid()); + + GetCurrentSerialEventTarget()->Dispatch( + NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy", + this, &CompositorManagerParent::DeferredDestroy)); + + StaticMutexAutoLock lock(sMutex); + if (sInstance == this) { + sInstance = nullptr; + } + +#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN + if (sActiveActors) { + sActiveActors->RemoveElement(this); + } +#endif +} + +void CompositorManagerParent::DeferredDestroy() { + mCompositorThreadHolder = nullptr; +} + +#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN +/* static */ +void CompositorManagerParent::ShutdownInternal() { + UniquePtr<nsTArray<CompositorManagerParent*>> actors; + + // We move here because we may attempt to acquire the same lock during the + // destroy to remove the reference in sActiveActors. + { + StaticMutexAutoLock lock(sMutex); + actors = WrapUnique(sActiveActors.forget()); + } + + if (actors) { + for (auto& actor : *actors) { + actor->Close(); + } + } +} +#endif // COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN + +/* static */ +void CompositorManagerParent::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + +#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN + CompositorThread()->Dispatch(NS_NewRunnableFunction( + "layers::CompositorManagerParent::Shutdown", + []() -> void { CompositorManagerParent::ShutdownInternal(); })); +#endif +} + +already_AddRefed<PCompositorBridgeParent> +CompositorManagerParent::AllocPCompositorBridgeParent( + const CompositorBridgeOptions& aOpt) { + switch (aOpt.type()) { + case CompositorBridgeOptions::TContentCompositorOptions: { + RefPtr<ContentCompositorBridgeParent> bridge = + new ContentCompositorBridgeParent(this); + return bridge.forget(); + } + case CompositorBridgeOptions::TWidgetCompositorOptions: { + // Only the UI process is allowed to create widget compositors in the + // compositor process. + gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton(); + if (NS_WARN_IF(!gpu || OtherPid() != gpu->OtherPid())) { + MOZ_ASSERT_UNREACHABLE("Child cannot create widget compositor!"); + break; + } + + const WidgetCompositorOptions& opt = aOpt.get_WidgetCompositorOptions(); + RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent( + this, opt.scale(), opt.vsyncRate(), opt.options(), + opt.useExternalSurfaceSize(), opt.surfaceSize(), opt.innerWindowId()); + return bridge.forget(); + } + case CompositorBridgeOptions::TSameProcessWidgetCompositorOptions: { + // If the GPU and UI process are combined, we actually already created the + // CompositorBridgeParent, so we need to reuse that to inject it into the + // IPDL framework. + if (NS_WARN_IF(OtherPid() != base::GetCurrentProcId())) { + MOZ_ASSERT_UNREACHABLE("Child cannot create same process compositor!"); + break; + } + + // Note that the static mutex not only is used to protect sInstance, but + // also mPendingCompositorBridges. + StaticMutexAutoLock lock(sMutex); + if (mPendingCompositorBridges.IsEmpty()) { + break; + } + + RefPtr<CompositorBridgeParent> bridge = mPendingCompositorBridges[0]; + mPendingCompositorBridges.RemoveElementAt(0); + return bridge.forget(); + } + default: + break; + } + + return nullptr; +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvAddSharedSurface( + const wr::ExternalImageId& aId, SurfaceDescriptorShared&& aDesc) { + SharedSurfacesParent::Add(aId, std::move(aDesc), OtherPid()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvRemoveSharedSurface( + const wr::ExternalImageId& aId) { + SharedSurfacesParent::Remove(aId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvReportSharedSurfacesMemory( + ReportSharedSurfacesMemoryResolver&& aResolver) { + SharedSurfacesMemoryReport report; + SharedSurfacesParent::AccumulateMemoryReport(OtherPid(), report); + aResolver(std::move(report)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvNotifyMemoryPressure() { + nsTArray<PCompositorBridgeParent*> compositorBridges; + ManagedPCompositorBridgeParent(compositorBridges); + for (auto bridge : compositorBridges) { + static_cast<CompositorBridgeParentBase*>(bridge)->NotifyMemoryPressure(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvReportMemory( + ReportMemoryResolver&& aResolver) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MemoryReport aggregate; + PodZero(&aggregate); + + // Accumulate RenderBackend usage. + nsTArray<PCompositorBridgeParent*> compositorBridges; + ManagedPCompositorBridgeParent(compositorBridges); + for (auto bridge : compositorBridges) { + static_cast<CompositorBridgeParentBase*>(bridge)->AccumulateMemoryReport( + &aggregate); + } + + // Accumulate Renderer usage asynchronously, and resolve. + // + // Note that the IPDL machinery requires aResolver to be called on this + // thread, so we can't just pass it over to the renderer thread. We use + // an intermediate MozPromise instead. + wr::RenderThread::AccumulateMemoryReport(aggregate)->Then( + CompositorThread(), __func__, + [resolver = std::move(aResolver)](MemoryReport aReport) { + resolver(aReport); + }, + [](bool) { + MOZ_ASSERT_UNREACHABLE("MemoryReport promises are never rejected"); + }); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorManagerParent::RecvInitCanvasManager( + Endpoint<PCanvasManagerParent>&& aEndpoint) { + gfx::CanvasManagerParent::Init(std::move(aEndpoint)); + return IPC_OK(); +} + +/* static */ +void CompositorManagerParent::NotifyWebRenderError(wr::WebRenderError aError) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + StaticMutexAutoLock lock(sMutex); + if (NS_WARN_IF(!sInstance)) { + return; + } + Unused << sInstance->SendNotifyWebRenderError(aError); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositorManagerParent.h b/gfx/layers/ipc/CompositorManagerParent.h new file mode 100644 index 0000000000..1273474649 --- /dev/null +++ b/gfx/layers/ipc/CompositorManagerParent.h @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_GFX_COMPOSITORMANAGERPARENT_H +#define MOZILLA_GFX_COMPOSITORMANAGERPARENT_H + +#include <stdint.h> // for uint32_t +#include "mozilla/Attributes.h" // for override +#include "mozilla/StaticPtr.h" // for StaticRefPtr +#include "mozilla/StaticMutex.h" // for StaticMutex +#include "mozilla/RefPtr.h" // for already_AddRefed +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/layers/PCompositorManagerParent.h" +#include "nsTArray.h" // for AutoTArray + +namespace mozilla { +namespace layers { + +class CompositorBridgeParent; +class CompositorThreadHolder; + +#ifndef DEBUG +# define COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN +#endif + +class CompositorManagerParent final : public PCompositorManagerParent { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerParent, final) + + public: + static already_AddRefed<CompositorManagerParent> CreateSameProcess(); + static bool Create(Endpoint<PCompositorManagerParent>&& aEndpoint, + dom::ContentParentId aContentId, bool aIsRoot); + static void Shutdown(); + + static already_AddRefed<CompositorBridgeParent> + CreateSameProcessWidgetCompositorBridge(CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, + uint64_t aInnerWindowId); + + mozilla::ipc::IPCResult RecvAddSharedSurface(const wr::ExternalImageId& aId, + SurfaceDescriptorShared&& aDesc); + mozilla::ipc::IPCResult RecvRemoveSharedSurface( + const wr::ExternalImageId& aId); + mozilla::ipc::IPCResult RecvReportSharedSurfacesMemory( + ReportSharedSurfacesMemoryResolver&&); + + mozilla::ipc::IPCResult RecvNotifyMemoryPressure(); + + mozilla::ipc::IPCResult RecvReportMemory(ReportMemoryResolver&&); + + mozilla::ipc::IPCResult RecvInitCanvasManager( + Endpoint<PCanvasManagerParent>&&); + + void BindComplete(bool aIsRoot); + void ActorDestroy(ActorDestroyReason aReason) override; + + already_AddRefed<PCompositorBridgeParent> AllocPCompositorBridgeParent( + const CompositorBridgeOptions& aOpt); + + static void NotifyWebRenderError(wr::WebRenderError aError); + + const dom::ContentParentId& GetContentId() const { return mContentId; } + + private: + static StaticRefPtr<CompositorManagerParent> sInstance; + static StaticMutex sMutex MOZ_UNANNOTATED; + +#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN + static StaticAutoPtr<nsTArray<CompositorManagerParent*>> sActiveActors; + static void ShutdownInternal(); +#endif + + explicit CompositorManagerParent(dom::ContentParentId aChildId); + virtual ~CompositorManagerParent(); + + void Bind(Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot); + + void DeferredDestroy(); + + dom::ContentParentId mContentId; + + RefPtr<CompositorThreadHolder> mCompositorThreadHolder; + + AutoTArray<RefPtr<CompositorBridgeParent>, 1> mPendingCompositorBridges; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/CompositorThread.cpp b/gfx/layers/ipc/CompositorThread.cpp new file mode 100644 index 0000000000..c66de80cfb --- /dev/null +++ b/gfx/layers/ipc/CompositorThread.cpp @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "CompositorThread.h" + +#include "CompositorBridgeParent.h" +#include "gfxGradientCache.h" +#include "MainThreadUtils.h" +#include "VRManagerParent.h" +#include "mozilla/BackgroundHangMonitor.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/layers/CanvasTranslator.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/VideoBridgeParent.h" +#include "mozilla/media/MediaSystemResourceService.h" +#include "nsThread.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace layers { + +static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder; +static Atomic<bool> sFinishedCompositorShutDown(false); +static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor; +static ProfilerThreadId sProfilerThreadId; + +nsISerialEventTarget* CompositorThread() { + return sCompositorThreadHolder + ? sCompositorThreadHolder->GetCompositorThread() + : nullptr; +} + +CompositorThreadHolder* CompositorThreadHolder::GetSingleton() { + return sCompositorThreadHolder; +} + +CompositorThreadHolder::CompositorThreadHolder() + : mCompositorThread(CreateCompositorThread()) { + MOZ_ASSERT(NS_IsMainThread()); +} + +CompositorThreadHolder::~CompositorThreadHolder() { + sFinishedCompositorShutDown = true; +} + +/* static */ already_AddRefed<nsIThread> +CompositorThreadHolder::CreateCompositorThread() { + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_ASSERT(!sCompositorThreadHolder, + "The compositor thread has already been started!"); + + // When the CanvasRenderer thread is disabled, WebGL may be handled on this + // thread, requiring a bigger stack size. See: CanvasManagerParent::Init + // + // This is 4M, which is higher than the default 256K. + // Increased with bug 1753349 to accommodate the `chromium/5359` branch of + // ANGLE, which has large peak stack usage for some pathological shader + // compilations. + // + // Previously increased to 512K to accommodate Mesa in bug 1753340. + // + // Previously increased to 320K to avoid a stack overflow in the + // Intel Vulkan driver initialization in bug 1716120. + // + // Note: we only override it if it's limited already. + uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE; + if (stackSize) { + stackSize = + std::max(stackSize, gfx::gfxVars::SupportsThreadsafeGL() && + !gfx::gfxVars::UseCanvasRenderThread() + ? 4096U << 10 + : 512U << 10); + } + + nsCOMPtr<nsIThread> compositorThread; + nsresult rv = NS_NewNamedThread( + "Compositor", getter_AddRefs(compositorThread), + NS_NewRunnableFunction( + "CompositorThreadHolder::CompositorThreadHolderSetup", + []() { + sProfilerThreadId = profiler_current_thread_id(); + sBackgroundHangMonitor = new mozilla::BackgroundHangMonitor( + "Compositor", + /* Timeout values are powers-of-two to enable us get better + data. 128ms is chosen for transient hangs because 8Hz should + be the minimally acceptable goal for Compositor + responsiveness (normal goal is 60Hz). */ + 128, + /* 2048ms is chosen for permanent hangs because it's longer than + * most Compositor hangs seen in the wild, but is short enough + * to not miss getting native hang stacks. */ + 2048); + nsCOMPtr<nsIThread> thread = NS_GetCurrentThread(); + static_cast<nsThread*>(thread.get())->SetUseHangMonitor(true); + }), + {.stackSize = stackSize}); + + if (NS_FAILED(rv)) { + return nullptr; + } + + CompositorBridgeParent::Setup(); + ImageBridgeParent::Setup(); + + return compositorThread.forget(); +} + +void CompositorThreadHolder::Start() { + MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); + MOZ_ASSERT(!sCompositorThreadHolder, + "The compositor thread has already been started!"); + + // We unset the holder instead of asserting because failing to start the + // compositor thread may not be a fatal error. As long as this succeeds in + // either the GPU process or the UI process, the user will have a usable + // browser. If we get neither, it will crash as soon as we try to post to the + // compositor thread for the first time. + sProfilerThreadId = ProfilerThreadId(); + sCompositorThreadHolder = new CompositorThreadHolder(); + if (!sCompositorThreadHolder->GetCompositorThread()) { + gfxCriticalNote << "Compositor thread not started (" + << XRE_IsParentProcess() << ")"; + sCompositorThreadHolder = nullptr; + } +} + +void CompositorThreadHolder::Shutdown() { + MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); + if (!sCompositorThreadHolder) { + // We've already shutdown or never started. + return; + } + + ImageBridgeParent::Shutdown(); + gfx::VRManagerParent::Shutdown(); + MediaSystemResourceService::Shutdown(); + CompositorManagerParent::Shutdown(); + CanvasTranslator::Shutdown(); + gfx::gfxGradientCache::Shutdown(); + + // Ensure there are no pending tasks that would cause an access to the + // thread's HangMonitor. APZ and Canvas can keep a reference to the compositor + // thread and may continue to dispatch tasks on it as the system shuts down. + CompositorThread()->Dispatch(NS_NewRunnableFunction( + "CompositorThreadHolder::Shutdown", + [compositorThreadHolder = + RefPtr<CompositorThreadHolder>(sCompositorThreadHolder), + backgroundHangMonitor = UniquePtr<mozilla::BackgroundHangMonitor>( + sBackgroundHangMonitor)]() { + VideoBridgeParent::UnregisterExternalImages(); + nsCOMPtr<nsIThread> thread = NS_GetCurrentThread(); + static_cast<nsThread*>(thread.get())->SetUseHangMonitor(false); + })); + + sCompositorThreadHolder = nullptr; + sBackgroundHangMonitor = nullptr; + + SpinEventLoopUntil("CompositorThreadHolder::Shutdown"_ns, [&]() { + bool finished = sFinishedCompositorShutDown; + return finished; + }); + + // At this point, the CompositorThreadHolder instance will have been + // destroyed, but the compositor thread itself may still be running due to + // APZ/Canvas code holding a reference to the underlying + // nsIThread/nsISerialEventTarget. Any tasks scheduled to run on the + // compositor thread earlier in this function will have been run to + // completion. + CompositorBridgeParent::FinishShutdown(); +} + +/* static */ +bool CompositorThreadHolder::IsInCompositorThread() { + if (!CompositorThread()) { + return false; + } + bool in = false; + MOZ_ALWAYS_SUCCEEDS(CompositorThread()->IsOnCurrentThread(&in)); + return in; +} + +/* static */ +ProfilerThreadId CompositorThreadHolder::GetThreadId() { + return sCompositorThreadHolder ? sProfilerThreadId : ProfilerThreadId(); +} + +} // namespace layers +} // namespace mozilla + +bool NS_IsInCompositorThread() { + return mozilla::layers::CompositorThreadHolder::IsInCompositorThread(); +} diff --git a/gfx/layers/ipc/CompositorThread.h b/gfx/layers/ipc/CompositorThread.h new file mode 100644 index 0000000000..3a07e21620 --- /dev/null +++ b/gfx/layers/ipc/CompositorThread.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ +#ifndef mozilla_layers_CompositorThread_h +#define mozilla_layers_CompositorThread_h + +#include "nsISupportsImpl.h" +#include "nsIThread.h" + +namespace mozilla::baseprofiler { +class BaseProfilerThreadId; +} +using ProfilerThreadId = mozilla::baseprofiler::BaseProfilerThreadId; +class nsISerialEventTarget; +class nsIThread; + +namespace mozilla { +namespace layers { + +class CompositorThreadHolder final { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD( + CompositorThreadHolder) + + public: + CompositorThreadHolder(); + + nsISerialEventTarget* GetCompositorThread() const { + return mCompositorThread; + } + + static CompositorThreadHolder* GetSingleton(); + + static bool IsActive() { return !!GetSingleton(); } + + /** + * Creates the compositor thread and the global compositor map. + */ + static void Start(); + + /* + * Waits for all [CrossProcess]CompositorBridgeParents to shutdown and + * releases compositor-thread owned resources. + */ + static void Shutdown(); + + // Returns true if the calling thread is the compositor thread. + static bool IsInCompositorThread(); + + // Thread id to use as a MarkerThreadId option for profiler markers. + static ProfilerThreadId GetThreadId(); + + private: + ~CompositorThreadHolder(); + + nsCOMPtr<nsIThread> mCompositorThread; + + static already_AddRefed<nsIThread> CreateCompositorThread(); + + friend class CompositorBridgeParent; +}; + +nsISerialEventTarget* CompositorThread(); + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CompositorThread_h diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.cpp b/gfx/layers/ipc/CompositorVsyncScheduler.cpp new file mode 100644 index 0000000000..6ef8903015 --- /dev/null +++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp @@ -0,0 +1,382 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/CompositorVsyncScheduler.h" + +#include <stdio.h> // for fprintf, stdout +#include <stdint.h> // for uint64_t +#include "base/task.h" // for CancelableTask, etc +#include "base/thread.h" // for Thread +#include "gfxPlatform.h" // for gfxPlatform +#ifdef MOZ_WIDGET_GTK +# include "gfxPlatformGtk.h" // for gfxPlatform +#endif +#include "mozilla/AutoRestore.h" // for AutoRestore +#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/gfx/2D.h" // for DrawTarget +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/gfx/Rect.h" // for IntSize +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/CompositorVsyncSchedulerOwner.h" +#include "mozilla/mozalloc.h" // for operator new, etc +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsDebug.h" // for NS_ASSERTION, etc +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "nsIWidget.h" // for nsIWidget +#include "nsThreadUtils.h" // for NS_IsMainThread +#include "mozilla/Telemetry.h" +#include "mozilla/VsyncDispatcher.h" +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +# include "VsyncSource.h" +#endif +#include "mozilla/widget/CompositorWidget.h" +#include "VRManager.h" + +namespace mozilla { + +namespace layers { + +using namespace mozilla::gfx; + +CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner) + : mMutex("CompositorVsyncScheduler.Observer.Mutex"), mOwner(aOwner) {} + +CompositorVsyncScheduler::Observer::~Observer() { MOZ_ASSERT(!mOwner); } + +void CompositorVsyncScheduler::Observer::NotifyVsync(const VsyncEvent& aVsync) { + MutexAutoLock lock(mMutex); + if (!mOwner) { + return; + } + mOwner->NotifyVsync(aVsync); +} + +void CompositorVsyncScheduler::Observer::Destroy() { + MutexAutoLock lock(mMutex); + mOwner = nullptr; +} + +CompositorVsyncScheduler::CompositorVsyncScheduler( + CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner, + widget::CompositorWidget* aWidget) + : mVsyncSchedulerOwner(aVsyncSchedulerOwner), + mLastComposeTime(SampleTime::FromNow()), + mLastVsyncTime(TimeStamp::Now()), + mLastVsyncOutputTime(TimeStamp::Now()), + mIsObservingVsync(false), + mRendersDelayedByVsyncReasons(wr::RenderReasons::NONE), + mVsyncNotificationsSkipped(0), + mWidget(aWidget), + mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor"), + mCurrentCompositeTask(nullptr), + mCurrentCompositeTaskReasons(wr::RenderReasons::NONE), + mCurrentVRTaskMonitor("CurrentVRTaskMonitor"), + mCurrentVRTask(nullptr) { + mVsyncObserver = new Observer(this); + + // mAsapScheduling is set on the main thread during init, + // but is only accessed after on the compositor thread. + mAsapScheduling = + StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 || + gfxPlatform::IsInLayoutAsapMode(); +} + +CompositorVsyncScheduler::~CompositorVsyncScheduler() { + MOZ_ASSERT(!mIsObservingVsync); + MOZ_ASSERT(!mVsyncObserver); + // The CompositorVsyncDispatcher is cleaned up before this in the + // nsBaseWidget, which stops vsync listeners + mVsyncSchedulerOwner = nullptr; +} + +void CompositorVsyncScheduler::Destroy() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + if (!mVsyncObserver) { + // Destroy was already called on this object. + return; + } + UnobserveVsync(); + mVsyncObserver->Destroy(); + mVsyncObserver = nullptr; + + mCompositeRequestedAt = TimeStamp(); + CancelCurrentCompositeTask(); + CancelCurrentVRTask(); +} + +void CompositorVsyncScheduler::PostCompositeTask(const VsyncEvent& aVsyncEvent, + wr::RenderReasons aReasons) { + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + mCurrentCompositeTaskReasons = mCurrentCompositeTaskReasons | aReasons; + if (mCurrentCompositeTask == nullptr && CompositorThread()) { + RefPtr<CancelableRunnable> task = + NewCancelableRunnableMethod<VsyncEvent, wr::RenderReasons>( + "layers::CompositorVsyncScheduler::Composite", this, + &CompositorVsyncScheduler::Composite, aVsyncEvent, aReasons); + mCurrentCompositeTask = task; + CompositorThread()->Dispatch(task.forget()); + } +} + +void CompositorVsyncScheduler::PostVRTask(TimeStamp aTimestamp) { + MonitorAutoLock lockVR(mCurrentVRTaskMonitor); + if (mCurrentVRTask == nullptr && CompositorThread()) { + RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>( + "layers::CompositorVsyncScheduler::DispatchVREvents", this, + &CompositorVsyncScheduler::DispatchVREvents, aTimestamp); + mCurrentVRTask = task; + CompositorThread()->Dispatch(task.forget()); + } +} + +void CompositorVsyncScheduler::ScheduleComposition(wr::RenderReasons aReasons) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (!mVsyncObserver) { + // Destroy was already called on this object. + return; + } + + // Make a synthetic vsync event for the calls to PostCompositeTask below. + TimeStamp vsyncTime = TimeStamp::Now(); + TimeStamp outputTime = vsyncTime + mVsyncSchedulerOwner->GetVsyncInterval(); + VsyncEvent vsyncEvent(VsyncId(), vsyncTime, outputTime); + + if (mAsapScheduling) { + // Used only for performance testing purposes, and when recording/replaying + // to ensure that graphics are up to date. + PostCompositeTask(vsyncEvent, aReasons); + } else { + if (!mCompositeRequestedAt) { + mCompositeRequestedAt = TimeStamp::Now(); + } + if (!mIsObservingVsync && mCompositeRequestedAt) { + ObserveVsync(); + // Starting to observe vsync is an async operation that goes + // through the main thread of the UI process. It's possible that + // we're blocking there waiting on a composite, so schedule an initial + // one now to get things started. + PostCompositeTask(vsyncEvent, + aReasons | wr::RenderReasons::START_OBSERVING_VSYNC); + } else { + mRendersDelayedByVsyncReasons = aReasons; + } + } +} + +void CompositorVsyncScheduler::NotifyVsync(const VsyncEvent& aVsync) { + // Called from the vsync dispatch thread. When in the GPU Process, that's + // the same as the compositor thread. +#ifdef DEBUG +# ifdef MOZ_WAYLAND + // On Wayland, we dispatch vsync from the main thread, without a GPU process. + // To allow this, we skip the following asserts if we're currently utilizing + // the Wayland backend. The IsParentProcess guard is needed to ensure that + // we don't accidentally attempt to initialize the gfxPlatform in the GPU + // process on X11. + if (!XRE_IsParentProcess() || + !gfxPlatformGtk::GetPlatform()->IsWaylandDisplay()) +# endif // MOZ_WAYLAND + { + MOZ_ASSERT_IF(XRE_IsParentProcess(), + !CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(!NS_IsMainThread()); + } + + MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, + CompositorThreadHolder::IsInCompositorThread()); +#endif // DEBUG + +#if defined(MOZ_WIDGET_ANDROID) + gfx::VRManager* vm = gfx::VRManager::Get(); + if (!vm->IsPresenting()) { + PostCompositeTask(aVsync, wr::RenderReasons::VSYNC); + } +#else + PostCompositeTask(aVsync, wr::RenderReasons::VSYNC); +#endif // defined(MOZ_WIDGET_ANDROID) + + PostVRTask(aVsync.mTime); +} + +void CompositorVsyncScheduler::CancelCurrentVRTask() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || + NS_IsMainThread()); + MonitorAutoLock lock(mCurrentVRTaskMonitor); + if (mCurrentVRTask) { + mCurrentVRTask->Cancel(); + mCurrentVRTask = nullptr; + } +} + +wr::RenderReasons CompositorVsyncScheduler::CancelCurrentCompositeTask() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || + NS_IsMainThread()); + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + wr::RenderReasons canceledTaskRenderReasons = mCurrentCompositeTaskReasons; + mCurrentCompositeTaskReasons = wr::RenderReasons::NONE; + if (mCurrentCompositeTask) { + mCurrentCompositeTask->Cancel(); + mCurrentCompositeTask = nullptr; + } + + return canceledTaskRenderReasons; +} + +void CompositorVsyncScheduler::Composite(const VsyncEvent& aVsyncEvent, + wr::RenderReasons aReasons) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(mVsyncSchedulerOwner); + + { // scope lock + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + aReasons = + aReasons | mCurrentCompositeTaskReasons | mRendersDelayedByVsyncReasons; + mCurrentCompositeTaskReasons = wr::RenderReasons::NONE; + mRendersDelayedByVsyncReasons = wr::RenderReasons::NONE; + mCurrentCompositeTask = nullptr; + } + + mLastVsyncTime = aVsyncEvent.mTime; + mLastVsyncOutputTime = aVsyncEvent.mOutputTime; + mLastVsyncId = aVsyncEvent.mId; + + if (!mAsapScheduling) { + // Some early exit conditions if we're not in ASAP mode + if (aVsyncEvent.mTime < mLastComposeTime.Time()) { + // We can sometimes get vsync timestamps that are in the past + // compared to the last compose with force composites. + // In those cases, wait until the next vsync; + return; + } + + if (mVsyncSchedulerOwner->IsPendingComposite()) { + // If previous composite is still on going, finish it and wait for the + // next vsync. + mVsyncSchedulerOwner->FinishPendingComposite(); + return; + } + } + + if (mCompositeRequestedAt || mAsapScheduling) { + mCompositeRequestedAt = TimeStamp(); + mLastComposeTime = SampleTime::FromVsync(aVsyncEvent.mTime); + + // Tell the owner to do a composite + mVsyncSchedulerOwner->CompositeToTarget(aVsyncEvent.mId, aReasons, nullptr, + nullptr); + + mVsyncNotificationsSkipped = 0; + + TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncEvent.mTime; + mozilla::Telemetry::Accumulate( + mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME, + compositeFrameTotal.ToMilliseconds()); + } else if (mVsyncNotificationsSkipped++ > + StaticPrefs::gfx_vsync_compositor_unobserve_count_AtStartup()) { + UnobserveVsync(); + } +} + +void CompositorVsyncScheduler::ForceComposeToTarget(wr::RenderReasons aReasons, + gfx::DrawTarget* aTarget, + const IntRect* aRect) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + /** + * bug 1138502 - There are cases such as during long-running window resizing + * events where we receive many force-composites. We also continue to get + * vsync notifications. Because the force-composites trigger compositing and + * clear the mCompositeRequestedAt timestamp, the vsync notifications will not + * need to do anything and so will increment the mVsyncNotificationsSkipped + * counter to indicate the vsync was ignored. If this happens enough times, we + * will disable listening for vsync entirely. On the next force-composite we + * will enable listening for vsync again, and continued force-composites and + * vsyncs will cause oscillation between observing vsync and not. On some + * platforms, enabling/disabling vsync is not free and this oscillating + * behavior causes a performance hit. In order to avoid this problem, we reset + * the mVsyncNotificationsSkipped counter to keep vsync enabled. + */ + mVsyncNotificationsSkipped = 0; + + mLastComposeTime = SampleTime::FromNow(); + MOZ_ASSERT(mVsyncSchedulerOwner); + mVsyncSchedulerOwner->CompositeToTarget(VsyncId(), aReasons, aTarget, aRect); +} + +bool CompositorVsyncScheduler::NeedsComposite() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return (bool)mCompositeRequestedAt; +} + +bool CompositorVsyncScheduler::FlushPendingComposite() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mCompositeRequestedAt) { + wr::RenderReasons reasons = CancelCurrentCompositeTask(); + ForceComposeToTarget(reasons, nullptr, nullptr); + return true; + } + return false; +} + +void CompositorVsyncScheduler::ObserveVsync() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mWidget->ObserveVsync(mVsyncObserver); + mIsObservingVsync = true; +} + +void CompositorVsyncScheduler::UnobserveVsync() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mWidget->ObserveVsync(nullptr); + mIsObservingVsync = false; +} + +void CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp) { + { + MonitorAutoLock lock(mCurrentVRTaskMonitor); + mCurrentVRTask = nullptr; + } + // This only allows to be called by CompositorVsyncScheduler::PostVRTask() + // When the process is going to shutdown, the runnable has chance to be + // executed by other threads, we only want it to be run in the compositor + // thread. + if (!CompositorThreadHolder::IsInCompositorThread()) { + return; + } + + VRManager* vm = VRManager::Get(); + vm->NotifyVsync(aVsyncTimestamp); +} + +const SampleTime& CompositorVsyncScheduler::GetLastComposeTime() const { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return mLastComposeTime; +} + +const TimeStamp& CompositorVsyncScheduler::GetLastVsyncTime() const { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return mLastVsyncTime; +} + +const TimeStamp& CompositorVsyncScheduler::GetLastVsyncOutputTime() const { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return mLastVsyncOutputTime; +} + +const VsyncId& CompositorVsyncScheduler::GetLastVsyncId() const { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return mLastVsyncId; +} + +void CompositorVsyncScheduler::UpdateLastComposeTime() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mLastComposeTime = SampleTime::FromNow(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.h b/gfx/layers/ipc/CompositorVsyncScheduler.h new file mode 100644 index 0000000000..8363450043 --- /dev/null +++ b/gfx/layers/ipc/CompositorVsyncScheduler.h @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_CompositorVsyncScheduler_h +#define mozilla_layers_CompositorVsyncScheduler_h + +#include <stdint.h> // for uint64_t + +#include "mozilla/Attributes.h" // for override +#include "mozilla/Monitor.h" // for Monitor +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/TimeStamp.h" // for TimeStamp +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/layers/SampleTime.h" +#include "mozilla/webrender/webrender_ffi.h" +#include "mozilla/VsyncDispatcher.h" +#include "mozilla/widget/CompositorWidget.h" +#include "nsISupportsImpl.h" + +namespace mozilla { + +class CancelableRunnable; +class Runnable; + +namespace gfx { +class DrawTarget; +} // namespace gfx + +namespace layers { + +class CompositorVsyncSchedulerOwner; + +/** + * Manages the vsync (de)registration and tracking on behalf of the + * compositor when it need to paint. + * Turns vsync notifications into scheduled composites. + **/ +class CompositorVsyncScheduler { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler) + + public: + CompositorVsyncScheduler(CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner, + widget::CompositorWidget* aWidget); + + /** + * Notify this class of a vsync. This will trigger a composite if one is + * needed. This must be called from the vsync dispatch thread. + */ + void NotifyVsync(const VsyncEvent& aVsync); + + /** + * Do cleanup. This must be called on the compositor thread. + */ + void Destroy(); + + /** + * Notify this class that a composition is needed. This will trigger a + * composition soon (likely at the next vsync). This must be called on the + * compositor thread. + */ + void ScheduleComposition(wr::RenderReasons aReasons); + + /** + * Cancel any composite task that has been scheduled but hasn't run yet. + * + * Returns the render reasons of the canceled task if any. + */ + wr::RenderReasons CancelCurrentCompositeTask(); + + /** + * Check if a composite is pending. This is generally true between a call + * to ScheduleComposition() and the time the composite happens. + */ + bool NeedsComposite(); + + /** + * Force a composite to happen right away, without waiting for the next vsync. + * This must be called on the compositor thread. + */ + void ForceComposeToTarget(wr::RenderReasons aReasons, + gfx::DrawTarget* aTarget, + const gfx::IntRect* aRect); + + /** + * If a composite is pending, force it to trigger right away. This must be + * called on the compositor thread. Returns true if there was a composite + * flushed. + */ + bool FlushPendingComposite(); + + /** + * Return the vsync timestamp of the last or ongoing composite. Must be called + * on the compositor thread. + */ + const SampleTime& GetLastComposeTime() const; + + /** + * Return the vsync timestamp and id of the most recently received + * vsync event. Must be called on the compositor thread. + */ + const TimeStamp& GetLastVsyncTime() const; + const TimeStamp& GetLastVsyncOutputTime() const; + const VsyncId& GetLastVsyncId() const; + + /** + * Update LastCompose TimeStamp to current timestamp. + * The function is typically used when composition is handled outside the + * CompositorVsyncScheduler. + */ + void UpdateLastComposeTime(); + + private: + virtual ~CompositorVsyncScheduler(); + + // Post a task to run Composite() on the compositor thread, if there isn't + // such a task already queued. Can be called from any thread. + void PostCompositeTask(const VsyncEvent& aVsyncEvent, + wr::RenderReasons aReasons); + + // Post a task to run DispatchVREvents() on the VR thread, if there isn't + // such a task already queued. Can be called from any thread. + void PostVRTask(TimeStamp aTimestamp); + + /** + * Cancel any VR task that has been scheduled but hasn't run yet. + */ + void CancelCurrentVRTask(); + + // This gets run at vsync time and "does" a composite (which really means + // update internal state and call the owner to do the composite). + void Composite(const VsyncEvent& aVsyncEvent, wr::RenderReasons aReasons); + + void ObserveVsync(); + void UnobserveVsync(); + + void DispatchVREvents(TimeStamp aVsyncTimestamp); + + class Observer final : public VsyncObserver { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler::Observer, + override) + + public: + explicit Observer(CompositorVsyncScheduler* aOwner); + void NotifyVsync(const VsyncEvent& aVsync) override; + void Destroy(); + + private: + virtual ~Observer(); + + Mutex mMutex MOZ_UNANNOTATED; + // Hold raw pointer to avoid mutual reference. + CompositorVsyncScheduler* mOwner; + }; + + CompositorVsyncSchedulerOwner* mVsyncSchedulerOwner; + SampleTime mLastComposeTime; + TimeStamp mLastVsyncTime; + TimeStamp mLastVsyncOutputTime; + VsyncId mLastVsyncId; + + bool mAsapScheduling; + bool mIsObservingVsync; + // Accessed on the compositor thread. + wr::RenderReasons mRendersDelayedByVsyncReasons; + TimeStamp mCompositeRequestedAt; + int32_t mVsyncNotificationsSkipped; + widget::CompositorWidget* mWidget; + RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver; + + mozilla::Monitor mCurrentCompositeTaskMonitor MOZ_UNANNOTATED; + RefPtr<CancelableRunnable> mCurrentCompositeTask; + // Accessed on multiple threads, guarded by mCurrentCompositeTaskMonitor. + wr::RenderReasons mCurrentCompositeTaskReasons; + + mozilla::Monitor mCurrentVRTaskMonitor MOZ_UNANNOTATED; + RefPtr<CancelableRunnable> mCurrentVRTask; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CompositorVsyncScheduler_h diff --git a/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h b/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h new file mode 100644 index 0000000000..ac147fd241 --- /dev/null +++ b/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_CompositorVsyncSchedulerOwner_h +#define mozilla_layers_CompositorVsyncSchedulerOwner_h + +#include "mozilla/VsyncDispatcher.h" +#include "mozilla/webrender/webrender_ffi.h" + +namespace mozilla { + +namespace gfx { +class DrawTarget; +} // namespace gfx + +namespace layers { + +class CompositorVsyncSchedulerOwner { + public: + virtual bool IsPendingComposite() = 0; + virtual void FinishPendingComposite() = 0; + virtual void CompositeToTarget(VsyncId aId, wr::RenderReasons aReasons, + gfx::DrawTarget* aTarget, + const gfx::IntRect* aRect = nullptr) = 0; + virtual TimeDuration GetVsyncInterval() const = 0; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CompositorVsyncSchedulerOwner_h diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp new file mode 100644 index 0000000000..352e7b0d63 --- /dev/null +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp @@ -0,0 +1,461 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/ContentCompositorBridgeParent.h" + +#include <stdint.h> // for uint64_t + +#include "apz/src/APZCTreeManager.h" // for APZCTreeManager +#include "gfxUtils.h" +#ifdef XP_WIN +# include "mozilla/gfx/DeviceManagerDx.h" // for DeviceManagerDx +# include "mozilla/layers/ImageDataSerializer.h" +#endif +#include "mozilla/layers/AnimationHelper.h" // for CompositorAnimationStorage +#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent +#include "mozilla/layers/APZUpdater.h" // for APZUpdater +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/CompositorOptions.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/RemoteContentController.h" +#include "mozilla/layers/WebRenderBridgeParent.h" +#include "mozilla/layers/AsyncImagePipelineManager.h" +#include "mozilla/mozalloc.h" // for operator new, etc +#include "nsDebug.h" // for NS_ASSERTION, etc +#include "nsTArray.h" // for nsTArray +#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop +#include "mozilla/Unused.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Telemetry.h" +#include "mozilla/BaseProfilerMarkerTypes.h" +#include "GeckoProfiler.h" + +namespace mozilla::layers { + +// defined in CompositorBridgeParent.cpp +using LayerTreeMap = std::map<LayersId, CompositorBridgeParent::LayerTreeState>; +extern LayerTreeMap sIndirectLayerTrees; +extern StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock; +void EraseLayerState(LayersId aId); + +void ContentCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { + mCanSend = false; + + // We must keep this object alive untill the code handling message + // reception is finished on this thread. + GetCurrentSerialEventTarget()->Dispatch(NewRunnableMethod( + "layers::ContentCompositorBridgeParent::DeferredDestroy", this, + &ContentCompositorBridgeParent::DeferredDestroy)); +} + +PAPZCTreeManagerParent* +ContentCompositorBridgeParent::AllocPAPZCTreeManagerParent( + const LayersId& aLayersId) { + // Check to see if this child process has access to this layer tree. + if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) { + NS_ERROR( + "Unexpected layers id in AllocPAPZCTreeManagerParent; dropping " + "message..."); + return nullptr; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& state = + sIndirectLayerTrees[aLayersId]; + + // If the widget has shutdown its compositor, we may not have had a chance yet + // to unmap our layers id, and we could get here without a parent compositor. + // In this case return an empty APZCTM. + if (!state.mParent) { + // Note: we immediately call ClearTree since otherwise the APZCTM will + // retain a reference to itself, through the checkerboard observer. + LayersId dummyId{0}; + const bool connectedToWebRender = false; + RefPtr<APZCTreeManager> temp = new APZCTreeManager(dummyId); + RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp, connectedToWebRender); + tempUpdater->ClearTree(dummyId); + return new APZCTreeManagerParent(aLayersId, temp, tempUpdater); + } + + // If we do not have APZ enabled, we should gracefully fail. + if (!state.mParent->GetOptions().UseAPZ()) { + return nullptr; + } + + state.mParent->AllocateAPZCTreeManagerParent(lock, aLayersId, state); + return state.mApzcTreeManagerParent; +} + +bool ContentCompositorBridgeParent::DeallocPAPZCTreeManagerParent( + PAPZCTreeManagerParent* aActor) { + APZCTreeManagerParent* parent = static_cast<APZCTreeManagerParent*>(aActor); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + auto iter = sIndirectLayerTrees.find(parent->GetLayersId()); + if (iter != sIndirectLayerTrees.end()) { + CompositorBridgeParent::LayerTreeState& state = iter->second; + MOZ_ASSERT(state.mApzcTreeManagerParent == parent); + state.mApzcTreeManagerParent = nullptr; + } + + delete parent; + + return true; +} + +PAPZParent* ContentCompositorBridgeParent::AllocPAPZParent( + const LayersId& aLayersId) { + // Check to see if this child process has access to this layer tree. + if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) { + NS_ERROR("Unexpected layers id in AllocPAPZParent; dropping message..."); + return nullptr; + } + + RemoteContentController* controller = new RemoteContentController(); + + // Increment the controller's refcount before we return it. This will keep the + // controller alive until it is released by IPDL in DeallocPAPZParent. + controller->AddRef(); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& state = + sIndirectLayerTrees[aLayersId]; + MOZ_ASSERT(!state.mController); + state.mController = controller; + + return controller; +} + +bool ContentCompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor) { + RemoteContentController* controller = + static_cast<RemoteContentController*>(aActor); + controller->Release(); + return true; +} + +PWebRenderBridgeParent* +ContentCompositorBridgeParent::AllocPWebRenderBridgeParent( + const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize, + const WindowKind& aWindowKind) { + LayersId layersId = wr::AsLayersId(aPipelineId); + // Check to see if this child process has access to this layer tree. + if (!LayerTreeOwnerTracker::Get()->IsMapped(layersId, OtherPid())) { + NS_ERROR( + "Unexpected layers id in AllocPWebRenderBridgeParent; dropping " + "message..."); + return nullptr; + } + + RefPtr<CompositorBridgeParent> cbp = nullptr; + RefPtr<WebRenderBridgeParent> root = nullptr; + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + MOZ_ASSERT(sIndirectLayerTrees.find(layersId) != sIndirectLayerTrees.end()); + MOZ_ASSERT(sIndirectLayerTrees[layersId].mWrBridge == nullptr); + cbp = sIndirectLayerTrees[layersId].mParent; + if (cbp) { + root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge; + } + } + + RefPtr<wr::WebRenderAPI> api; + if (root) { + api = root->GetWebRenderAPI(); + } + + if (!root || !api) { + // This could happen when this function is called after + // CompositorBridgeParent destruction. This was observed during Tab move + // between different windows. + NS_WARNING( + nsPrintfCString("Created child without a matching parent? root %p", + root.get()) + .get()); + nsCString error("NO_PARENT"); + WebRenderBridgeParent* parent = + WebRenderBridgeParent::CreateDestroyed(aPipelineId, std::move(error)); + parent->AddRef(); // IPDL reference + return parent; + } + + api = api->Clone(); + RefPtr<AsyncImagePipelineManager> holder = root->AsyncImageManager(); + WebRenderBridgeParent* parent = new WebRenderBridgeParent( + this, aPipelineId, nullptr, root->CompositorScheduler(), std::move(api), + std::move(holder), cbp->GetVsyncInterval()); + parent->AddRef(); // IPDL reference + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[layersId].mContentCompositorBridgeParent = this; + sIndirectLayerTrees[layersId].mWrBridge = parent; + } + + return parent; +} + +bool ContentCompositorBridgeParent::DeallocPWebRenderBridgeParent( + PWebRenderBridgeParent* aActor) { + WebRenderBridgeParent* parent = static_cast<WebRenderBridgeParent*>(aActor); + EraseLayerState(wr::AsLayersId(parent->PipelineId())); + parent->Release(); // IPDL reference + return true; +} + +mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvNotifyChildCreated( + const LayersId& child, CompositorOptions* aOptions) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + for (auto& entry : sIndirectLayerTrees) { + CompositorBridgeParent::LayerTreeState& lts = entry.second; + if (lts.mParent && lts.mContentCompositorBridgeParent == this) { + lts.mParent->NotifyChildCreated(child); + *aOptions = lts.mParent->GetOptions(); + return IPC_OK(); + } + } + return IPC_FAIL_NO_REASON(this); +} + +mozilla::ipc::IPCResult +ContentCompositorBridgeParent::RecvMapAndNotifyChildCreated( + const LayersId& child, const base::ProcessId& pid, + CompositorOptions* aOptions) { + // This can only be called from the browser process, as the mapping + // ensures proper window ownership of layer trees. + return IPC_FAIL_NO_REASON(this); +} + +mozilla::ipc::IPCResult +ContentCompositorBridgeParent::RecvNotifyMemoryPressure() { + // This can only be called from the browser process. + return IPC_FAIL_NO_REASON(this); +} + +mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvCheckContentOnlyTDR( + const uint32_t& sequenceNum, bool* isContentOnlyTDR) { + *isContentOnlyTDR = false; +#ifdef XP_WIN + gfx::ContentDeviceData compositor; + + gfx::DeviceManagerDx* dm = gfx::DeviceManagerDx::Get(); + + // Check that the D3D11 device sequence numbers match. + gfx::D3D11DeviceStatus status; + dm->ExportDeviceInfo(&status); + + if (sequenceNum == static_cast<uint32_t>(status.sequenceNumber()) && + !dm->HasDeviceReset()) { + *isContentOnlyTDR = true; + } + +#endif + return IPC_OK(); +}; + +void ContentCompositorBridgeParent::DidCompositeLocked( + LayersId aId, const VsyncId& aVsyncId, TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd) { + sIndirectLayerTreesLock->AssertCurrentThreadOwns(); + if (sIndirectLayerTrees[aId].mWrBridge) { + MOZ_ASSERT(false); // this should never get called for a WR compositor + } +} + +bool ContentCompositorBridgeParent::SetTestSampleTime(const LayersId& aId, + const TimeStamp& aTime) { + MOZ_ASSERT(aId.IsValid()); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(aId); + if (!state) { + return false; + } + + MOZ_ASSERT(state->mParent); + return state->mParent->SetTestSampleTime(aId, aTime); +} + +void ContentCompositorBridgeParent::LeaveTestMode(const LayersId& aId) { + MOZ_ASSERT(aId.IsValid()); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(aId); + if (!state) { + return; + } + + MOZ_ASSERT(state->mParent); + state->mParent->LeaveTestMode(aId); +} + +void ContentCompositorBridgeParent::SetTestAsyncScrollOffset( + const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId, + const CSSPoint& aPoint) { + MOZ_ASSERT(aLayersId.IsValid()); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(aLayersId); + if (!state) { + return; + } + + MOZ_ASSERT(state->mParent); + state->mParent->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint); +} + +void ContentCompositorBridgeParent::SetTestAsyncZoom( + const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId, + const LayerToParentLayerScale& aZoom) { + MOZ_ASSERT(aLayersId.IsValid()); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(aLayersId); + if (!state) { + return; + } + + MOZ_ASSERT(state->mParent); + state->mParent->SetTestAsyncZoom(aLayersId, aScrollId, aZoom); +} + +void ContentCompositorBridgeParent::FlushApzRepaints( + const LayersId& aLayersId) { + MOZ_ASSERT(aLayersId.IsValid()); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(aLayersId); + if (!state || !state->mParent) { + return; + } + + state->mParent->FlushApzRepaints(aLayersId); +} + +void ContentCompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId, + APZTestData* aOutData) { + MOZ_ASSERT(aLayersId.IsValid()); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(aLayersId); + if (!state || !state->mParent) { + return; + } + + state->mParent->GetAPZTestData(aLayersId, aOutData); +} + +void ContentCompositorBridgeParent::GetFrameUniformity( + const LayersId& aLayersId, FrameUniformityData* aOutData) { + MOZ_ASSERT(aLayersId.IsValid()); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(aLayersId); + if (!state || !state->mParent) { + return; + } + + state->mParent->GetFrameUniformity(aLayersId, aOutData); +} + +void ContentCompositorBridgeParent::SetConfirmedTargetAPZC( + const LayersId& aLayersId, const uint64_t& aInputBlockId, + nsTArray<ScrollableLayerGuid>&& aTargets) { + MOZ_ASSERT(aLayersId.IsValid()); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(aLayersId); + if (!state || !state->mParent) { + return; + } + + state->mParent->SetConfirmedTargetAPZC(aLayersId, aInputBlockId, + std::move(aTargets)); +} + +void ContentCompositorBridgeParent::DeferredDestroy() { mSelfRef = nullptr; } + +ContentCompositorBridgeParent::~ContentCompositorBridgeParent() { + MOZ_ASSERT(XRE_GetIOMessageLoop()); +} + +PTextureParent* ContentCompositorBridgeParent::AllocPTextureParent( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const LayersId& aId, const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId) { + CompositorBridgeParent::LayerTreeState* state = nullptr; + + LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId); + if (sIndirectLayerTrees.end() != itr) { + state = &itr->second; + } + + TextureFlags flags = aFlags; + + LayersBackend actualBackend = LayersBackend::LAYERS_NONE; + if (!state) { + // The compositor was recreated, and we're receiving layers updates for a + // a layer manager that will soon be discarded or invalidated. We can't + // return null because this will mess up deserialization later and we'll + // kill the content process. Instead, we signal that the underlying + // TextureHost should not attempt to access the compositor. + flags |= TextureFlags::INVALID_COMPOSITOR; + } else if (actualBackend != LayersBackend::LAYERS_NONE && + aLayersBackend != actualBackend) { + gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) + << "Texture backend is wrong"; + } + + return TextureHost::CreateIPDLActor( + this, aSharedData, std::move(aReadLock), aLayersBackend, aFlags, + mCompositorManager->GetContentId(), aSerial, aExternalImageId); +} + +bool ContentCompositorBridgeParent::DeallocPTextureParent( + PTextureParent* actor) { + return TextureHost::DestroyIPDLActor(actor); +} + +mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvInitPCanvasParent( + Endpoint<PCanvasParent>&& aEndpoint) { + MOZ_RELEASE_ASSERT(!mCanvasTranslator, + "mCanvasTranslator must be released before recreating."); + + mCanvasTranslator = CanvasTranslator::Create(std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +ContentCompositorBridgeParent::RecvReleasePCanvasParent() { + MOZ_RELEASE_ASSERT(mCanvasTranslator, + "mCanvasTranslator hasn't been created."); + + mCanvasTranslator = nullptr; + return IPC_OK(); +} + +UniquePtr<SurfaceDescriptor> +ContentCompositorBridgeParent::LookupSurfaceDescriptorForClientTexture( + const int64_t aTextureId) { + MOZ_RELEASE_ASSERT(mCanvasTranslator, + "mCanvasTranslator hasn't been created."); + + return mCanvasTranslator->WaitForSurfaceDescriptor(aTextureId); +} + +bool ContentCompositorBridgeParent::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void ContentCompositorBridgeParent::ObserveLayersUpdate( + LayersId aLayersId, LayersObserverEpoch aEpoch, bool aActive) { + MOZ_ASSERT(aLayersId.IsValid()); + + CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(aLayersId); + if (!state || !state->mParent) { + return; + } + + Unused << state->mParent->SendObserveLayersUpdate(aLayersId, aEpoch, aActive); +} + +} // namespace mozilla::layers diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.h b/gfx/layers/ipc/ContentCompositorBridgeParent.h new file mode 100644 index 0000000000..77836eac55 --- /dev/null +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.h @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_ContentCompositorBridgeParent_h +#define mozilla_layers_ContentCompositorBridgeParent_h + +#include "mozilla/layers/CanvasTranslator.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/UniquePtr.h" + +namespace mozilla::layers { + +class CompositorOptions; + +/** + * This class handles layer updates pushed directly from child processes to + * the compositor thread. It's associated with a CompositorBridgeParent on the + * compositor thread. While it uses the PCompositorBridge protocol to manage + * these updates, it doesn't actually drive compositing itself. For that it + * hands off work to the CompositorBridgeParent it's associated with. + */ +class ContentCompositorBridgeParent final : public CompositorBridgeParentBase { + friend class CompositorBridgeParent; + + public: + explicit ContentCompositorBridgeParent(CompositorManagerParent* aManager) + : CompositorBridgeParentBase(aManager), mDestroyCalled(false) {} + + void ActorDestroy(ActorDestroyReason aWhy) override; + + // FIXME/bug 774388: work out what shutdown protocol we need. + mozilla::ipc::IPCResult RecvInitialize( + const LayersId& aRootLayerTreeId) override { + return IPC_FAIL_NO_REASON(this); + } + mozilla::ipc::IPCResult RecvWillClose() override { return IPC_OK(); } + mozilla::ipc::IPCResult RecvPause() override { return IPC_OK(); } + mozilla::ipc::IPCResult RecvRequestFxrOutput() override { + return IPC_FAIL_NO_REASON(this); + } + mozilla::ipc::IPCResult RecvResume() override { return IPC_OK(); } + mozilla::ipc::IPCResult RecvResumeAsync() override { return IPC_OK(); } + mozilla::ipc::IPCResult RecvNotifyChildCreated( + const LayersId& child, CompositorOptions* aOptions) override; + mozilla::ipc::IPCResult RecvMapAndNotifyChildCreated( + const LayersId& child, const base::ProcessId& pid, + CompositorOptions* aOptions) override; + mozilla::ipc::IPCResult RecvNotifyChildRecreated( + const LayersId& child, CompositorOptions* aOptions) override { + return IPC_FAIL_NO_REASON(this); + } + mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& child) override { + return IPC_FAIL_NO_REASON(this); + } + mozilla::ipc::IPCResult RecvFlushRendering( + const wr::RenderReasons&) override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvFlushRenderingAsync( + const wr::RenderReasons&) override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvForcePresent(const wr::RenderReasons&) override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvStartFrameTimeRecording( + const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvStopFrameTimeRecording( + const uint32_t& aStartIndex, nsTArray<float>* intervals) override { + return IPC_OK(); + } + + mozilla::ipc::IPCResult RecvNotifyMemoryPressure() override; + + mozilla::ipc::IPCResult RecvCheckContentOnlyTDR( + const uint32_t& sequenceNum, bool* isContentOnlyTDR) override; + + mozilla::ipc::IPCResult RecvBeginRecording( + const TimeStamp& aRecordingStart, + BeginRecordingResolver&& aResolve) override { + aResolve(false); + return IPC_OK(); + } + + mozilla::ipc::IPCResult RecvEndRecording( + EndRecordingResolver&& aResolve) override { + aResolve(Nothing()); + return IPC_OK(); + } + + bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override; + void LeaveTestMode(const LayersId& aId) override; + void SetTestAsyncScrollOffset(const LayersId& aLayersId, + const ScrollableLayerGuid::ViewID& aScrollId, + const CSSPoint& aPoint) override; + void SetTestAsyncZoom(const LayersId& aLayersId, + const ScrollableLayerGuid::ViewID& aScrollId, + const LayerToParentLayerScale& aZoom) override; + void FlushApzRepaints(const LayersId& aLayersId) override; + void GetAPZTestData(const LayersId& aLayersId, + APZTestData* aOutData) override; + void GetFrameUniformity(const LayersId& aLayersId, + FrameUniformityData* aOutData) override; + void SetConfirmedTargetAPZC( + const LayersId& aLayersId, const uint64_t& aInputBlockId, + nsTArray<ScrollableLayerGuid>&& aTargets) override; + + // Use DidCompositeLocked if you already hold a lock on + // sIndirectLayerTreesLock; Otherwise use DidComposite, which would request + // the lock automatically. + void DidCompositeLocked(LayersId aId, const VsyncId& aVsyncId, + TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd); + + PTextureParent* AllocPTextureParent( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const LayersId& aId, const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId) override; + + bool DeallocPTextureParent(PTextureParent* actor) override; + + mozilla::ipc::IPCResult RecvInitPCanvasParent( + Endpoint<PCanvasParent>&& aEndpoint) final; + + mozilla::ipc::IPCResult RecvReleasePCanvasParent() final; + + bool IsSameProcess() const override; + + PCompositorWidgetParent* AllocPCompositorWidgetParent( + const CompositorWidgetInitData& aInitData) override { + // Not allowed. + return nullptr; + } + bool DeallocPCompositorWidgetParent( + PCompositorWidgetParent* aActor) override { + // Not allowed. + return false; + } + + PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent( + const LayersId& aLayersId) override; + bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override; + + PAPZParent* AllocPAPZParent(const LayersId& aLayersId) override; + bool DeallocPAPZParent(PAPZParent* aActor) override; + + PWebRenderBridgeParent* AllocPWebRenderBridgeParent( + const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize, + const WindowKind& aWindowKind) override; + bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override; + + void ObserveLayersUpdate(LayersId aLayersId, LayersObserverEpoch aEpoch, + bool aActive) override; + + bool IsRemote() const override { return true; } + + UniquePtr<SurfaceDescriptor> LookupSurfaceDescriptorForClientTexture( + const int64_t aTextureId) final; + + private: + // Private destructor, to discourage deletion outside of Release(): + virtual ~ContentCompositorBridgeParent(); + + void DeferredDestroy(); + + // There can be many CPCPs, and IPDL-generated code doesn't hold a + // reference to top-level actors. So we hold a reference to + // ourself. This is released (deferred) in ActorDestroy(). + RefPtr<ContentCompositorBridgeParent> mSelfRef; + + bool mDestroyCalled; + + RefPtr<CanvasTranslator> mCanvasTranslator; +}; + +} // namespace mozilla::layers + +#endif // mozilla_layers_ContentCompositorBridgeParent_h diff --git a/gfx/layers/ipc/ISurfaceAllocator.cpp b/gfx/layers/ipc/ISurfaceAllocator.cpp new file mode 100644 index 0000000000..7baca7a9b6 --- /dev/null +++ b/gfx/layers/ipc/ISurfaceAllocator.cpp @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "ISurfaceAllocator.h" + +#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent +#include "mozilla/layers/TextureHost.h" // for TextureHost +#include "mozilla/layers/TextureForwarder.h" +#include "mozilla/layers/CompositableForwarder.h" + +namespace mozilla { +namespace layers { + +NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter) + +mozilla::Atomic<ptrdiff_t> GfxMemoryImageReporter::sAmount(0); + +void HostIPCAllocator::SendPendingAsyncMessages() { + if (mPendingAsyncMessage.empty()) { + return; + } + + // Some type of AsyncParentMessageData message could have + // one file descriptor (e.g. OpDeliverFence). + // A number of file descriptors per gecko ipc message have a limitation + // on OS_POSIX (MACOSX or LINUX). + static const uint32_t kMaxMessageNumber = + IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE; + + nsTArray<AsyncParentMessageData> messages; + messages.SetCapacity(mPendingAsyncMessage.size()); + for (size_t i = 0; i < mPendingAsyncMessage.size(); i++) { + messages.AppendElement(mPendingAsyncMessage[i]); + // Limit maximum number of messages. + if (messages.Length() >= kMaxMessageNumber) { + SendAsyncMessage(messages); + // Initialize Messages. + messages.Clear(); + } + } + + if (messages.Length() > 0) { + SendAsyncMessage(messages); + } + mPendingAsyncMessage.clear(); +} + +// XXX - We should actually figure out the minimum shmem allocation size on +// a certain platform and use that. +const uint32_t sShmemPageSize = 4096; + +#ifdef DEBUG +const uint32_t sSupportedBlockSize = 4; +#endif + +FixedSizeSmallShmemSectionAllocator::FixedSizeSmallShmemSectionAllocator( + LayersIPCChannel* aShmProvider) + : mShmProvider(aShmProvider) { + MOZ_ASSERT(mShmProvider); +} + +FixedSizeSmallShmemSectionAllocator::~FixedSizeSmallShmemSectionAllocator() { + ShrinkShmemSectionHeap(); +} + +bool FixedSizeSmallShmemSectionAllocator::IPCOpen() const { + return mShmProvider->IPCOpen(); +} + +bool FixedSizeSmallShmemSectionAllocator::AllocShmemSection( + uint32_t aSize, ShmemSection* aShmemSection) { + // For now we only support sizes of 4. If we want to support different sizes + // some more complicated bookkeeping should be added. + MOZ_ASSERT(aSize == sSupportedBlockSize); + MOZ_ASSERT(aShmemSection); + + if (!IPCOpen()) { + gfxCriticalError() << "Attempt to allocate a ShmemSection after shutdown."; + return false; + } + + uint32_t allocationSize = (aSize + sizeof(ShmemSectionHeapAllocation)); + + for (size_t i = 0; i < mUsedShmems.size(); i++) { + ShmemSectionHeapHeader* header = + mUsedShmems[i].get<ShmemSectionHeapHeader>(); + if ((header->mAllocatedBlocks + 1) * allocationSize + + sizeof(ShmemSectionHeapHeader) < + sShmemPageSize) { + aShmemSection->shmem() = mUsedShmems[i]; + MOZ_ASSERT(mUsedShmems[i].IsWritable()); + break; + } + } + + if (!aShmemSection->shmem().IsWritable()) { + ipc::Shmem tmp; + if (!mShmProvider->AllocUnsafeShmem(sShmemPageSize, &tmp)) { + return false; + } + + ShmemSectionHeapHeader* header = tmp.get<ShmemSectionHeapHeader>(); + header->mTotalBlocks = 0; + header->mAllocatedBlocks = 0; + + mUsedShmems.push_back(tmp); + aShmemSection->shmem() = tmp; + } + + MOZ_ASSERT(aShmemSection->shmem().IsWritable()); + + ShmemSectionHeapHeader* header = + aShmemSection->shmem().get<ShmemSectionHeapHeader>(); + uint8_t* heap = + aShmemSection->shmem().get<uint8_t>() + sizeof(ShmemSectionHeapHeader); + + ShmemSectionHeapAllocation* allocHeader = nullptr; + + if (header->mTotalBlocks > header->mAllocatedBlocks) { + // Search for the first available block. + for (size_t i = 0; i < header->mTotalBlocks; i++) { + allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap); + + if (allocHeader->mStatus == STATUS_FREED) { + break; + } + heap += allocationSize; + } + MOZ_ASSERT(allocHeader && allocHeader->mStatus == STATUS_FREED); + MOZ_ASSERT(allocHeader->mSize == sSupportedBlockSize); + } else { + heap += header->mTotalBlocks * allocationSize; + + header->mTotalBlocks++; + allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap); + allocHeader->mSize = aSize; + } + + MOZ_ASSERT(allocHeader); + header->mAllocatedBlocks++; + allocHeader->mStatus = STATUS_ALLOCATED; + + aShmemSection->size() = aSize; + aShmemSection->offset() = (heap + sizeof(ShmemSectionHeapAllocation)) - + aShmemSection->shmem().get<uint8_t>(); + ShrinkShmemSectionHeap(); + return true; +} + +void FixedSizeSmallShmemSectionAllocator::FreeShmemSection( + mozilla::layers::ShmemSection& aShmemSection) { + MOZ_ASSERT(aShmemSection.size() == sSupportedBlockSize); + MOZ_ASSERT(aShmemSection.offset() < sShmemPageSize - sSupportedBlockSize); + + if (!aShmemSection.shmem().IsWritable()) { + return; + } + + ShmemSectionHeapAllocation* allocHeader = + reinterpret_cast<ShmemSectionHeapAllocation*>( + aShmemSection.shmem().get<char>() + aShmemSection.offset() - + sizeof(ShmemSectionHeapAllocation)); + + MOZ_ASSERT(allocHeader->mSize == aShmemSection.size()); + + DebugOnly<bool> success = + allocHeader->mStatus.compareExchange(STATUS_ALLOCATED, STATUS_FREED); + // If this fails something really weird is going on. + MOZ_ASSERT(success); + + ShmemSectionHeapHeader* header = + aShmemSection.shmem().get<ShmemSectionHeapHeader>(); + header->mAllocatedBlocks--; +} + +void FixedSizeSmallShmemSectionAllocator::DeallocShmemSection( + mozilla::layers::ShmemSection& aShmemSection) { + if (!IPCOpen()) { + gfxCriticalNote << "Attempt to dealloc a ShmemSections after shutdown."; + return; + } + + FreeShmemSection(aShmemSection); + ShrinkShmemSectionHeap(); +} + +void FixedSizeSmallShmemSectionAllocator::ShrinkShmemSectionHeap() { + if (!IPCOpen()) { + mUsedShmems.clear(); + return; + } + + // The loop will terminate as we either increase i, or decrease size + // every time through. + size_t i = 0; + while (i < mUsedShmems.size()) { + ShmemSectionHeapHeader* header = + mUsedShmems[i].get<ShmemSectionHeapHeader>(); + if (header->mAllocatedBlocks == 0) { + mShmProvider->DeallocShmem(mUsedShmems[i]); + // We don't particularly care about order, move the last one in the array + // to this position. + if (i < mUsedShmems.size() - 1) { + mUsedShmems[i] = mUsedShmems[mUsedShmems.size() - 1]; + } + mUsedShmems.pop_back(); + } else { + i++; + } + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ISurfaceAllocator.h b/gfx/layers/ipc/ISurfaceAllocator.h new file mode 100644 index 0000000000..77edf4fc5f --- /dev/null +++ b/gfx/layers/ipc/ISurfaceAllocator.h @@ -0,0 +1,285 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef GFX_LAYERS_ISURFACEDEALLOCATOR +#define GFX_LAYERS_ISURFACEDEALLOCATOR + +#include <stddef.h> // for size_t +#include <stdint.h> // for uint32_t +#include "gfxTypes.h" +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc +#include "mozilla/RefPtr.h" +#include "nsIMemoryReporter.h" // for nsIMemoryReporter +#include "mozilla/Atomics.h" // for Atomic +#include "mozilla/layers/LayersMessages.h" // for ShmemSection + +namespace mozilla { +namespace ipc { +class Shmem; +class IShmemAllocator; +} // namespace ipc +namespace gfx { +class DataSourceSurface; +} // namespace gfx + +namespace layers { + +class CompositableForwarder; +class CompositorBridgeParentBase; +class TextureForwarder; + +class ShmemSectionAllocator; +class LegacySurfaceDescriptorAllocator; +class ClientIPCAllocator; +class HostIPCAllocator; +class LayersIPCChannel; + +enum BufferCapabilities { + DEFAULT_BUFFER_CAPS = 0, + /** + * The allocated buffer must be efficiently mappable as a DataSourceSurface. + */ + MAP_AS_IMAGE_SURFACE = 1 << 0, + /** + * The allocated buffer will be used for GL rendering only + */ + USING_GL_RENDERING_ONLY = 1 << 1 +}; + +class SurfaceDescriptor; + +/** + * An interface used to create and destroy surfaces that are shared with the + * Compositor process (using shmem, or other platform specific memory) + * + * Most of the methods here correspond to methods that are implemented by IPDL + * actors without a common polymorphic interface. + * These methods should be only called in the ipdl implementor's thread, unless + * specified otherwise in the implementing class. + */ +class ISurfaceAllocator { + public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(ISurfaceAllocator) + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ISurfaceAllocator) + + ISurfaceAllocator() = default; + + // down-casting + + virtual mozilla::ipc::IShmemAllocator* AsShmemAllocator() { return nullptr; } + + virtual ShmemSectionAllocator* AsShmemSectionAllocator() { return nullptr; } + + virtual CompositableForwarder* AsCompositableForwarder() { return nullptr; } + + virtual TextureForwarder* GetTextureForwarder() { return nullptr; } + + virtual ClientIPCAllocator* AsClientAllocator() { return nullptr; } + + virtual HostIPCAllocator* AsHostIPCAllocator() { return nullptr; } + + virtual LegacySurfaceDescriptorAllocator* + AsLegacySurfaceDescriptorAllocator() { + return nullptr; + } + + virtual CompositorBridgeParentBase* AsCompositorBridgeParentBase() { + return nullptr; + } + + // ipc info + + virtual bool IPCOpen() const { return true; } + + virtual bool IsSameProcess() const = 0; + + virtual bool UsesImageBridge() const { return false; } + + virtual bool UsesWebRenderBridge() const { return false; } + + virtual dom::ContentParentId GetContentId() { return dom::ContentParentId(); } + + protected: + void Finalize() {} + + virtual ~ISurfaceAllocator() = default; +}; + +/// Methods that are specific to the client/child side. +class ClientIPCAllocator : public ISurfaceAllocator { + public: + ClientIPCAllocator() = default; + + ClientIPCAllocator* AsClientAllocator() override { return this; } + + virtual base::ProcessId GetParentPid() const = 0; + + virtual MessageLoop* GetMessageLoop() const = 0; + + virtual void CancelWaitForNotifyNotUsed(uint64_t aTextureId) = 0; +}; + +/// Methods that are specific to the host/parent side. +class HostIPCAllocator : public ISurfaceAllocator { + public: + HostIPCAllocator() = default; + + HostIPCAllocator* AsHostIPCAllocator() override { return this; } + + /** + * Get child side's process Id. + */ + virtual base::ProcessId GetChildProcessId() = 0; + + virtual void NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) = 0; + + virtual void SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) = 0; + + virtual void SendPendingAsyncMessages(); + + virtual void SetAboutToSendAsyncMessages() { + mAboutToSendAsyncMessages = true; + } + + bool IsAboutToSendAsyncMessages() { return mAboutToSendAsyncMessages; } + + protected: + std::vector<AsyncParentMessageData> mPendingAsyncMessage; + bool mAboutToSendAsyncMessages = false; +}; + +/// An allocator that can group allocations in bigger chunks of shared memory. +/// +/// The allocated shmem sections can only be deallocated by the same allocator +/// instance (and only in the child process). +class ShmemSectionAllocator { + public: + virtual bool AllocShmemSection(uint32_t aSize, + ShmemSection* aShmemSection) = 0; + + virtual void DeallocShmemSection(ShmemSection& aShmemSection) = 0; + + virtual void MemoryPressure() {} +}; + +/// Some old stuff that's still around and used for screenshots. +/// +/// New code should not need this (see TextureClient). +class LegacySurfaceDescriptorAllocator { + public: + virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize, + gfxContentType aContent, + SurfaceDescriptor* aBuffer) = 0; + + virtual bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize, + gfxContentType aContent, + uint32_t aCaps, + SurfaceDescriptor* aBuffer) = 0; + + virtual void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) = 0; +}; + +bool IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface); + +already_AddRefed<gfx::DataSourceSurface> GetSurfaceForDescriptor( + const SurfaceDescriptor& aDescriptor); + +uint8_t* GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor); + +void DestroySurfaceDescriptor(mozilla::ipc::IShmemAllocator* aAllocator, + SurfaceDescriptor* aSurface); + +class GfxMemoryImageReporter final : public nsIMemoryReporter { + ~GfxMemoryImageReporter() = default; + + public: + NS_DECL_ISUPPORTS + + GfxMemoryImageReporter() { +#ifdef DEBUG + // There must be only one instance of this class, due to |sAmount| + // being static. + static bool hasRun = false; + MOZ_ASSERT(!hasRun); + hasRun = true; +#endif + } + + MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc) + MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree) + + static void DidAlloc(void* aPointer) { + sAmount += MallocSizeOfOnAlloc(aPointer); + } + + static void WillFree(void* aPointer) { + sAmount -= MallocSizeOfOnFree(aPointer); + } + + NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) override { + MOZ_COLLECT_REPORT( + "explicit/gfx/heap-textures", KIND_HEAP, UNITS_BYTES, sAmount, + "Heap memory shared between threads by texture clients and hosts."); + + return NS_OK; + } + + private: + // Typically we use |size_t| in memory reporters, but in the past this + // variable has sometimes gone negative due to missing DidAlloc() calls. + // Therefore, we use a signed type so that any such negative values show up + // as negative in about:memory, rather than as enormous positive numbers. + static mozilla::Atomic<ptrdiff_t> sAmount; +}; + +/// A simple shmem section allocator that can only allocate small +/// fixed size elements (only intended to be used to store tile +/// copy-on-write locks for now). +class FixedSizeSmallShmemSectionAllocator final : public ShmemSectionAllocator { + public: + enum AllocationStatus { STATUS_ALLOCATED, STATUS_FREED }; + + struct ShmemSectionHeapHeader { + Atomic<uint32_t> mTotalBlocks; + Atomic<uint32_t> mAllocatedBlocks; + }; + + struct ShmemSectionHeapAllocation { + Atomic<uint32_t> mStatus; + uint32_t mSize; + }; + + explicit FixedSizeSmallShmemSectionAllocator(LayersIPCChannel* aShmProvider); + + ~FixedSizeSmallShmemSectionAllocator(); + + bool AllocShmemSection(uint32_t aSize, ShmemSection* aShmemSection) override; + + void DeallocShmemSection(ShmemSection& aShmemSection) override; + + void MemoryPressure() override { ShrinkShmemSectionHeap(); } + + // can be called on the compositor process. + static void FreeShmemSection(ShmemSection& aShmemSection); + + void ShrinkShmemSectionHeap(); + + bool IPCOpen() const; + + protected: + std::vector<mozilla::ipc::Shmem> mUsedShmems; + LayersIPCChannel* mShmProvider; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp new file mode 100644 index 0000000000..6dcf2be415 --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -0,0 +1,939 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "ImageBridgeChild.h" + +#include <vector> // for vector + +#include "ImageBridgeParent.h" // for ImageBridgeParent +#include "ImageContainer.h" // for ImageContainer +#include "SynchronousTask.h" +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock +#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc +#include "mozilla/StaticMutex.h" +#include "mozilla/StaticPtr.h" // for StaticRefPtr +#include "mozilla/dom/ContentChild.h" +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc +#include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator +#include "mozilla/layers/ImageClient.h" // for ImageClient +#include "mozilla/layers/LayersMessages.h" // for CompositableOperation +#include "mozilla/layers/TextureClient.h" // for TextureClient +#include "mozilla/layers/TextureClient.h" +#include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager +#include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild +#include "mozilla/mozalloc.h" // for operator new, etc +#include "transport/runnable_utils.h" +#include "nsContentUtils.h" +#include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc +#include "nsTArray.h" // for AutoTArray, nsTArray, etc +#include "nsTArrayForwardDeclare.h" // for AutoTArray +#include "nsThreadUtils.h" // for NS_IsMainThread + +#if defined(XP_WIN) +# include "mozilla/gfx/DeviceManagerDx.h" +#endif + +namespace mozilla { +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layers { + +using namespace mozilla::ipc; +using namespace mozilla::gfx; +using namespace mozilla::media; + +typedef std::vector<CompositableOperation> OpVector; +typedef nsTArray<OpDestroy> OpDestroyVector; + +struct CompositableTransaction { + CompositableTransaction() : mFinished(true) {} + ~CompositableTransaction() { End(); } + bool Finished() const { return mFinished; } + void Begin() { + MOZ_ASSERT(mFinished); + mFinished = false; + } + void End() { + mFinished = true; + mOperations.clear(); + mDestroyedActors.Clear(); + } + bool IsEmpty() const { + return mOperations.empty() && mDestroyedActors.IsEmpty(); + } + void AddNoSwapEdit(const CompositableOperation& op) { + MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); + mOperations.push_back(op); + } + + OpVector mOperations; + OpDestroyVector mDestroyedActors; + + bool mFinished; +}; + +struct AutoEndTransaction final { + explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {} + ~AutoEndTransaction() { mTxn->End(); } + CompositableTransaction* mTxn; +}; + +void ImageBridgeChild::UseTextures( + CompositableClient* aCompositable, + const nsTArray<TimedTextureClient>& aTextures) { + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aCompositable->GetIPCHandle()); + MOZ_ASSERT(aCompositable->IsConnected()); + + AutoTArray<TimedTexture, 4> textures; + + for (auto& t : aTextures) { + MOZ_ASSERT(t.mTextureClient); + MOZ_ASSERT(t.mTextureClient->GetIPDLActor()); + + if (!t.mTextureClient->IsSharedWithCompositor()) { + return; + } + + bool readLocked = t.mTextureClient->OnForwardedToHost(); + + textures.AppendElement(TimedTexture( + WrapNotNull(t.mTextureClient->GetIPDLActor()), t.mTimeStamp, + t.mPictureRect, t.mFrameID, t.mProducerID, readLocked)); + + // Wait end of usage on host side if TextureFlags::RECYCLE is set + HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient); + } + mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(), + OpUseTexture(textures))); +} + +void ImageBridgeChild::UseRemoteTexture(CompositableClient* aCompositable, + const RemoteTextureId aTextureId, + const RemoteTextureOwnerId aOwnerId, + const gfx::IntSize aSize, + const TextureFlags aFlags) { + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aCompositable->GetIPCHandle()); + MOZ_ASSERT(aCompositable->IsConnected()); + + mTxn->AddNoSwapEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpUseRemoteTexture(aTextureId, aOwnerId, aSize, aFlags))); +} + +void ImageBridgeChild::EnableRemoteTexturePushCallback( + CompositableClient* aCompositable, const RemoteTextureOwnerId aOwnerId, + const gfx::IntSize aSize, const TextureFlags aFlags) { + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aCompositable->GetIPCHandle()); + MOZ_ASSERT(aCompositable->IsConnected()); + + mTxn->AddNoSwapEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpEnableRemoteTexturePushCallback(aOwnerId, aSize, aFlags))); +} + +void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary( + TextureClient* aClient) { + if (!aClient) { + return; + } + + // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE or + // TextureFlags::WAIT_HOST_USAGE_END is set on ImageBridge. + bool waitNotifyNotUsed = + aClient->GetFlags() & TextureFlags::RECYCLE || + aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END; + if (!waitNotifyNotUsed) { + return; + } + + aClient->SetLastFwdTransactionId(GetFwdTransactionId()); + mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient); +} + +void ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId, + uint64_t aFwdTransactionId) { + auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId); + if (it != mTexturesWaitingNotifyNotUsed.end()) { + if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) { + // Released on host side, but client already requested newer use texture. + return; + } + mTexturesWaitingNotifyNotUsed.erase(it); + } +} + +void ImageBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) { + MOZ_ASSERT(InImageBridgeChildThread()); + mTexturesWaitingNotifyNotUsed.erase(aTextureId); +} + +// Singleton +static StaticMutex sImageBridgeSingletonLock MOZ_UNANNOTATED; +static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton; +static StaticRefPtr<nsIThread> sImageBridgeChildThread; + +// dispatched function +void ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask) { + AutoCompleteTask complete(aTask); + + MOZ_ASSERT(InImageBridgeChildThread(), + "Should be in ImageBridgeChild thread."); + + MediaSystemResourceManager::Shutdown(); + + // Force all managed protocols to shut themselves down cleanly + nsTArray<PTextureChild*> textures; + ManagedPTextureChild(textures); + for (int i = textures.Length() - 1; i >= 0; --i) { + RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]); + if (client) { + client->Destroy(); + } + } + + if (mCanSend) { + SendWillClose(); + } + MarkShutDown(); + + // From now on, no message can be sent through the image bridge from the + // client side except the final Stop message. +} + +// dispatched function +void ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask) { + AutoCompleteTask complete(aTask); + + MOZ_ASSERT(InImageBridgeChildThread(), + "Should be in ImageBridgeChild thread."); + if (!mDestroyed) { + Close(); + } +} + +void ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { + mCanSend = false; + mDestroyed = true; + { + MutexAutoLock lock(mContainerMapLock); + mImageContainerListeners.clear(); + } +} + +void ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask, + RefPtr<ImageClient>* result, + CompositableType aType, + ImageContainer* aImageContainer) { + AutoCompleteTask complete(aTask); + *result = CreateImageClientNow(aType, aImageContainer); +} + +ImageBridgeChild::ImageBridgeChild(uint32_t aNamespace) + : mNamespace(aNamespace), + mCanSend(false), + mDestroyed(false), + mFwdTransactionId(0), + mContainerMapLock("ImageBridgeChild.mContainerMapLock") { + MOZ_ASSERT(mNamespace); + MOZ_ASSERT(NS_IsMainThread()); + + mTxn = new CompositableTransaction(); +} + +ImageBridgeChild::~ImageBridgeChild() { delete mTxn; } + +void ImageBridgeChild::MarkShutDown() { + mTexturesWaitingNotifyNotUsed.clear(); + + mCanSend = false; +} + +void ImageBridgeChild::Connect(CompositableClient* aCompositable, + ImageContainer* aImageContainer) { + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(InImageBridgeChildThread()); + MOZ_ASSERT(CanSend()); + + CompositableHandle handle = CompositableHandle::GetNext(); + + // ImageClient of ImageContainer provides aImageContainer. + // But offscreen canvas does not provide it. + if (aImageContainer) { + MutexAutoLock lock(mContainerMapLock); + MOZ_ASSERT(mImageContainerListeners.find(uint64_t(handle)) == + mImageContainerListeners.end()); + mImageContainerListeners.emplace( + uint64_t(handle), aImageContainer->GetImageContainerListener()); + } + + aCompositable->InitIPDL(handle); + SendNewCompositable(handle, aCompositable->GetTextureInfo()); +} + +void ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle) { + MutexAutoLock lock(mContainerMapLock); + mImageContainerListeners.erase(aHandle.Value()); +} + +/* static */ +RefPtr<ImageBridgeChild> ImageBridgeChild::GetSingleton() { + StaticMutexAutoLock lock(sImageBridgeSingletonLock); + return sImageBridgeChildSingleton; +} + +void ImageBridgeChild::UpdateImageClient(RefPtr<ImageContainer> aContainer) { + if (!aContainer) { + return; + } + + if (!InImageBridgeChildThread()) { + RefPtr<Runnable> runnable = + WrapRunnable(RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::UpdateImageClient, aContainer); + GetThread()->Dispatch(runnable.forget()); + return; + } + + if (!CanSend()) { + return; + } + + RefPtr<ImageClient> client = aContainer->GetImageClient(); + if (NS_WARN_IF(!client)) { + return; + } + + // If the client has become disconnected before this event was dispatched, + // early return now. + if (!client->IsConnected()) { + return; + } + + BeginTransaction(); + client->UpdateImage(aContainer); + EndTransaction(); +} + +void ImageBridgeChild::UpdateCompositable( + const RefPtr<ImageContainer> aContainer, const RemoteTextureId aTextureId, + const RemoteTextureOwnerId aOwnerId, const gfx::IntSize aSize, + const TextureFlags aFlags) { + if (!aContainer) { + return; + } + + if (!InImageBridgeChildThread()) { + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::UpdateCompositable, + aContainer, aTextureId, aOwnerId, aSize, aFlags); + GetThread()->Dispatch(runnable.forget()); + return; + } + + if (!CanSend()) { + return; + } + + RefPtr<ImageClient> client = aContainer->GetImageClient(); + if (NS_WARN_IF(!client)) { + return; + } + + // If the client has become disconnected before this event was dispatched, + // early return now. + if (!client->IsConnected()) { + return; + } + + BeginTransaction(); + UseRemoteTexture(client, aTextureId, aOwnerId, aSize, aFlags); + EndTransaction(); +} + +void ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask, + ImageClient* aClient, + ImageContainer* aContainer) { + AutoCompleteTask complete(aTask); + + if (!CanSend()) { + return; + } + + MOZ_ASSERT(aClient); + BeginTransaction(); + if (aContainer) { + aContainer->ClearImagesFromImageBridge(); + } + aClient->FlushAllImages(); + EndTransaction(); +} + +void ImageBridgeChild::FlushAllImages(ImageClient* aClient, + ImageContainer* aContainer) { + MOZ_ASSERT(aClient); + MOZ_ASSERT(!InImageBridgeChildThread()); + + if (InImageBridgeChildThread()) { + NS_ERROR( + "ImageBridgeChild::FlushAllImages() is called on ImageBridge thread."); + return; + } + + SynchronousTask task("FlushAllImages Lock"); + + // RefPtrs on arguments are not needed since this dispatches synchronously. + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::FlushAllImagesSync, + &task, aClient, aContainer); + GetThread()->Dispatch(runnable.forget()); + + task.Wait(); +} + +void ImageBridgeChild::BeginTransaction() { + MOZ_ASSERT(CanSend()); + MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?"); + UpdateFwdTransactionId(); + mTxn->Begin(); +} + +void ImageBridgeChild::EndTransaction() { + MOZ_ASSERT(CanSend()); + MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?"); + + AutoEndTransaction _(mTxn); + + if (mTxn->IsEmpty()) { + return; + } + + AutoTArray<CompositableOperation, 10> cset; + cset.SetCapacity(mTxn->mOperations.size()); + if (!mTxn->mOperations.empty()) { + cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size()); + } + + if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) { + NS_WARNING("could not send async texture transaction"); + return; + } +} + +bool ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint, + uint32_t aNamespace) { + MOZ_ASSERT(NS_IsMainThread()); + + gfxPlatform::GetPlatform(); + + if (!sImageBridgeChildThread) { + nsCOMPtr<nsIThread> thread; + nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread)); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), + "Failed to start ImageBridgeChild thread!"); + sImageBridgeChildThread = thread.forget(); + } + + RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace); + + child->GetThread()->Dispatch(NS_NewRunnableFunction( + "layers::ImageBridgeChild::Bind", + [child, endpoint = std::move(aEndpoint)]() mutable { + child->Bind(std::move(endpoint)); + })); + + // Assign this after so other threads can't post messages before we connect to + // IPDL. + { + StaticMutexAutoLock lock(sImageBridgeSingletonLock); + sImageBridgeChildSingleton = child; + } + + return true; +} + +bool ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint, + uint32_t aNamespace) { + MOZ_ASSERT(NS_IsMainThread()); + + // Note that at this point, ActorDestroy may not have been called yet, + // meaning mCanSend is still true. In this case we will try to send a + // synchronous WillClose message to the parent, and will certainly get a + // false result and a MsgDropped processing error. This is okay. + ShutdownSingleton(); + + return InitForContent(std::move(aEndpoint), aNamespace); +} + +void ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + return; + } + + mCanSend = true; +} + +void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) { + Open(aParent, aParent->GetThread(), mozilla::ipc::ChildSide); + + mCanSend = true; +} + +/* static */ +void ImageBridgeChild::ShutDown() { + MOZ_ASSERT(NS_IsMainThread()); + + ShutdownSingleton(); + + if (sImageBridgeChildThread) { + sImageBridgeChildThread->Shutdown(); + sImageBridgeChildThread = nullptr; + } +} + +/* static */ +void ImageBridgeChild::ShutdownSingleton() { + MOZ_ASSERT(NS_IsMainThread()); + + if (RefPtr<ImageBridgeChild> child = GetSingleton()) { + child->WillShutdown(); + + StaticMutexAutoLock lock(sImageBridgeSingletonLock); + sImageBridgeChildSingleton = nullptr; + } +} + +void ImageBridgeChild::WillShutdown() { + { + SynchronousTask task("ImageBridge ShutdownStep1 lock"); + + RefPtr<Runnable> runnable = + WrapRunnable(RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::ShutdownStep1, &task); + GetThread()->Dispatch(runnable.forget()); + + task.Wait(); + } + + { + SynchronousTask task("ImageBridge ShutdownStep2 lock"); + + RefPtr<Runnable> runnable = + WrapRunnable(RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::ShutdownStep2, &task); + GetThread()->Dispatch(runnable.forget()); + + task.Wait(); + } +} + +void ImageBridgeChild::InitSameProcess(uint32_t aNamespace) { + NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); + + MOZ_ASSERT(!sImageBridgeChildSingleton); + MOZ_ASSERT(!sImageBridgeChildThread); + + nsCOMPtr<nsIThread> thread; + nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread)); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), + "Failed to start ImageBridgeChild thread!"); + sImageBridgeChildThread = thread.forget(); + + RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace); + RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess(); + + RefPtr<Runnable> runnable = + WrapRunnable(child, &ImageBridgeChild::BindSameProcess, parent); + child->GetThread()->Dispatch(runnable.forget()); + + // Assign this after so other threads can't post messages before we connect to + // IPDL. + { + StaticMutexAutoLock lock(sImageBridgeSingletonLock); + sImageBridgeChildSingleton = child; + } +} + +/* static */ +void ImageBridgeChild::InitWithGPUProcess( + Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sImageBridgeChildSingleton); + MOZ_ASSERT(!sImageBridgeChildThread); + + nsCOMPtr<nsIThread> thread; + nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread)); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), + "Failed to start ImageBridgeChild thread!"); + sImageBridgeChildThread = thread.forget(); + + RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace); + + child->GetThread()->Dispatch(NS_NewRunnableFunction( + "layers::ImageBridgeChild::Bind", + [child, endpoint = std::move(aEndpoint)]() mutable { + child->Bind(std::move(endpoint)); + })); + + // Assign this after so other threads can't post messages before we connect to + // IPDL. + { + StaticMutexAutoLock lock(sImageBridgeSingletonLock); + sImageBridgeChildSingleton = child; + } +} + +bool InImageBridgeChildThread() { + return sImageBridgeChildThread && + sImageBridgeChildThread->IsOnCurrentThread(); +} + +nsISerialEventTarget* ImageBridgeChild::GetThread() const { + return sImageBridgeChildThread; +} + +/* static */ +void ImageBridgeChild::IdentifyCompositorTextureHost( + const TextureFactoryIdentifier& aIdentifier) { + if (RefPtr<ImageBridgeChild> child = GetSingleton()) { + child->UpdateTextureFactoryIdentifier(aIdentifier); + } +} + +void ImageBridgeChild::UpdateTextureFactoryIdentifier( + const TextureFactoryIdentifier& aIdentifier) { + IdentifyTextureHost(aIdentifier); +} + +RefPtr<ImageClient> ImageBridgeChild::CreateImageClient( + CompositableType aType, ImageContainer* aImageContainer) { + if (InImageBridgeChildThread()) { + return CreateImageClientNow(aType, aImageContainer); + } + + SynchronousTask task("CreateImageClient Lock"); + + RefPtr<ImageClient> result = nullptr; + + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::CreateImageClientSync, + &task, &result, aType, aImageContainer); + GetThread()->Dispatch(runnable.forget()); + + task.Wait(); + + return result; +} + +RefPtr<ImageClient> ImageBridgeChild::CreateImageClientNow( + CompositableType aType, ImageContainer* aImageContainer) { + MOZ_ASSERT(InImageBridgeChildThread()); + if (!CanSend()) { + return nullptr; + } + + RefPtr<ImageClient> client = + ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS); + MOZ_ASSERT(client, "failed to create ImageClient"); + if (client) { + client->Connect(aImageContainer); + } + return client; +} + +bool ImageBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) { + if (!InImageBridgeChildThread()) { + return DispatchAllocShmemInternal(aSize, aShmem, + true); // true: unsafe + } + + if (!CanSend()) { + return false; + } + return PImageBridgeChild::AllocUnsafeShmem(aSize, aShmem); +} + +bool ImageBridgeChild::AllocShmem(size_t aSize, ipc::Shmem* aShmem) { + if (!InImageBridgeChildThread()) { + return DispatchAllocShmemInternal(aSize, aShmem, + false); // false: unsafe + } + + if (!CanSend()) { + return false; + } + return PImageBridgeChild::AllocShmem(aSize, aShmem); +} + +void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize, + ipc::Shmem* aShmem, bool aUnsafe, + bool* aSuccess) { + AutoCompleteTask complete(aTask); + + if (!CanSend()) { + return; + } + + bool ok = false; + if (aUnsafe) { + ok = AllocUnsafeShmem(aSize, aShmem); + } else { + ok = AllocShmem(aSize, aShmem); + } + *aSuccess = ok; +} + +bool ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize, + ipc::Shmem* aShmem, + bool aUnsafe) { + SynchronousTask task("AllocatorProxy alloc"); + + bool success = false; + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyAllocShmemNow, + &task, aSize, aShmem, aUnsafe, &success); + GetThread()->Dispatch(runnable.forget()); + + task.Wait(); + + return success; +} + +void ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask, + ipc::Shmem* aShmem, bool* aResult) { + AutoCompleteTask complete(aTask); + + if (!CanSend()) { + return; + } + *aResult = DeallocShmem(*aShmem); +} + +bool ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem) { + if (InImageBridgeChildThread()) { + if (!CanSend()) { + return false; + } + return PImageBridgeChild::DeallocShmem(aShmem); + } + + // If we can't post a task, then we definitely cannot send, so there's + // no reason to queue up this send. + if (!CanPostTask()) { + return false; + } + + SynchronousTask task("AllocatorProxy Dealloc"); + bool result = false; + + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyDeallocShmemNow, + &task, &aShmem, &result); + GetThread()->Dispatch(runnable.forget()); + + task.Wait(); + return result; +} + +PTextureChild* ImageBridgeChild::AllocPTextureChild( + const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&, + const TextureFlags&, const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId) { + MOZ_ASSERT(CanSend()); + return TextureClient::CreateIPDLActor(); +} + +bool ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) { + return TextureClient::DestroyIPDLActor(actor); +} + +PMediaSystemResourceManagerChild* +ImageBridgeChild::AllocPMediaSystemResourceManagerChild() { + MOZ_ASSERT(CanSend()); + return new mozilla::media::MediaSystemResourceManagerChild(); +} + +bool ImageBridgeChild::DeallocPMediaSystemResourceManagerChild( + PMediaSystemResourceManagerChild* aActor) { + MOZ_ASSERT(aActor); + delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor); + return true; +} + +mozilla::ipc::IPCResult ImageBridgeChild::RecvParentAsyncMessages( + nsTArray<AsyncParentMessageData>&& aMessages) { + for (AsyncParentMessageArray::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 IPC_FAIL_NO_REASON(this); + } + } + return IPC_OK(); +} + +RefPtr<ImageContainerListener> ImageBridgeChild::FindListener( + const CompositableHandle& aHandle) { + RefPtr<ImageContainerListener> listener; + MutexAutoLock lock(mContainerMapLock); + auto it = mImageContainerListeners.find(aHandle.Value()); + if (it != mImageContainerListeners.end()) { + listener = it->second; + } + return listener; +} + +mozilla::ipc::IPCResult ImageBridgeChild::RecvDidComposite( + nsTArray<ImageCompositeNotification>&& aNotifications) { + for (auto& n : aNotifications) { + RefPtr<ImageContainerListener> listener = FindListener(n.compositable()); + if (listener) { + listener->NotifyComposite(n); + } + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult ImageBridgeChild::RecvReportFramesDropped( + const CompositableHandle& aHandle, const uint32_t& aFrames) { + RefPtr<ImageContainerListener> listener = FindListener(aHandle); + if (listener) { + listener->NotifyDropped(aFrames); + } + + return IPC_OK(); +} + +PTextureChild* ImageBridgeChild::CreateTexture( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, + const dom::ContentParentId& aContentId, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId) { + MOZ_ASSERT(CanSend()); + return SendPTextureConstructor(aSharedData, std::move(aReadLock), + aLayersBackend, aFlags, aSerial, + aExternalImageId); +} + +static bool IBCAddOpDestroy(CompositableTransaction* aTxn, + const OpDestroy& op) { + if (aTxn->Finished()) { + return false; + } + + aTxn->mDestroyedActors.AppendElement(op); + return true; +} + +bool ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture) { + return IBCAddOpDestroy(mTxn, OpDestroy(WrapNotNull(aTexture))); +} + +bool ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle) { + return IBCAddOpDestroy(mTxn, OpDestroy(aHandle)); +} + +void ImageBridgeChild::RemoveTextureFromCompositable( + CompositableClient* aCompositable, TextureClient* aTexture) { + MOZ_ASSERT(CanSend()); + MOZ_ASSERT(aTexture); + MOZ_ASSERT(aTexture->IsSharedWithCompositor()); + MOZ_ASSERT(aCompositable->IsConnected()); + if (!aTexture || !aTexture->IsSharedWithCompositor() || + !aCompositable->IsConnected()) { + return; + } + + mTxn->AddNoSwapEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpRemoveTexture(WrapNotNull(aTexture->GetIPDLActor())))); +} + +bool ImageBridgeChild::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +bool ImageBridgeChild::CanPostTask() const { + // During shutdown, the cycle collector may free objects that are holding a + // reference to ImageBridgeChild. Since this happens on the main thread, + // ImageBridgeChild will attempt to post a task to the ImageBridge thread. + // However the thread manager has already been shut down, so the task cannot + // post. + // + // It's okay if this races. We only care about the shutdown case where + // everything's happening on the main thread. Even if it races outside of + // shutdown, it's still harmless to post the task, since the task must + // check CanSend(). + return !mDestroyed; +} + +void ImageBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle) { + if (!InImageBridgeChildThread()) { + // If we can't post a task, then we definitely cannot send, so there's + // no reason to queue up this send. + if (!CanPostTask()) { + return; + } + + RefPtr<Runnable> runnable = + WrapRunnable(RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::ReleaseCompositable, aHandle); + GetThread()->Dispatch(runnable.forget()); + return; + } + + if (!CanSend()) { + return; + } + + if (!DestroyInTransaction(aHandle)) { + SendReleaseCompositable(aHandle); + } + + { + MutexAutoLock lock(mContainerMapLock); + mImageContainerListeners.erase(aHandle.Value()); + } +} + +bool ImageBridgeChild::CanSend() const { + MOZ_ASSERT(InImageBridgeChildThread()); + return mCanSend; +} + +void ImageBridgeChild::HandleFatalError(const char* aMsg) { + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); +} + +wr::MaybeExternalImageId ImageBridgeChild::GetNextExternalImageId() { + static uint32_t sNextID = 1; + ++sNextID; + MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX); + + uint64_t imageId = mNamespace; + imageId = imageId << 32 | sNextID; + return Some(wr::ToExternalImageId(imageId)); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ImageBridgeChild.h b/gfx/layers/ipc/ImageBridgeChild.h new file mode 100644 index 0000000000..ee8ff97299 --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeChild.h @@ -0,0 +1,376 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_GFX_IMAGEBRIDGECHILD_H +#define MOZILLA_GFX_IMAGEBRIDGECHILD_H + +#include <stddef.h> // for size_t +#include <stdint.h> // for uint32_t, uint64_t +#include <unordered_map> + +#include "mozilla/Attributes.h" // for override +#include "mozilla/Atomics.h" +#include "mozilla/RefPtr.h" // for already_AddRefed +#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc +#include "mozilla/layers/CompositableForwarder.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/PImageBridgeChild.h" +#include "mozilla/layers/TextureForwarder.h" +#include "mozilla/Mutex.h" +#include "mozilla/webrender/WebRenderTypes.h" +#include "nsRegion.h" // for nsIntRegion +#include "mozilla/gfx/Rect.h" +#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc + +namespace mozilla { +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layers { + +class ImageClient; +class ImageContainer; +class ImageContainerListener; +class ImageBridgeParent; +class CompositableClient; +struct CompositableTransaction; +class Image; +class TextureClient; +class SynchronousTask; + +/** + * Returns true if the current thread is the ImageBrdigeChild's thread. + * + * Can be called from any thread. + */ +bool InImageBridgeChildThread(); + +/** + * The ImageBridge protocol is meant to allow ImageContainers to forward images + * directly to the compositor thread/process without using the main thread. + * + * ImageBridgeChild is a CompositableForwarder just like ShadowLayerForwarder. + * This means it also does transactions with the compositor thread/process, + * except that the transactions are restricted to operations on the + * Compositables and cannot contain messages affecting layers directly. + * + * ImageBridgeChild is also a ISurfaceAllocator. It can be used to allocate or + * deallocate data that is shared with the compositor. The main differerence + * with other ISurfaceAllocators is that some of its overriden methods can be + * invoked from any thread. + * + * There are three important phases in the ImageBridge protocol. These three + * steps can do different things depending if (A) the ImageContainer uses + * ImageBridge or (B) it does not use ImageBridge: + * + * - When an ImageContainer calls its method SetCurrentImage: + * - (A) The image is sent directly to the compositor process through the + * ImageBridge IPDL protocol. + * On the compositor side the image is stored in a global table that + * associates the image with an ID corresponding to the ImageContainer, and a + * composition is triggered. + * - (B) Since it does not have an ImageBridge, the image is not sent yet. + * instead the will be sent to the compositor during the next layer + * transaction (on the main thread). + * + * - During a Layer transaction: + * - (A) The ImageContainer uses ImageBridge. The image is already available + * to the compositor process because it has been sent with SetCurrentImage. + * Yet, the CompositableHost on the compositor side will needs the ID + * referring to the ImageContainer to access the Image. So during the Swap + * operation that happens in the transaction, we swap the container ID rather + * than the image data. + * - (B) Since the ImageContainer does not use ImageBridge, the image data is + * swaped. + * + * - During composition: + * - (A) The CompositableHost has an AsyncID, it looks up the ID in the + * global table to see if there is an image. If there is no image, nothing is + * rendered. + * - (B) The CompositableHost has image data rather than an ID (meaning it is + * not using ImageBridge), then it just composites the image data normally. + * + * This means that there might be a possibility for the ImageBridge to send the + * first frame before the first layer transaction that will pass the container + * ID to the CompositableHost happens. In this (unlikely) case the layer is not + * composited until the layer transaction happens. This means this scenario is + * not harmful. + * + * Since sending an image through imageBridge triggers compositing, the main + * thread is not used at all (except for the very first transaction that + * provides the CompositableHost with an AsyncID). + */ +class ImageBridgeChild final : public PImageBridgeChild, + public CompositableForwarder, + public TextureForwarder { + friend class ImageContainer; + + typedef nsTArray<AsyncParentMessageData> AsyncParentMessageArray; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageBridgeChild, override); + + TextureForwarder* GetTextureForwarder() override { return this; } + LayersIPCActor* GetLayersIPCActor() override { return this; } + + /** + * Creates the image bridge with a dedicated thread for ImageBridgeChild. + * + * We may want to use a specifi thread in the future. In this case, use + * CreateWithThread instead. + */ + static void InitSameProcess(uint32_t aNamespace); + + static void InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint, + uint32_t aNamespace); + static bool InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint, + uint32_t aNamespace); + static bool ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint, + uint32_t aNamespace); + + /** + * Destroys the image bridge by calling DestroyBridge, and destroys the + * ImageBridge's thread. + * + * If you don't want to destroy the thread, call DestroyBridge directly + * instead. + */ + static void ShutDown(); + + /** + * returns the singleton instance. + * + * can be called from any thread. + */ + static RefPtr<ImageBridgeChild> GetSingleton(); + + static void IdentifyCompositorTextureHost( + const TextureFactoryIdentifier& aIdentifier); + + void BeginTransaction(); + void EndTransaction(); + + /** + * Returns the ImageBridgeChild's thread. + * + * Can be called from any thread. + */ + nsISerialEventTarget* GetThread() const override; + + base::ProcessId GetParentPid() const override { return OtherPid(); } + + PTextureChild* AllocPTextureChild( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId); + + bool DeallocPTextureChild(PTextureChild* actor); + + PMediaSystemResourceManagerChild* AllocPMediaSystemResourceManagerChild(); + bool DeallocPMediaSystemResourceManagerChild( + PMediaSystemResourceManagerChild* aActor); + + mozilla::ipc::IPCResult RecvParentAsyncMessages( + nsTArray<AsyncParentMessageData>&& aMessages); + + mozilla::ipc::IPCResult RecvDidComposite( + nsTArray<ImageCompositeNotification>&& aNotifications); + + mozilla::ipc::IPCResult RecvReportFramesDropped( + const CompositableHandle& aHandle, const uint32_t& aFrames); + + // Create an ImageClient from any thread. + RefPtr<ImageClient> CreateImageClient(CompositableType aType, + ImageContainer* aImageContainer); + + // Create an ImageClient from the ImageBridge thread. + RefPtr<ImageClient> CreateImageClientNow(CompositableType aType, + ImageContainer* aImageContainer); + + void UpdateImageClient(RefPtr<ImageContainer> aContainer); + + void UpdateCompositable(const RefPtr<ImageContainer> aContainer, + const RemoteTextureId aTextureId, + const RemoteTextureOwnerId aOwnerId, + const gfx::IntSize aSize, const TextureFlags aFlags); + + /** + * Flush all Images sent to CompositableHost. + */ + void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer); + + bool IPCOpen() const override { return mCanSend; } + + private: + /** + * This must be called by the static function DeleteImageBridgeSync defined + * in ImageBridgeChild.cpp ONLY. + */ + virtual ~ImageBridgeChild(); + + // Helpers for dispatching. + void CreateImageClientSync(SynchronousTask* aTask, + RefPtr<ImageClient>* result, + CompositableType aType, + ImageContainer* aImageContainer); + + void FlushAllImagesSync(SynchronousTask* aTask, ImageClient* aClient, + ImageContainer* aContainer); + + void ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize, + mozilla::ipc::Shmem* aShmem, bool aUnsafe, + bool* aSuccess); + void ProxyDeallocShmemNow(SynchronousTask* aTask, mozilla::ipc::Shmem* aShmem, + bool* aResult); + + void UpdateTextureFactoryIdentifier( + const TextureFactoryIdentifier& aIdentifier); + + public: + // CompositableForwarder + + void Connect(CompositableClient* aCompositable, + ImageContainer* aImageContainer) override; + + bool UsesImageBridge() const override { return true; } + + /** + * See CompositableForwarder::UseTextures + */ + void UseTextures(CompositableClient* aCompositable, + const nsTArray<TimedTextureClient>& aTextures) override; + + void UseRemoteTexture(CompositableClient* aCompositable, + const RemoteTextureId aTextureId, + const RemoteTextureOwnerId aOwnerId, + const gfx::IntSize aSize, + const TextureFlags aFlags) override; + + void EnableRemoteTexturePushCallback(CompositableClient* aCompositable, + const RemoteTextureOwnerId aOwnerId, + const gfx::IntSize aSize, + const TextureFlags aFlags) override; + + void ReleaseCompositable(const CompositableHandle& aHandle) override; + + void ForgetImageContainer(const CompositableHandle& aHandle); + + /** + * Hold TextureClient ref until end of usage on host side if + * TextureFlags::RECYCLE is set. Host side's usage is checked via + * CompositableRef. + */ + void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient); + + /** + * Notify id of Texture When host side end its use. Transaction id is used to + * make sure if there is no newer usage. + */ + void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId); + + void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override; + + bool DestroyInTransaction(PTextureChild* aTexture) override; + bool DestroyInTransaction(const CompositableHandle& aHandle); + + void RemoveTextureFromCompositable(CompositableClient* aCompositable, + TextureClient* aTexture) override; + + // ISurfaceAllocator + + /** + * See ISurfaceAllocator.h + * Can be used from any thread. + * If used outside the ImageBridgeChild thread, it will proxy a synchronous + * call on the ImageBridgeChild thread. + */ + bool AllocUnsafeShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override; + bool AllocShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override; + + /** + * See ISurfaceAllocator.h + * Can be used from any thread. + * If used outside the ImageBridgeChild thread, it will proxy a synchronous + * call on the ImageBridgeChild thread. + */ + bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + + PTextureChild* CreateTexture( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, + const dom::ContentParentId& aContentId, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId) override; + + bool IsSameProcess() const override; + + void UpdateFwdTransactionId() override { ++mFwdTransactionId; } + uint64_t GetFwdTransactionId() override { return mFwdTransactionId; } + + bool InForwarderThread() override { return InImageBridgeChildThread(); } + + void HandleFatalError(const char* aMsg) override; + + wr::MaybeExternalImageId GetNextExternalImageId() override; + + protected: + explicit ImageBridgeChild(uint32_t aNamespace); + bool DispatchAllocShmemInternal(size_t aSize, Shmem* aShmem, bool aUnsafe); + + void Bind(Endpoint<PImageBridgeChild>&& aEndpoint); + void BindSameProcess(RefPtr<ImageBridgeParent> aParent); + + void SendImageBridgeThreadId(); + + void WillShutdown(); + void ShutdownStep1(SynchronousTask* aTask); + void ShutdownStep2(SynchronousTask* aTask); + void MarkShutDown(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + bool CanSend() const; + bool CanPostTask() const; + + static void ShutdownSingleton(); + + private: + uint32_t mNamespace; + + CompositableTransaction* mTxn; + + bool mCanSend; + mozilla::Atomic<bool> mDestroyed; + + /** + * Transaction id of CompositableForwarder. + * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction() + * call. + */ + uint64_t mFwdTransactionId; + + /** + * Hold TextureClients refs until end of their usages on host side. + * It defer calling of TextureClient recycle callback. + */ + std::unordered_map<uint64_t, RefPtr<TextureClient>> + mTexturesWaitingNotifyNotUsed; + + /** + * Mapping from async compositable IDs to image containers. + */ + Mutex mContainerMapLock MOZ_UNANNOTATED; + std::unordered_map<uint64_t, RefPtr<ImageContainerListener>> + mImageContainerListeners; + RefPtr<ImageContainerListener> FindListener( + const CompositableHandle& aHandle); +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp new file mode 100644 index 0000000000..bd9b2f6dd7 --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -0,0 +1,428 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "ImageBridgeParent.h" +#include <stdint.h> // for uint64_t, uint32_t +#include "CompositableHost.h" // for CompositableParent, Create +#include "base/process.h" // for ProcessId +#include "base/task.h" // for CancelableTask, DeleteTask, etc +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/Hal.h" // for hal::SetCurrentThreadPriority() +#include "mozilla/HalTypes.h" // for hal::THREAD_PRIORITY_COMPOSITOR +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc +#include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent +#include "mozilla/layers/BufferTexture.h" +#include "mozilla/layers/CompositableTransactionParent.h" +#include "mozilla/layers/LayersMessages.h" // for EditReply +#include "mozilla/layers/PImageBridgeParent.h" +#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL +#include "mozilla/layers/Compositor.h" +#include "mozilla/Monitor.h" +#include "mozilla/mozalloc.h" // for operator new, etc +#include "mozilla/ProfilerLabels.h" +#include "mozilla/ProfilerMarkers.h" +#include "mozilla/Unused.h" +#include "nsDebug.h" // for NS_ASSERTION, etc +#include "nsISupportsImpl.h" // for ImageBridgeParent::Release, etc +#include "nsTArray.h" // for nsTArray, nsTArray_Impl +#include "nsTArrayForwardDeclare.h" // for nsTArray +#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop +#include "mozilla/layers/TextureHost.h" +#include "nsThreadUtils.h" + +#if defined(OS_WIN) +# include "mozilla/layers/TextureD3D11.h" +#endif + +namespace mozilla { +namespace layers { + +using namespace mozilla::ipc; +using namespace mozilla::gfx; +using namespace mozilla::media; + +ImageBridgeParent::ImageBridgeMap ImageBridgeParent::sImageBridges; + +StaticAutoPtr<mozilla::Monitor> sImageBridgesLock; + +static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton; + +/* static */ +void ImageBridgeParent::Setup() { + MOZ_ASSERT(NS_IsMainThread()); + if (!sImageBridgesLock) { + sImageBridgesLock = new Monitor("ImageBridges"); + mozilla::ClearOnShutdown(&sImageBridgesLock); + } +} + +ImageBridgeParent::ImageBridgeParent(nsISerialEventTarget* aThread, + ProcessId aChildProcessId, + dom::ContentParentId aContentId) + : mThread(aThread), + mContentId(aContentId), + mClosed(false), + mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) { + MOZ_ASSERT(NS_IsMainThread()); + SetOtherProcessId(aChildProcessId); +} + +ImageBridgeParent::~ImageBridgeParent() = default; + +/* static */ +ImageBridgeParent* ImageBridgeParent::CreateSameProcess() { + base::ProcessId pid = base::GetCurrentProcId(); + RefPtr<ImageBridgeParent> parent = + new ImageBridgeParent(CompositorThread(), pid, dom::ContentParentId()); + + { + MonitorAutoLock lock(*sImageBridgesLock); + MOZ_RELEASE_ASSERT(sImageBridges.count(pid) == 0); + sImageBridges[pid] = parent; + } + + sImageBridgeParentSingleton = parent; + return parent; +} + +/* static */ +bool ImageBridgeParent::CreateForGPUProcess( + Endpoint<PImageBridgeParent>&& aEndpoint) { + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); + + nsCOMPtr<nsISerialEventTarget> compositorThread = CompositorThread(); + if (!compositorThread) { + return false; + } + + RefPtr<ImageBridgeParent> parent = new ImageBridgeParent( + compositorThread, aEndpoint.OtherPid(), dom::ContentParentId()); + + compositorThread->Dispatch(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>( + "layers::ImageBridgeParent::Bind", parent, &ImageBridgeParent::Bind, + std::move(aEndpoint))); + + sImageBridgeParentSingleton = parent; + return true; +} + +/* static */ +void ImageBridgeParent::ShutdownInternal() { + // We make a copy because we don't want to hold the lock while closing and we + // don't want the object to get freed underneath us. + nsTArray<RefPtr<ImageBridgeParent>> actors; + { + MonitorAutoLock lock(*sImageBridgesLock); + for (const auto& iter : sImageBridges) { + actors.AppendElement(iter.second); + } + } + + for (auto const& actor : actors) { + MOZ_RELEASE_ASSERT(!actor->mClosed); + actor->Close(); + } + + sImageBridgeParentSingleton = nullptr; +} + +/* static */ +void ImageBridgeParent::Shutdown() { + CompositorThread()->Dispatch(NS_NewRunnableFunction( + "ImageBridgeParent::Shutdown", + []() -> void { ImageBridgeParent::ShutdownInternal(); })); +} + +void ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { + // Can't alloc/dealloc shmems from now on. + mClosed = true; + + for (const auto& entry : mCompositables) { + entry.second->OnReleased(); + } + mCompositables.clear(); + { + MonitorAutoLock lock(*sImageBridgesLock); + sImageBridges.erase(OtherPid()); + } + GetThread()->Dispatch( + NewRunnableMethod("layers::ImageBridgeParent::DeferredDestroy", this, + &ImageBridgeParent::DeferredDestroy)); + + // It is very important that this method gets called at shutdown (be it a + // clean or an abnormal shutdown), because DeferredDestroy is what clears + // mCompositorThreadHolder. If mCompositorThreadHolder is not null and + // ActorDestroy is not called, the ImageBridgeParent is leaked which causes + // the CompositorThreadHolder to be leaked and CompsoitorParent's shutdown + // ends up spinning the event loop forever, waiting for the compositor thread + // to terminate. +} + +class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender final { + public: + explicit AutoImageBridgeParentAsyncMessageSender( + ImageBridgeParent* aImageBridge, + nsTArray<OpDestroy>* aToDestroy = nullptr) + : mImageBridge(aImageBridge), mToDestroy(aToDestroy) { + mImageBridge->SetAboutToSendAsyncMessages(); + } + + ~AutoImageBridgeParentAsyncMessageSender() { + mImageBridge->SendPendingAsyncMessages(); + if (mToDestroy) { + for (const auto& op : *mToDestroy) { + mImageBridge->DestroyActor(op); + } + } + } + + private: + ImageBridgeParent* mImageBridge; + nsTArray<OpDestroy>* mToDestroy; +}; + +mozilla::ipc::IPCResult ImageBridgeParent::RecvUpdate( + EditArray&& aEdits, OpDestroyArray&& aToDestroy, + const uint64_t& aFwdTransactionId) { + AUTO_PROFILER_TRACING_MARKER("Paint", "ImageBridgeTransaction", GRAPHICS); + AUTO_PROFILER_LABEL("ImageBridgeParent::RecvUpdate", GRAPHICS); + + // This ensures that destroy operations are always processed. It is not safe + // to early-return from RecvUpdate without doing so. + AutoImageBridgeParentAsyncMessageSender autoAsyncMessageSender(this, + &aToDestroy); + UpdateFwdTransactionId(aFwdTransactionId); + + for (const auto& edit : aEdits) { + RefPtr<CompositableHost> compositable = + FindCompositable(edit.compositable()); + if (!compositable || + !ReceiveCompositableUpdate(edit.detail(), WrapNotNull(compositable), + edit.compositable())) { + return IPC_FAIL_NO_REASON(this); + } + uint32_t dropped = compositable->GetDroppedFrames(); + if (dropped) { + Unused << SendReportFramesDropped(edit.compositable(), dropped); + } + } + + return IPC_OK(); +} + +/* static */ +bool ImageBridgeParent::CreateForContent( + Endpoint<PImageBridgeParent>&& aEndpoint, dom::ContentParentId aContentId) { + nsCOMPtr<nsISerialEventTarget> compositorThread = CompositorThread(); + if (!compositorThread) { + return false; + } + + RefPtr<ImageBridgeParent> bridge = + new ImageBridgeParent(compositorThread, aEndpoint.OtherPid(), aContentId); + compositorThread->Dispatch(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>( + "layers::ImageBridgeParent::Bind", bridge, &ImageBridgeParent::Bind, + std::move(aEndpoint))); + + return true; +} + +void ImageBridgeParent::Bind(Endpoint<PImageBridgeParent>&& aEndpoint) { + if (!aEndpoint.Bind(this)) return; + + // If the child process ID was reused by the OS before the ImageBridgeParent + // object was destroyed, we need to clean it up first. + RefPtr<ImageBridgeParent> oldActor; + { + MonitorAutoLock lock(*sImageBridgesLock); + ImageBridgeMap::const_iterator i = sImageBridges.find(OtherPid()); + if (i != sImageBridges.end()) { + oldActor = i->second; + } + } + + // We can't hold the lock during Close because it erases itself from the map. + if (oldActor) { + MOZ_RELEASE_ASSERT(!oldActor->mClosed); + oldActor->Close(); + } + + { + MonitorAutoLock lock(*sImageBridgesLock); + sImageBridges[OtherPid()] = this; + } +} + +mozilla::ipc::IPCResult ImageBridgeParent::RecvWillClose() { + // If there is any texture still alive we have to force it to deallocate the + // device data (GL textures, etc.) now because shortly after SenStop() returns + // on the child side the widget will be destroyed along with it's associated + // GL context. + nsTArray<PTextureParent*> textures; + ManagedPTextureParent(textures); + for (unsigned int i = 0; i < textures.Length(); ++i) { + RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]); + tex->DeallocateDeviceData(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult ImageBridgeParent::RecvNewCompositable( + const CompositableHandle& aHandle, const TextureInfo& aInfo) { + RefPtr<CompositableHost> host = AddCompositable(aHandle, aInfo); + if (!host) { + return IPC_FAIL_NO_REASON(this); + } + + host->SetAsyncRef(AsyncCompositableRef(OtherPid(), aHandle)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult ImageBridgeParent::RecvReleaseCompositable( + const CompositableHandle& aHandle) { + ReleaseCompositable(aHandle); + return IPC_OK(); +} + +PTextureParent* ImageBridgeParent::AllocPTextureParent( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const uint64_t& aSerial, const wr::MaybeExternalImageId& aExternalImageId) { + return TextureHost::CreateIPDLActor(this, aSharedData, std::move(aReadLock), + aLayersBackend, aFlags, mContentId, + aSerial, aExternalImageId); +} + +bool ImageBridgeParent::DeallocPTextureParent(PTextureParent* actor) { + return TextureHost::DestroyIPDLActor(actor); +} + +PMediaSystemResourceManagerParent* +ImageBridgeParent::AllocPMediaSystemResourceManagerParent() { + return new mozilla::media::MediaSystemResourceManagerParent(); +} + +bool ImageBridgeParent::DeallocPMediaSystemResourceManagerParent( + PMediaSystemResourceManagerParent* aActor) { + MOZ_ASSERT(aActor); + delete static_cast<mozilla::media::MediaSystemResourceManagerParent*>(aActor); + return true; +} + +void ImageBridgeParent::SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) { + mozilla::Unused << SendParentAsyncMessages(aMessage); +} + +class ProcessIdComparator { + public: + bool Equals(const ImageCompositeNotificationInfo& aA, + const ImageCompositeNotificationInfo& aB) const { + return aA.mImageBridgeProcessId == aB.mImageBridgeProcessId; + } + bool LessThan(const ImageCompositeNotificationInfo& aA, + const ImageCompositeNotificationInfo& aB) const { + return aA.mImageBridgeProcessId < aB.mImageBridgeProcessId; + } +}; + +/* static */ +bool ImageBridgeParent::NotifyImageComposites( + nsTArray<ImageCompositeNotificationInfo>& aNotifications) { + // Group the notifications by destination process ID and then send the + // notifications in one message per group. + aNotifications.Sort(ProcessIdComparator()); + uint32_t i = 0; + bool ok = true; + while (i < aNotifications.Length()) { + AutoTArray<ImageCompositeNotification, 1> notifications; + notifications.AppendElement(aNotifications[i].mNotification); + uint32_t end = i + 1; + MOZ_ASSERT(aNotifications[i].mNotification.compositable()); + ProcessId pid = aNotifications[i].mImageBridgeProcessId; + while (end < aNotifications.Length() && + aNotifications[end].mImageBridgeProcessId == pid) { + notifications.AppendElement(aNotifications[end].mNotification); + ++end; + } + RefPtr<ImageBridgeParent> bridge = GetInstance(pid); + if (!bridge || bridge->mClosed) { + i = end; + continue; + } + bridge->SendPendingAsyncMessages(); + if (!bridge->SendDidComposite(notifications)) { + ok = false; + } + i = end; + } + return ok; +} + +void ImageBridgeParent::DeferredDestroy() { mCompositorThreadHolder = nullptr; } + +already_AddRefed<ImageBridgeParent> ImageBridgeParent::GetInstance( + ProcessId aId) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MonitorAutoLock lock(*sImageBridgesLock); + ImageBridgeMap::const_iterator i = sImageBridges.find(aId); + if (i == sImageBridges.end()) { + NS_WARNING("Cannot find image bridge for process!"); + return nullptr; + } + RefPtr<ImageBridgeParent> bridge = i->second; + return bridge.forget(); +} + +bool ImageBridgeParent::AllocShmem(size_t aSize, ipc::Shmem* aShmem) { + if (mClosed) { + return false; + } + return PImageBridgeParent::AllocShmem(aSize, aShmem); +} + +bool ImageBridgeParent::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) { + if (mClosed) { + return false; + } + return PImageBridgeParent::AllocUnsafeShmem(aSize, aShmem); +} + +bool ImageBridgeParent::DeallocShmem(ipc::Shmem& aShmem) { + if (mClosed) { + return false; + } + return PImageBridgeParent::DeallocShmem(aShmem); +} + +bool ImageBridgeParent::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void ImageBridgeParent::NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) { + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); + if (!texture) { + return; + } + + if (!(texture->GetFlags() & TextureFlags::RECYCLE) && + !(texture->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END)) { + return; + } + + uint64_t textureId = TextureHost::GetTextureSerial(aTexture); + mPendingAsyncMessage.push_back(OpNotifyNotUsed(textureId, aTransactionId)); + + if (!IsAboutToSendAsyncMessages()) { + SendPendingAsyncMessages(); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h new file mode 100644 index 0000000000..7f2c7ce4ce --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeParent.h @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef gfx_layers_ipc_ImageBridgeParent_h_ +#define gfx_layers_ipc_ImageBridgeParent_h_ + +#include <stddef.h> // for size_t +#include <stdint.h> // for uint32_t, uint64_t +#include "CompositableTransactionParent.h" +#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 +#include "mozilla/Attributes.h" // for override +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/PImageBridgeParent.h" +#include "nsISupportsImpl.h" +#include "nsTArrayForwardDeclare.h" // for nsTArray + +namespace mozilla { +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layers { + +struct ImageCompositeNotificationInfo; + +/** + * ImageBridgeParent is the manager Protocol of async Compositables. + */ +class ImageBridgeParent final : public PImageBridgeParent, + public CompositableParentManager, + public mozilla::ipc::IShmemAllocator { + public: + typedef nsTArray<CompositableOperation> EditArray; + typedef nsTArray<OpDestroy> OpDestroyArray; + + protected: + ImageBridgeParent(nsISerialEventTarget* aThread, ProcessId aChildProcessId, + dom::ContentParentId aContentId); + + public: + NS_IMETHOD_(MozExternalRefCountType) AddRef() override { + return ISurfaceAllocator::AddRef(); + } + NS_IMETHOD_(MozExternalRefCountType) Release() override { + return ISurfaceAllocator::Release(); + } + + /** + * Creates the globals of ImageBridgeParent. + */ + static void Setup(); + + static ImageBridgeParent* CreateSameProcess(); + static bool CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint); + static bool CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint, + dom::ContentParentId aContentId); + static void Shutdown(); + + IShmemAllocator* AsShmemAllocator() override { return this; } + + void ActorDestroy(ActorDestroyReason aWhy) override; + + // CompositableParentManager + void SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) override; + + void NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) override; + + base::ProcessId GetChildProcessId() override { return OtherPid(); } + dom::ContentParentId GetContentId() override { return mContentId; } + + // PImageBridge + mozilla::ipc::IPCResult RecvUpdate(EditArray&& aEdits, + OpDestroyArray&& aToDestroy, + const uint64_t& aFwdTransactionId); + + PTextureParent* AllocPTextureParent( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId); + bool DeallocPTextureParent(PTextureParent* actor); + + mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle, + const TextureInfo& aInfo); + mozilla::ipc::IPCResult RecvReleaseCompositable( + const CompositableHandle& aHandle); + + PMediaSystemResourceManagerParent* AllocPMediaSystemResourceManagerParent(); + bool DeallocPMediaSystemResourceManagerParent( + PMediaSystemResourceManagerParent* aActor); + + // Shutdown step 1 + mozilla::ipc::IPCResult RecvWillClose(); + + nsISerialEventTarget* GetThread() const { return mThread; } + + // IShmemAllocator + + bool AllocShmem(size_t aSize, ipc::Shmem* aShmem) override; + + bool AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) override; + + bool DeallocShmem(ipc::Shmem& aShmem) override; + + bool IsSameProcess() const override; + + static already_AddRefed<ImageBridgeParent> GetInstance(ProcessId aId); + + static bool NotifyImageComposites( + nsTArray<ImageCompositeNotificationInfo>& aNotifications); + + bool UsesImageBridge() const override { return true; } + + bool IPCOpen() const override { return !mClosed; } + + protected: + void Bind(Endpoint<PImageBridgeParent>&& aEndpoint); + + private: + virtual ~ImageBridgeParent(); + + static void ShutdownInternal(); + + void DeferredDestroy(); + nsCOMPtr<nsISerialEventTarget> mThread; + + dom::ContentParentId mContentId; + + bool mClosed; + + /** + * Map of all living ImageBridgeParent instances + */ + typedef std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeMap; + static ImageBridgeMap sImageBridges; + + RefPtr<CompositorThreadHolder> mCompositorThreadHolder; +}; + +} // namespace layers +} // namespace mozilla + +#endif // gfx_layers_ipc_ImageBridgeParent_h_ diff --git a/gfx/layers/ipc/KnowsCompositor.cpp b/gfx/layers/ipc/KnowsCompositor.cpp new file mode 100644 index 0000000000..4d2ed90603 --- /dev/null +++ b/gfx/layers/ipc/KnowsCompositor.cpp @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "KnowsCompositor.h" +#include "mozilla/layers/ImageDataSerializer.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/ipc/ProtocolUtils.h" + +namespace mozilla { +namespace layers { + +void KnowsCompositor::IdentifyTextureHost( + const TextureFactoryIdentifier& aIdentifier) { + auto lock = mData.Lock(); + lock.ref().mTextureFactoryIdentifier = aIdentifier; + + lock.ref().mSyncObject = + SyncObjectClient::CreateSyncObjectClientForContentDevice( + aIdentifier.mSyncHandle); +} + +KnowsCompositor::KnowsCompositor() + : mData("KnowsCompositorMutex"), mSerial(++sSerialCounter) {} + +KnowsCompositor::~KnowsCompositor() = default; + +KnowsCompositorMediaProxy::KnowsCompositorMediaProxy( + const TextureFactoryIdentifier& aIdentifier) { + auto lock = mData.Lock(); + lock.ref().mTextureFactoryIdentifier = aIdentifier; + // overwrite mSerial's value set by the parent class because we use the same + // serial as the KnowsCompositor we are proxying. + mThreadSafeAllocator = ImageBridgeChild::GetSingleton(); + lock.ref().mSyncObject = mThreadSafeAllocator->GetSyncObject(); +} + +KnowsCompositorMediaProxy::~KnowsCompositorMediaProxy() = default; + +TextureForwarder* KnowsCompositorMediaProxy::GetTextureForwarder() { + return mThreadSafeAllocator->GetTextureForwarder(); +} + +LayersIPCActor* KnowsCompositorMediaProxy::GetLayersIPCActor() { + return mThreadSafeAllocator->GetLayersIPCActor(); +} + +ActiveResourceTracker* KnowsCompositorMediaProxy::GetActiveResourceTracker() { + return mThreadSafeAllocator->GetActiveResourceTracker(); +} + +void KnowsCompositorMediaProxy::SyncWithCompositor() { + mThreadSafeAllocator->SyncWithCompositor(); +} + +bool IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface) { + return aSurface.type() != SurfaceDescriptor::T__None && + aSurface.type() != SurfaceDescriptor::Tnull_t; +} + +uint8_t* GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor) { + MOZ_ASSERT(IsSurfaceDescriptorValid(aDescriptor)); + MOZ_RELEASE_ASSERT( + aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer, + "GFX: surface descriptor is not the right type."); + + auto memOrShmem = aDescriptor.get_SurfaceDescriptorBuffer().data(); + if (memOrShmem.type() == MemoryOrShmem::TShmem) { + return memOrShmem.get_Shmem().get<uint8_t>(); + } else { + return reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t()); + } +} + +already_AddRefed<gfx::DataSourceSurface> GetSurfaceForDescriptor( + const SurfaceDescriptor& aDescriptor) { + if (aDescriptor.type() != SurfaceDescriptor::TSurfaceDescriptorBuffer) { + return nullptr; + } + uint8_t* data = GetAddressFromDescriptor(aDescriptor); + auto rgb = + aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor(); + uint32_t stride = ImageDataSerializer::GetRGBStride(rgb); + return gfx::Factory::CreateWrappingDataSourceSurface(data, stride, rgb.size(), + rgb.format()); +} + +void DestroySurfaceDescriptor(ipc::IShmemAllocator* aAllocator, + SurfaceDescriptor* aSurface) { + MOZ_ASSERT(aSurface); + + SurfaceDescriptorBuffer& desc = aSurface->get_SurfaceDescriptorBuffer(); + switch (desc.data().type()) { + case MemoryOrShmem::TShmem: { + aAllocator->DeallocShmem(desc.data().get_Shmem()); + break; + } + case MemoryOrShmem::Tuintptr_t: { + uint8_t* ptr = (uint8_t*)desc.data().get_uintptr_t(); + GfxMemoryImageReporter::WillFree(ptr); + delete[] ptr; + break; + } + default: + MOZ_CRASH("surface type not implemented!"); + } + *aSurface = SurfaceDescriptor(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/KnowsCompositor.h b/gfx/layers/ipc/KnowsCompositor.h new file mode 100644 index 0000000000..7c8c3b780d --- /dev/null +++ b/gfx/layers/ipc/KnowsCompositor.h @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_LAYERS_KNOWSCOMPOSITOR +#define MOZILLA_LAYERS_KNOWSCOMPOSITOR + +#include "mozilla/layers/LayersTypes.h" // for LayersBackend +#include "mozilla/layers/CompositorTypes.h" +#include "nsExpirationTracker.h" +#include "mozilla/DataMutex.h" +#include "mozilla/layers/SyncObject.h" + +namespace mozilla { +namespace layers { + +class TextureForwarder; +class LayersIPCActor; +class ImageBridgeChild; + +/** + * See ActiveResourceTracker below. + */ +class ActiveResource { + public: + virtual void NotifyInactive() = 0; + nsExpirationState* GetExpirationState() { return &mExpirationState; } + bool IsActivityTracked() { return mExpirationState.IsTracked(); } + + private: + nsExpirationState mExpirationState; +}; + +/** + * A convenience class on top of nsExpirationTracker + */ +class ActiveResourceTracker : public nsExpirationTracker<ActiveResource, 3> { + public: + ActiveResourceTracker(uint32_t aExpirationCycle, const char* aName, + nsIEventTarget* aEventTarget) + : nsExpirationTracker(aExpirationCycle, aName, aEventTarget) {} + + void NotifyExpired(ActiveResource* aResource) override { + RemoveObject(aResource); + aResource->NotifyInactive(); + } +}; + +/** + * An abstract interface for classes that are tied to a specific Compositor + * across IPDL and uses TextureFactoryIdentifier to describe this Compositor. + */ +class KnowsCompositor { + public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + KnowsCompositor(); + virtual ~KnowsCompositor(); + + void IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier); + + // The sync object for the global content device. + RefPtr<SyncObjectClient> GetSyncObject() { + auto lock = mData.Lock(); + if (lock.ref().mSyncObject) { + lock.ref().mSyncObject->EnsureInitialized(); + } + return lock.ref().mSyncObject; + } + + /// And by "thread-safe" here we merely mean "okay to hold strong references + /// to from multiple threads". Not all methods actually are thread-safe. + virtual bool IsThreadSafe() const { return true; } + + virtual RefPtr<KnowsCompositor> GetForMedia() { + return RefPtr<KnowsCompositor>(this); + } + + int32_t GetMaxTextureSize() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mMaxTextureSize; + } + + /** + * Returns the type of backend that is used off the main thread. + * We only don't allow changing the backend type at runtime so this value can + * be queried once and will not change until Gecko is restarted. + */ + LayersBackend GetCompositorBackendType() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mParentBackend; + } + + WebRenderCompositor GetWebRenderCompositorType() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor; + } + + bool SupportsTextureBlitting() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mSupportsTextureBlitting; + } + + bool SupportsPartialUploads() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mSupportsPartialUploads; + } + + bool SupportsComponentAlpha() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mSupportsComponentAlpha; + } + + bool SupportsD3D11NV12() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mSupportsD3D11NV12; + } + + bool SupportsD3D11() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mParentBackend == + layers::LayersBackend::LAYERS_WR && + (lock.ref().mTextureFactoryIdentifier.mCompositorUseANGLE || + lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor == + layers::WebRenderCompositor::D3D11); + } + + bool GetCompositorUseANGLE() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mCompositorUseANGLE; + } + + bool GetCompositorUseDComp() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mCompositorUseDComp; + } + + bool GetUseCompositorWnd() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mUseCompositorWnd; + } + + WebRenderBackend GetWebRenderBackend() const { + auto lock = mData.Lock(); + MOZ_ASSERT(lock.ref().mTextureFactoryIdentifier.mParentBackend == + layers::LayersBackend::LAYERS_WR); + return lock.ref().mTextureFactoryIdentifier.mWebRenderBackend; + } + + bool UsingHardwareWebRender() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mParentBackend == + layers::LayersBackend::LAYERS_WR && + lock.ref().mTextureFactoryIdentifier.mWebRenderBackend == + WebRenderBackend::HARDWARE; + } + + bool UsingSoftwareWebRender() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mParentBackend == + layers::LayersBackend::LAYERS_WR && + lock.ref().mTextureFactoryIdentifier.mWebRenderBackend == + WebRenderBackend::SOFTWARE; + } + + bool UsingSoftwareWebRenderD3D11() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mParentBackend == + layers::LayersBackend::LAYERS_WR && + lock.ref().mTextureFactoryIdentifier.mWebRenderBackend == + WebRenderBackend::SOFTWARE && + lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor == + layers::WebRenderCompositor::D3D11; + } + + bool UsingSoftwareWebRenderOpenGL() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mParentBackend == + layers::LayersBackend::LAYERS_WR && + lock.ref().mTextureFactoryIdentifier.mWebRenderBackend == + WebRenderBackend::SOFTWARE && + lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor == + layers::WebRenderCompositor::OPENGL; + } + + TextureFactoryIdentifier GetTextureFactoryIdentifier() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier; + } + + int32_t GetSerial() const { return mSerial; } + + /** + * Sends a synchronous ping to the compsoitor. + * + * This is bad for performance and should only be called as a last resort if + * the compositor may be blocked for a long period of time, to avoid that the + * content process accumulates resource allocations that the compositor is not + * consuming and releasing. + */ + virtual void SyncWithCompositor() { MOZ_ASSERT_UNREACHABLE("Unimplemented"); } + + /** + * Helpers for finding other related interface. These are infallible. + */ + virtual TextureForwarder* GetTextureForwarder() = 0; + virtual LayersIPCActor* GetLayersIPCActor() = 0; + virtual ActiveResourceTracker* GetActiveResourceTracker() { + MOZ_ASSERT_UNREACHABLE("Unimplemented"); + return nullptr; + } + + protected: + struct SharedData { + TextureFactoryIdentifier mTextureFactoryIdentifier; + RefPtr<SyncObjectClient> mSyncObject; + }; + mutable DataMutex<SharedData> mData; + + const int32_t mSerial; + static mozilla::Atomic<int32_t> sSerialCounter; +}; + +/// Some implementations of KnowsCompositor can be used off their IPDL thread +/// like the ImageBridgeChild, and others just can't. Instead of passing them +/// we create a proxy KnowsCompositor that has information about compositor +/// backend but proxies allocations to the ImageBridge. +/// This is kind of specific to the needs of media which wants to allocate +/// textures, usually through the Image Bridge accessed by KnowsCompositor but +/// also wants access to the compositor backend information that ImageBridge +/// doesn't know about. +/// +/// This is really a band aid to what turned into a class hierarchy horror show. +/// Hopefully we can come back and simplify this some way. +class KnowsCompositorMediaProxy : public KnowsCompositor { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KnowsCompositorMediaProxy, override); + + explicit KnowsCompositorMediaProxy( + const TextureFactoryIdentifier& aIdentifier); + + TextureForwarder* GetTextureForwarder() override; + + LayersIPCActor* GetLayersIPCActor() override; + + ActiveResourceTracker* GetActiveResourceTracker() override; + + void SyncWithCompositor() override; + + protected: + virtual ~KnowsCompositorMediaProxy(); + + RefPtr<ImageBridgeChild> mThreadSafeAllocator; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/LayerTreeOwnerTracker.cpp b/gfx/layers/ipc/LayerTreeOwnerTracker.cpp new file mode 100644 index 0000000000..ab3551ad29 --- /dev/null +++ b/gfx/layers/ipc/LayerTreeOwnerTracker.cpp @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "LayerTreeOwnerTracker.h" + +#include "mozilla/StaticPtr.h" // for StaticAutoPtr +#include "mozilla/gfx/GPUChild.h" // for GPUChild +#include "mozilla/gfx/GPUProcessManager.h" // for GPUProcessManager + +#include <functional> +#include <utility> // for std::make_pair + +namespace mozilla { +namespace layers { + +static StaticAutoPtr<LayerTreeOwnerTracker> sSingleton; + +LayerTreeOwnerTracker::LayerTreeOwnerTracker() + : mLayerIdsLock("LayerTreeOwnerTrackerLock") {} + +void LayerTreeOwnerTracker::Initialize() { + MOZ_ASSERT(!sSingleton); + sSingleton = new LayerTreeOwnerTracker(); +} + +void LayerTreeOwnerTracker::Shutdown() { sSingleton = nullptr; } + +LayerTreeOwnerTracker* LayerTreeOwnerTracker::Get() { return sSingleton; } + +void LayerTreeOwnerTracker::Map(LayersId aLayersId, + base::ProcessId aProcessId) { + MutexAutoLock lock(mLayerIdsLock); + + // Add the mapping to the list + mLayerIds[aLayersId] = aProcessId; +} + +void LayerTreeOwnerTracker::Unmap(LayersId aLayersId, + base::ProcessId aProcessId) { + MutexAutoLock lock(mLayerIdsLock); + + MOZ_ASSERT(mLayerIds[aLayersId] == aProcessId); + mLayerIds.erase(aLayersId); +} + +bool LayerTreeOwnerTracker::IsMapped(LayersId aLayersId, + base::ProcessId aProcessId) { + MutexAutoLock lock(mLayerIdsLock); + + auto iter = mLayerIds.find(aLayersId); + return iter != mLayerIds.end() && iter->second == aProcessId; +} + +void LayerTreeOwnerTracker::Iterate( + const std::function<void(LayersId aLayersId, base::ProcessId aProcessId)>& + aCallback) { + MutexAutoLock lock(mLayerIdsLock); + + for (const auto& iter : mLayerIds) { + aCallback(iter.first, iter.second); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/LayerTreeOwnerTracker.h b/gfx/layers/ipc/LayerTreeOwnerTracker.h new file mode 100644 index 0000000000..651c695309 --- /dev/null +++ b/gfx/layers/ipc/LayerTreeOwnerTracker.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_LayerTreeOwnerTracker_h +#define mozilla_layers_LayerTreeOwnerTracker_h + +#include "base/process.h" // for base::ProcessId +#include "LayersTypes.h" // for LayersId +#include "mozilla/Mutex.h" // for mozilla::Mutex + +#include <functional> +#include <map> + +namespace mozilla { + +namespace dom { +class ContentParent; +} + +namespace layers { + +/** + * A utility class for tracking which content processes should be allowed + * to access which layer trees. + * + * ProcessId's are used to track which content process can access the layer + * tree, and in the case of nested browser's we use the top level content + * processes' ProcessId. + * + * This class is only available in the main process and gpu process. Mappings + * are synced from main process to the gpu process. The actual syncing happens + * in GPUProcessManager, and so this class should not be used directly. + */ +class LayerTreeOwnerTracker final { + public: + static void Initialize(); + static void Shutdown(); + static LayerTreeOwnerTracker* Get(); + + /** + * Map aLayersId and aProcessId together so that that process + * can access that layer tree. + */ + void Map(LayersId aLayersId, base::ProcessId aProcessId); + + /** + * Remove an existing mapping. + */ + void Unmap(LayersId aLayersId, base::ProcessId aProcessId); + + /** + * Checks whether it is okay for aProcessId to access aLayersId. + */ + bool IsMapped(LayersId aLayersId, base::ProcessId aProcessId); + + void Iterate( + const std::function<void(LayersId aLayersId, base::ProcessId aProcessId)>& + aCallback); + + private: + LayerTreeOwnerTracker(); + + mozilla::Mutex mLayerIdsLock MOZ_UNANNOTATED; + std::map<LayersId, base::ProcessId> mLayerIds; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_LayerTreeOwnerTracker_h diff --git a/gfx/layers/ipc/LayersMessageUtils.h b/gfx/layers/ipc/LayersMessageUtils.h new file mode 100644 index 0000000000..df421f79a6 --- /dev/null +++ b/gfx/layers/ipc/LayersMessageUtils.h @@ -0,0 +1,1156 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_LayersMessageUtils +#define mozilla_layers_LayersMessageUtils + +#include <stdint.h> + +#include <utility> + +#include "FrameMetrics.h" +#include "VsyncSource.h" +#include "chrome/common/ipc_message_utils.h" +#include "ipc/EnumSerializer.h" +#include "ipc/IPCMessageUtils.h" +#include "mozilla/MotionPathUtils.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/ipc/ByteBuf.h" +#include "mozilla/layers/APZInputBridge.h" +#include "mozilla/layers/AsyncDragMetrics.h" +#include "mozilla/layers/CompositorOptions.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/FocusTarget.h" +#include "mozilla/layers/GeckoContentControllerTypes.h" +#include "mozilla/layers/KeyboardMap.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/MatrixMessage.h" +#include "mozilla/layers/OverlayInfo.h" +#include "mozilla/layers/RepaintRequest.h" +#include "mozilla/layers/ScrollbarData.h" +#include "nsSize.h" +#include "mozilla/layers/DoubleTapToZoom.h" + +// For ParamTraits, could be moved to cpp file +#include "ipc/nsGUIEventIPC.h" +#include "mozilla/GfxMessageUtils.h" +#include "mozilla/ipc/ByteBufUtils.h" + +#ifdef _MSC_VER +# pragma warning(disable : 4800) +#endif + +namespace IPC { + +template <> +struct ParamTraits<mozilla::layers::LayersId> + : public PlainOldDataSerializer<mozilla::layers::LayersId> {}; + +template <typename T> +struct ParamTraits<mozilla::layers::BaseTransactionId<T>> + : public PlainOldDataSerializer<mozilla::layers::BaseTransactionId<T>> {}; + +template <> +struct ParamTraits<mozilla::VsyncId> + : public PlainOldDataSerializer<mozilla::VsyncId> {}; + +template <> +struct ParamTraits<mozilla::VsyncEvent> { + typedef mozilla::VsyncEvent paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.mId); + WriteParam(writer, param.mTime); + WriteParam(writer, param.mOutputTime); + } + static bool Read(MessageReader* reader, paramType* result) { + return ReadParam(reader, &result->mId) && + ReadParam(reader, &result->mTime) && + ReadParam(reader, &result->mOutputTime); + } +}; + +template <> +struct ParamTraits<mozilla::layers::MatrixMessage> { + typedef mozilla::layers::MatrixMessage paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mMatrix); + WriteParam(aWriter, aParam.mTopLevelViewportVisibleRectInBrowserCoords); + WriteParam(aWriter, aParam.mLayersId); + } + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mMatrix) && + ReadParam(aReader, + &aResult->mTopLevelViewportVisibleRectInBrowserCoords) && + ReadParam(aReader, &aResult->mLayersId); + } +}; + +template <> +struct ParamTraits<mozilla::layers::LayersObserverEpoch> + : public PlainOldDataSerializer<mozilla::layers::LayersObserverEpoch> {}; + +template <> +struct ParamTraits<mozilla::layers::WindowKind> + : public ContiguousEnumSerializer<mozilla::layers::WindowKind, + mozilla::layers::WindowKind::MAIN, + mozilla::layers::WindowKind::LAST> {}; + +template <> +struct ParamTraits<mozilla::layers::LayersBackend> + : public ContiguousEnumSerializer< + mozilla::layers::LayersBackend, + mozilla::layers::LayersBackend::LAYERS_NONE, + mozilla::layers::LayersBackend::LAYERS_LAST> {}; + +template <> +struct ParamTraits<mozilla::layers::WebRenderBackend> + : public ContiguousEnumSerializer< + mozilla::layers::WebRenderBackend, + mozilla::layers::WebRenderBackend::HARDWARE, + mozilla::layers::WebRenderBackend::LAST> {}; + +template <> +struct ParamTraits<mozilla::layers::WebRenderCompositor> + : public ContiguousEnumSerializer< + mozilla::layers::WebRenderCompositor, + mozilla::layers::WebRenderCompositor::DRAW, + mozilla::layers::WebRenderCompositor::LAST> {}; + +template <> +struct ParamTraits<mozilla::layers::TextureType> + : public ContiguousEnumSerializer<mozilla::layers::TextureType, + mozilla::layers::TextureType::Unknown, + mozilla::layers::TextureType::Last> {}; + +template <> +struct ParamTraits<mozilla::layers::ScaleMode> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::ScaleMode, mozilla::layers::ScaleMode::SCALE_NONE, + mozilla::layers::kHighestScaleMode> {}; + +template <> +struct ParamTraits<mozilla::StyleScrollSnapStrictness> + : public ContiguousEnumSerializerInclusive< + mozilla::StyleScrollSnapStrictness, + mozilla::StyleScrollSnapStrictness::None, + mozilla::StyleScrollSnapStrictness::Proximity> {}; + +template <> +struct ParamTraits<mozilla::layers::TextureFlags> + : public BitFlagsEnumSerializer<mozilla::layers::TextureFlags, + mozilla::layers::TextureFlags::ALL_BITS> {}; + +template <> +struct ParamTraits<mozilla::layers::DiagnosticTypes> + : public BitFlagsEnumSerializer< + mozilla::layers::DiagnosticTypes, + mozilla::layers::DiagnosticTypes::ALL_BITS> {}; + +template <> +struct ParamTraits<mozilla::layers::ScrollDirection> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::ScrollDirection, + mozilla::layers::ScrollDirection::eVertical, + mozilla::layers::kHighestScrollDirection> {}; + +template <> +struct ParamTraits<mozilla::layers::FrameMetrics::ScrollOffsetUpdateType> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::FrameMetrics::ScrollOffsetUpdateType, + mozilla::layers::FrameMetrics::ScrollOffsetUpdateType::eNone, + mozilla::layers::FrameMetrics::sHighestScrollOffsetUpdateType> {}; + +template <> +struct ParamTraits<mozilla::layers::RepaintRequest::ScrollOffsetUpdateType> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::RepaintRequest::ScrollOffsetUpdateType, + mozilla::layers::RepaintRequest::ScrollOffsetUpdateType::eNone, + mozilla::layers::RepaintRequest::sHighestScrollOffsetUpdateType> {}; + +template <> +struct ParamTraits<mozilla::layers::OverscrollBehavior> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::OverscrollBehavior, + mozilla::layers::OverscrollBehavior::Auto, + mozilla::layers::kHighestOverscrollBehavior> {}; + +template <> +struct ParamTraits<mozilla::layers::LayerHandle> { + typedef mozilla::layers::LayerHandle paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.mHandle); + } + static bool Read(MessageReader* reader, paramType* result) { + return ReadParam(reader, &result->mHandle); + } +}; + +template <> +struct ParamTraits<mozilla::layers::CompositableHandle> { + typedef mozilla::layers::CompositableHandle paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.mHandle); + } + static bool Read(MessageReader* reader, paramType* result) { + return ReadParam(reader, &result->mHandle); + } +}; + +template <> +struct ParamTraits<mozilla::layers::CompositableHandleOwner> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::CompositableHandleOwner, + mozilla::layers::CompositableHandleOwner::WebRenderBridge, + mozilla::layers::CompositableHandleOwner::ImageBridge> {}; + +template <> +struct ParamTraits<mozilla::layers::RemoteTextureId> { + typedef mozilla::layers::RemoteTextureId paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.mId); + } + static bool Read(MessageReader* reader, paramType* result) { + return ReadParam(reader, &result->mId); + } +}; + +template <> +struct ParamTraits<mozilla::layers::RemoteTextureOwnerId> { + typedef mozilla::layers::RemoteTextureOwnerId paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.mId); + } + static bool Read(MessageReader* reader, paramType* result) { + return ReadParam(reader, &result->mId); + } +}; + +template <> +struct ParamTraits<mozilla::layers::GpuProcessTextureId> { + typedef mozilla::layers::GpuProcessTextureId paramType; + + static void Write(MessageWriter* writer, const paramType& param) { + WriteParam(writer, param.mId); + } + static bool Read(MessageReader* reader, paramType* result) { + return ReadParam(reader, &result->mId); + } +}; + +template <> +struct ParamTraits<mozilla::layers::FrameMetrics> + : BitfieldHelper<mozilla::layers::FrameMetrics> { + typedef mozilla::layers::FrameMetrics paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mScrollId); + WriteParam(aWriter, aParam.mPresShellResolution); + WriteParam(aWriter, aParam.mCompositionBounds); + WriteParam(aWriter, aParam.mCompositionBoundsWidthIgnoringScrollbars); + WriteParam(aWriter, aParam.mDisplayPort); + WriteParam(aWriter, aParam.mScrollableRect); + WriteParam(aWriter, aParam.mCumulativeResolution); + WriteParam(aWriter, aParam.mDevPixelsPerCSSPixel); + WriteParam(aWriter, aParam.mScrollOffset); + WriteParam(aWriter, aParam.mZoom); + WriteParam(aWriter, aParam.mScrollGeneration); + WriteParam(aWriter, aParam.mBoundingCompositionSize); + WriteParam(aWriter, aParam.mPresShellId); + WriteParam(aWriter, aParam.mLayoutViewport); + WriteParam(aWriter, aParam.mTransformToAncestorScale); + WriteParam(aWriter, aParam.mPaintRequestTime); + WriteParam(aWriter, aParam.mVisualDestination); + WriteParam(aWriter, aParam.mVisualScrollUpdateType); + WriteParam(aWriter, aParam.mFixedLayerMargins); + WriteParam(aWriter, aParam.mCompositionSizeWithoutDynamicToolbar); + WriteParam(aWriter, aParam.mIsRootContent); + WriteParam(aWriter, aParam.mIsScrollInfoLayer); + WriteParam(aWriter, aParam.mHasNonZeroDisplayPortMargins); + WriteParam(aWriter, aParam.mMinimalDisplayPort); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ( + ReadParam(aReader, &aResult->mScrollId) && + ReadParam(aReader, &aResult->mPresShellResolution) && + ReadParam(aReader, &aResult->mCompositionBounds) && + ReadParam(aReader, + &aResult->mCompositionBoundsWidthIgnoringScrollbars) && + ReadParam(aReader, &aResult->mDisplayPort) && + ReadParam(aReader, &aResult->mScrollableRect) && + ReadParam(aReader, &aResult->mCumulativeResolution) && + ReadParam(aReader, &aResult->mDevPixelsPerCSSPixel) && + ReadParam(aReader, &aResult->mScrollOffset) && + ReadParam(aReader, &aResult->mZoom) && + ReadParam(aReader, &aResult->mScrollGeneration) && + ReadParam(aReader, &aResult->mBoundingCompositionSize) && + ReadParam(aReader, &aResult->mPresShellId) && + ReadParam(aReader, &aResult->mLayoutViewport) && + ReadParam(aReader, &aResult->mTransformToAncestorScale) && + ReadParam(aReader, &aResult->mPaintRequestTime) && + ReadParam(aReader, &aResult->mVisualDestination) && + ReadParam(aReader, &aResult->mVisualScrollUpdateType) && + ReadParam(aReader, &aResult->mFixedLayerMargins) && + ReadParam(aReader, &aResult->mCompositionSizeWithoutDynamicToolbar) && + ReadBoolForBitfield(aReader, aResult, ¶mType::SetIsRootContent) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetIsScrollInfoLayer) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetHasNonZeroDisplayPortMargins) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetMinimalDisplayPort)); + } +}; + +template <> +struct ParamTraits<mozilla::APZScrollAnimationType> + : public ContiguousEnumSerializerInclusive< + mozilla::APZScrollAnimationType, mozilla::APZScrollAnimationType::No, + mozilla::APZScrollAnimationType::TriggeredByUserInput> {}; + +template <> +struct ParamTraits<mozilla::ScrollSnapTargetIds> { + typedef mozilla::ScrollSnapTargetIds paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mIdsOnX); + WriteParam(aWriter, aParam.mIdsOnY); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mIdsOnX) && + ReadParam(aReader, &aResult->mIdsOnY); + } +}; + +template <> +struct ParamTraits<mozilla::layers::RepaintRequest> + : BitfieldHelper<mozilla::layers::RepaintRequest> { + typedef mozilla::layers::RepaintRequest paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mScrollId); + WriteParam(aWriter, aParam.mPresShellResolution); + WriteParam(aWriter, aParam.mCompositionBounds); + WriteParam(aWriter, aParam.mCumulativeResolution); + WriteParam(aWriter, aParam.mDevPixelsPerCSSPixel); + WriteParam(aWriter, aParam.mScrollOffset); + WriteParam(aWriter, aParam.mZoom); + WriteParam(aWriter, aParam.mScrollGeneration); + WriteParam(aWriter, aParam.mScrollGenerationOnApz); + WriteParam(aWriter, aParam.mDisplayPortMargins); + WriteParam(aWriter, aParam.mPresShellId); + WriteParam(aWriter, aParam.mLayoutViewport); + WriteParam(aWriter, aParam.mTransformToAncestorScale); + WriteParam(aWriter, aParam.mPaintRequestTime); + WriteParam(aWriter, aParam.mScrollUpdateType); + WriteParam(aWriter, aParam.mScrollAnimationType); + WriteParam(aWriter, aParam.mLastSnapTargetIds); + WriteParam(aWriter, aParam.mIsRootContent); + WriteParam(aWriter, aParam.mIsScrollInfoLayer); + WriteParam(aWriter, aParam.mIsInScrollingGesture); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ( + ReadParam(aReader, &aResult->mScrollId) && + ReadParam(aReader, &aResult->mPresShellResolution) && + ReadParam(aReader, &aResult->mCompositionBounds) && + ReadParam(aReader, &aResult->mCumulativeResolution) && + ReadParam(aReader, &aResult->mDevPixelsPerCSSPixel) && + ReadParam(aReader, &aResult->mScrollOffset) && + ReadParam(aReader, &aResult->mZoom) && + ReadParam(aReader, &aResult->mScrollGeneration) && + ReadParam(aReader, &aResult->mScrollGenerationOnApz) && + ReadParam(aReader, &aResult->mDisplayPortMargins) && + ReadParam(aReader, &aResult->mPresShellId) && + ReadParam(aReader, &aResult->mLayoutViewport) && + ReadParam(aReader, &aResult->mTransformToAncestorScale) && + ReadParam(aReader, &aResult->mPaintRequestTime) && + ReadParam(aReader, &aResult->mScrollUpdateType) && + ReadParam(aReader, &aResult->mScrollAnimationType) && + ReadParam(aReader, &aResult->mLastSnapTargetIds) && + ReadBoolForBitfield(aReader, aResult, ¶mType::SetIsRootContent) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetIsScrollInfoLayer) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetIsInScrollingGesture)); + } +}; + +template <> +struct ParamTraits<nsSize> { + typedef nsSize paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.width); + WriteParam(aWriter, aParam.height); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->width) && + ReadParam(aReader, &aResult->height); + } +}; + +template <> +struct ParamTraits<mozilla::StyleScrollSnapStop> + : public ContiguousEnumSerializerInclusive< + mozilla::StyleScrollSnapStop, mozilla::StyleScrollSnapStop::Normal, + mozilla::StyleScrollSnapStop::Always> {}; + +template <> +struct ParamTraits<mozilla::ScrollSnapTargetId> + : public PlainOldDataSerializer<mozilla::ScrollSnapTargetId> {}; + +template <> +struct ParamTraits<mozilla::layers::ScrollSnapInfo::SnapTarget> { + typedef mozilla::layers::ScrollSnapInfo::SnapTarget paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mSnapPositionX); + WriteParam(aWriter, aParam.mSnapPositionY); + WriteParam(aWriter, aParam.mSnapArea); + WriteParam(aWriter, aParam.mScrollSnapStop); + WriteParam(aWriter, aParam.mTargetId); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mSnapPositionX) && + ReadParam(aReader, &aResult->mSnapPositionY) && + ReadParam(aReader, &aResult->mSnapArea) && + ReadParam(aReader, &aResult->mScrollSnapStop) && + ReadParam(aReader, &aResult->mTargetId); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ScrollSnapInfo::ScrollSnapRange> { + typedef mozilla::layers::ScrollSnapInfo::ScrollSnapRange paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mStart); + WriteParam(aWriter, aParam.mEnd); + WriteParam(aWriter, aParam.mTargetId); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mStart) && + ReadParam(aReader, &aResult->mEnd) && + ReadParam(aReader, &aResult->mTargetId); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ScrollSnapInfo> { + typedef mozilla::layers::ScrollSnapInfo paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mScrollSnapStrictnessX); + WriteParam(aWriter, aParam.mScrollSnapStrictnessY); + WriteParam(aWriter, aParam.mSnapTargets); + WriteParam(aWriter, aParam.mXRangeWiderThanSnapport); + WriteParam(aWriter, aParam.mYRangeWiderThanSnapport); + WriteParam(aWriter, aParam.mSnapportSize); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mScrollSnapStrictnessX) && + ReadParam(aReader, &aResult->mScrollSnapStrictnessY) && + ReadParam(aReader, &aResult->mSnapTargets) && + ReadParam(aReader, &aResult->mXRangeWiderThanSnapport) && + ReadParam(aReader, &aResult->mYRangeWiderThanSnapport) && + ReadParam(aReader, &aResult->mSnapportSize)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::OverscrollBehaviorInfo> { + // Not using PlainOldDataSerializer so we get enum validation + // for the members. + + typedef mozilla::layers::OverscrollBehaviorInfo paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mBehaviorX); + WriteParam(aWriter, aParam.mBehaviorY); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mBehaviorX) && + ReadParam(aReader, &aResult->mBehaviorY)); + } +}; + +template <typename T> +struct ParamTraits<mozilla::ScrollGeneration<T>> + : PlainOldDataSerializer<mozilla::ScrollGeneration<T>> {}; + +template <> +struct ParamTraits<mozilla::ScrollUpdateType> + : public ContiguousEnumSerializerInclusive< + mozilla::ScrollUpdateType, mozilla::ScrollUpdateType::Absolute, + mozilla::ScrollUpdateType::PureRelative> {}; + +template <> +struct ParamTraits<mozilla::ScrollMode> + : public ContiguousEnumSerializerInclusive<mozilla::ScrollMode, + mozilla::ScrollMode::Instant, + mozilla::ScrollMode::Normal> {}; + +template <> +struct ParamTraits<mozilla::ScrollOrigin> + : public ContiguousEnumSerializerInclusive< + mozilla::ScrollOrigin, mozilla::ScrollOrigin::None, + mozilla::ScrollOrigin::Scrollbars> {}; + +template <> +struct ParamTraits<mozilla::ScrollTriggeredByScript> + : public ContiguousEnumSerializerInclusive< + mozilla::ScrollTriggeredByScript, + mozilla::ScrollTriggeredByScript::No, + mozilla::ScrollTriggeredByScript::Yes> {}; + +template <> +struct ParamTraits<mozilla::ScrollPositionUpdate> { + typedef mozilla::ScrollPositionUpdate paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mScrollGeneration); + WriteParam(aWriter, aParam.mType); + WriteParam(aWriter, aParam.mScrollMode); + WriteParam(aWriter, aParam.mScrollOrigin); + WriteParam(aWriter, aParam.mDestination); + WriteParam(aWriter, aParam.mSource); + WriteParam(aWriter, aParam.mDelta); + WriteParam(aWriter, aParam.mTriggeredByScript); + WriteParam(aWriter, aParam.mSnapTargetIds); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mScrollGeneration) && + ReadParam(aReader, &aResult->mType) && + ReadParam(aReader, &aResult->mScrollMode) && + ReadParam(aReader, &aResult->mScrollOrigin) && + ReadParam(aReader, &aResult->mDestination) && + ReadParam(aReader, &aResult->mSource) && + ReadParam(aReader, &aResult->mDelta) && + ReadParam(aReader, &aResult->mTriggeredByScript) && + ReadParam(aReader, &aResult->mSnapTargetIds); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ScrollMetadata> + : BitfieldHelper<mozilla::layers::ScrollMetadata> { + typedef mozilla::layers::ScrollMetadata paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mMetrics); + WriteParam(aWriter, aParam.mSnapInfo); + WriteParam(aWriter, aParam.mScrollParentId); + WriteParam(aWriter, aParam.GetContentDescription()); + WriteParam(aWriter, aParam.mLineScrollAmount); + WriteParam(aWriter, aParam.mPageScrollAmount); + WriteParam(aWriter, aParam.mHasScrollgrab); + WriteParam(aWriter, aParam.mIsLayersIdRoot); + WriteParam(aWriter, aParam.mIsAutoDirRootContentRTL); + WriteParam(aWriter, aParam.mForceDisableApz); + WriteParam(aWriter, aParam.mResolutionUpdated); + WriteParam(aWriter, aParam.mIsRDMTouchSimulationActive); + WriteParam(aWriter, aParam.mDidContentGetPainted); + WriteParam(aWriter, aParam.mForceMousewheelAutodir); + WriteParam(aWriter, aParam.mForceMousewheelAutodirHonourRoot); + WriteParam(aWriter, aParam.mIsPaginatedPresentation); + WriteParam(aWriter, aParam.mDisregardedDirection); + WriteParam(aWriter, aParam.mOverscrollBehavior); + WriteParam(aWriter, aParam.mScrollUpdates); + } + + static bool ReadContentDescription(MessageReader* aReader, + paramType* aResult) { + nsCString str; + if (!ReadParam(aReader, &str)) { + return false; + } + aResult->SetContentDescription(str); + return true; + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mMetrics) && + ReadParam(aReader, &aResult->mSnapInfo) && + ReadParam(aReader, &aResult->mScrollParentId) && + ReadContentDescription(aReader, aResult) && + ReadParam(aReader, &aResult->mLineScrollAmount) && + ReadParam(aReader, &aResult->mPageScrollAmount) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetHasScrollgrab) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetIsLayersIdRoot) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetIsAutoDirRootContentRTL) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetForceDisableApz) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetResolutionUpdated) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetIsRDMTouchSimulationActive)) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetDidContentGetPainted) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetForceMousewheelAutodir) && + ReadBoolForBitfield( + aReader, aResult, + ¶mType::SetForceMousewheelAutodirHonourRoot) && + ReadBoolForBitfield(aReader, aResult, + ¶mType::SetIsPaginatedPresentation) && + ReadParam(aReader, &aResult->mDisregardedDirection) && + ReadParam(aReader, &aResult->mOverscrollBehavior) && + ReadParam(aReader, &aResult->mScrollUpdates); + } +}; + +template <> +struct ParamTraits<mozilla::layers::TextureFactoryIdentifier> { + typedef mozilla::layers::TextureFactoryIdentifier paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mParentBackend); + WriteParam(aWriter, aParam.mWebRenderBackend); + WriteParam(aWriter, aParam.mWebRenderCompositor); + WriteParam(aWriter, aParam.mParentProcessType); + WriteParam(aWriter, aParam.mMaxTextureSize); + WriteParam(aWriter, aParam.mCompositorUseANGLE); + WriteParam(aWriter, aParam.mCompositorUseDComp); + WriteParam(aWriter, aParam.mUseCompositorWnd); + WriteParam(aWriter, aParam.mSupportsTextureBlitting); + WriteParam(aWriter, aParam.mSupportsPartialUploads); + WriteParam(aWriter, aParam.mSupportsComponentAlpha); + WriteParam(aWriter, aParam.mSupportsD3D11NV12); + WriteParam(aWriter, aParam.mSyncHandle); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + bool result = ReadParam(aReader, &aResult->mParentBackend) && + ReadParam(aReader, &aResult->mWebRenderBackend) && + ReadParam(aReader, &aResult->mWebRenderCompositor) && + ReadParam(aReader, &aResult->mParentProcessType) && + ReadParam(aReader, &aResult->mMaxTextureSize) && + ReadParam(aReader, &aResult->mCompositorUseANGLE) && + ReadParam(aReader, &aResult->mCompositorUseDComp) && + ReadParam(aReader, &aResult->mUseCompositorWnd) && + ReadParam(aReader, &aResult->mSupportsTextureBlitting) && + ReadParam(aReader, &aResult->mSupportsPartialUploads) && + ReadParam(aReader, &aResult->mSupportsComponentAlpha) && + ReadParam(aReader, &aResult->mSupportsD3D11NV12) && + ReadParam(aReader, &aResult->mSyncHandle); + return result; + } +}; + +template <> +struct ParamTraits<mozilla::layers::TextureInfo> { + typedef mozilla::layers::TextureInfo paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mCompositableType); + WriteParam(aWriter, aParam.mTextureFlags); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mCompositableType) && + ReadParam(aReader, &aResult->mTextureFlags); + } +}; + +template <> +struct ParamTraits<mozilla::layers::CompositableType> + : public ContiguousEnumSerializer< + mozilla::layers::CompositableType, + mozilla::layers::CompositableType::UNKNOWN, + mozilla::layers::CompositableType::COUNT> {}; + +template <> +struct ParamTraits<mozilla::layers::ScrollableLayerGuid> { + typedef mozilla::layers::ScrollableLayerGuid paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mLayersId); + WriteParam(aWriter, aParam.mPresShellId); + WriteParam(aWriter, aParam.mScrollId); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mLayersId) && + ReadParam(aReader, &aResult->mPresShellId) && + ReadParam(aReader, &aResult->mScrollId)); + } +}; + +template <> +struct ParamTraits<nsEventStatus> + : public ContiguousEnumSerializer<nsEventStatus, nsEventStatus_eIgnore, + nsEventStatus_eSentinel> {}; + +template <> +struct ParamTraits<mozilla::layers::APZHandledPlace> + : public ContiguousEnumSerializer< + mozilla::layers::APZHandledPlace, + mozilla::layers::APZHandledPlace::Unhandled, + mozilla::layers::APZHandledPlace::Last> {}; + +template <> +struct ParamTraits<mozilla::layers::ScrollDirections> { + typedef mozilla::layers::ScrollDirections paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.serialize()); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + uint8_t value; + if (!ReadParam(aReader, &value)) { + return false; + } + aResult->deserialize(value); + return true; + } +}; + +template <> +struct ParamTraits<mozilla::layers::APZHandledResult> { + typedef mozilla::layers::APZHandledResult paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mPlace); + WriteParam(aWriter, aParam.mScrollableDirections); + WriteParam(aWriter, aParam.mOverscrollDirections); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mPlace) && + ReadParam(aReader, &aResult->mScrollableDirections) && + ReadParam(aReader, &aResult->mOverscrollDirections)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::APZEventResult> { + typedef mozilla::layers::APZEventResult paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.GetStatus()); + WriteParam(aWriter, aParam.GetHandledResult()); + WriteParam(aWriter, aParam.mTargetGuid); + WriteParam(aWriter, aParam.mInputBlockId); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + nsEventStatus status; + if (!ReadParam(aReader, &status)) { + return false; + } + aResult->UpdateStatus(status); + + mozilla::Maybe<mozilla::layers::APZHandledResult> handledResult; + if (!ReadParam(aReader, &handledResult)) { + return false; + } + aResult->UpdateHandledResult(handledResult); + + return (ReadParam(aReader, &aResult->mTargetGuid) && + ReadParam(aReader, &aResult->mInputBlockId)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ZoomConstraints> { + typedef mozilla::layers::ZoomConstraints paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mAllowZoom); + WriteParam(aWriter, aParam.mAllowDoubleTapZoom); + WriteParam(aWriter, aParam.mMinZoom); + WriteParam(aWriter, aParam.mMaxZoom); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mAllowZoom) && + ReadParam(aReader, &aResult->mAllowDoubleTapZoom) && + ReadParam(aReader, &aResult->mMinZoom) && + ReadParam(aReader, &aResult->mMaxZoom)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::FocusTarget::ScrollTargets> { + typedef mozilla::layers::FocusTarget::ScrollTargets paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mHorizontal); + WriteParam(aWriter, aParam.mVertical); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mHorizontal) && + ReadParam(aReader, &aResult->mVertical); + } +}; + +template <> +struct ParamTraits<mozilla::layers::FocusTarget::NoFocusTarget> + : public EmptyStructSerializer< + mozilla::layers::FocusTarget::NoFocusTarget> {}; + +template <> +struct ParamTraits<mozilla::layers::FocusTarget> { + typedef mozilla::layers::FocusTarget paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mSequenceNumber); + WriteParam(aWriter, aParam.mFocusHasKeyEventListeners); + WriteParam(aWriter, aParam.mData); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + if (!ReadParam(aReader, &aResult->mSequenceNumber) || + !ReadParam(aReader, &aResult->mFocusHasKeyEventListeners) || + !ReadParam(aReader, &aResult->mData)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits< + mozilla::layers::KeyboardScrollAction::KeyboardScrollActionType> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::KeyboardScrollAction::KeyboardScrollActionType, + mozilla::layers::KeyboardScrollAction::KeyboardScrollActionType:: + eScrollCharacter, + mozilla::layers::KeyboardScrollAction:: + sHighestKeyboardScrollActionType> {}; + +template <> +struct ParamTraits<mozilla::layers::KeyboardScrollAction> { + typedef mozilla::layers::KeyboardScrollAction paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mType); + WriteParam(aWriter, aParam.mForward); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mType) && + ReadParam(aReader, &aResult->mForward); + } +}; + +template <> +struct ParamTraits<mozilla::layers::KeyboardShortcut> { + typedef mozilla::layers::KeyboardShortcut paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mAction); + WriteParam(aWriter, aParam.mKeyCode); + WriteParam(aWriter, aParam.mCharCode); + WriteParam(aWriter, aParam.mModifiers); + WriteParam(aWriter, aParam.mModifiersMask); + WriteParam(aWriter, aParam.mEventType); + WriteParam(aWriter, aParam.mDispatchToContent); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mAction) && + ReadParam(aReader, &aResult->mKeyCode) && + ReadParam(aReader, &aResult->mCharCode) && + ReadParam(aReader, &aResult->mModifiers) && + ReadParam(aReader, &aResult->mModifiersMask) && + ReadParam(aReader, &aResult->mEventType) && + ReadParam(aReader, &aResult->mDispatchToContent); + } +}; + +template <> +struct ParamTraits<mozilla::layers::KeyboardMap> { + typedef mozilla::layers::KeyboardMap paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.Shortcuts()); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + nsTArray<mozilla::layers::KeyboardShortcut> shortcuts; + if (!ReadParam(aReader, &shortcuts)) { + return false; + } + *aResult = mozilla::layers::KeyboardMap(std::move(shortcuts)); + return true; + } +}; + +template <> +struct ParamTraits<mozilla::layers::GeckoContentController_TapType> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::GeckoContentController_TapType, + mozilla::layers::GeckoContentController_TapType::eSingleTap, + mozilla::layers::kHighestGeckoContentController_TapType> {}; + +template <> +struct ParamTraits<mozilla::layers::GeckoContentController_APZStateChange> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::GeckoContentController_APZStateChange, + mozilla::layers::GeckoContentController_APZStateChange:: + eTransformBegin, + mozilla::layers::kHighestGeckoContentController_APZStateChange> {}; + +template <> +struct ParamTraits<mozilla::layers::EventRegionsOverride> + : public BitFlagsEnumSerializer< + mozilla::layers::EventRegionsOverride, + mozilla::layers::EventRegionsOverride::ALL_BITS> {}; + +template <> +struct ParamTraits<mozilla::layers::AsyncDragMetrics> { + typedef mozilla::layers::AsyncDragMetrics paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mViewId); + WriteParam(aWriter, aParam.mPresShellId); + WriteParam(aWriter, aParam.mDragStartSequenceNumber); + WriteParam(aWriter, aParam.mScrollbarDragOffset); + WriteParam(aWriter, aParam.mDirection); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mViewId) && + ReadParam(aReader, &aResult->mPresShellId) && + ReadParam(aReader, &aResult->mDragStartSequenceNumber) && + ReadParam(aReader, &aResult->mScrollbarDragOffset) && + ReadParam(aReader, &aResult->mDirection)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::BrowserGestureResponse> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::BrowserGestureResponse, + mozilla::layers::BrowserGestureResponse::NotConsumed, + mozilla::layers::BrowserGestureResponse::Consumed> {}; + +template <> +struct ParamTraits<mozilla::layers::CompositorOptions> { + typedef mozilla::layers::CompositorOptions paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mUseAPZ); + WriteParam(aWriter, aParam.mUseSoftwareWebRender); + WriteParam(aWriter, aParam.mAllowSoftwareWebRenderD3D11); + WriteParam(aWriter, aParam.mAllowSoftwareWebRenderOGL); + WriteParam(aWriter, aParam.mInitiallyPaused); + WriteParam(aWriter, aParam.mNeedFastSnaphot); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mUseAPZ) && + ReadParam(aReader, &aResult->mUseSoftwareWebRender) && + ReadParam(aReader, &aResult->mAllowSoftwareWebRenderD3D11) && + ReadParam(aReader, &aResult->mAllowSoftwareWebRenderOGL) && + ReadParam(aReader, &aResult->mInitiallyPaused) && + ReadParam(aReader, &aResult->mNeedFastSnaphot); + } +}; + +template <> +struct ParamTraits<mozilla::layers::OverlaySupportType> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::OverlaySupportType, + mozilla::layers::OverlaySupportType::None, + mozilla::layers::OverlaySupportType::MAX> {}; + +template <> +struct ParamTraits<mozilla::layers::OverlayInfo> { + typedef mozilla::layers::OverlayInfo paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mSupportsOverlays); + WriteParam(aWriter, aParam.mNv12Overlay); + WriteParam(aWriter, aParam.mYuy2Overlay); + WriteParam(aWriter, aParam.mBgra8Overlay); + WriteParam(aWriter, aParam.mRgb10a2Overlay); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mSupportsOverlays) && + ReadParam(aReader, &aResult->mNv12Overlay) && + ReadParam(aReader, &aResult->mYuy2Overlay) && + ReadParam(aReader, &aResult->mBgra8Overlay) && + ReadParam(aReader, &aResult->mRgb10a2Overlay); + } +}; + +template <> +struct ParamTraits<mozilla::layers::SwapChainInfo> { + typedef mozilla::layers::SwapChainInfo paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mTearingSupported); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mTearingSupported); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ScrollbarLayerType> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::ScrollbarLayerType, + mozilla::layers::ScrollbarLayerType::None, + mozilla::layers::kHighestScrollbarLayerType> {}; + +template <> +struct ParamTraits<mozilla::layers::ScrollbarData> { + typedef mozilla::layers::ScrollbarData paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mDirection); + WriteParam(aWriter, aParam.mScrollbarLayerType); + WriteParam(aWriter, aParam.mThumbRatio); + WriteParam(aWriter, aParam.mThumbStart); + WriteParam(aWriter, aParam.mThumbLength); + WriteParam(aWriter, aParam.mThumbMinLength); + WriteParam(aWriter, aParam.mThumbIsAsyncDraggable); + WriteParam(aWriter, aParam.mScrollTrackStart); + WriteParam(aWriter, aParam.mScrollTrackLength); + WriteParam(aWriter, aParam.mTargetViewId); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mDirection) && + ReadParam(aReader, &aResult->mScrollbarLayerType) && + ReadParam(aReader, &aResult->mThumbRatio) && + ReadParam(aReader, &aResult->mThumbStart) && + ReadParam(aReader, &aResult->mThumbLength) && + ReadParam(aReader, &aResult->mThumbMinLength) && + ReadParam(aReader, &aResult->mThumbIsAsyncDraggable) && + ReadParam(aReader, &aResult->mScrollTrackStart) && + ReadParam(aReader, &aResult->mScrollTrackLength) && + ReadParam(aReader, &aResult->mTargetViewId); + } +}; + +template <> +struct ParamTraits<mozilla::layers::CompositionPayloadType> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::CompositionPayloadType, + mozilla::layers::CompositionPayloadType::eKeyPress, + mozilla::layers::kHighestCompositionPayloadType> {}; + +template <> +struct ParamTraits<mozilla::layers::CompositionPayload> { + typedef mozilla::layers::CompositionPayload paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mType); + WriteParam(aWriter, aParam.mTimeStamp); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mType) && + ReadParam(aReader, &aResult->mTimeStamp); + } +}; + +template <> +struct ParamTraits<mozilla::RayReferenceData> { + typedef mozilla::RayReferenceData paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mInitialPosition); + WriteParam(aWriter, aParam.mContainingBlockRect); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mInitialPosition) && + ReadParam(aReader, &aResult->mContainingBlockRect)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::CantZoomOutBehavior> + : public ContiguousEnumSerializerInclusive< + mozilla::layers::CantZoomOutBehavior, + mozilla::layers::CantZoomOutBehavior::Nothing, + mozilla::layers::CantZoomOutBehavior::ZoomIn> {}; + +template <> +struct ParamTraits<mozilla::layers::ZoomTarget> { + typedef mozilla::layers::ZoomTarget paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.targetRect); + WriteParam(aWriter, aParam.cantZoomOutBehavior); + WriteParam(aWriter, aParam.elementBoundingRect); + WriteParam(aWriter, aParam.documentRelativePointerPosition); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->targetRect) && + ReadParam(aReader, &aResult->cantZoomOutBehavior) && + ReadParam(aReader, &aResult->elementBoundingRect) && + ReadParam(aReader, &aResult->documentRelativePointerPosition)); + } +}; + +#define IMPL_PARAMTRAITS_BY_SERDE(type_) \ + template <> \ + struct ParamTraits<mozilla::type_> { \ + typedef mozilla::type_ paramType; \ + static void Write(MessageWriter* aWriter, const paramType& aParam) { \ + mozilla::ipc::ByteBuf v; \ + mozilla::DebugOnly<bool> rv = Servo_##type_##_Serialize(&aParam, &v); \ + MOZ_ASSERT(rv, "Serialize ##type_## failed"); \ + WriteParam(aWriter, std::move(v)); \ + } \ + static ReadResult<paramType> Read(MessageReader* aReader) { \ + mozilla::ipc::ByteBuf in; \ + ReadResult<paramType> result; \ + if (!ReadParam(aReader, &in) || !in.mData) { \ + return result; \ + } \ + /* TODO: Should be able to initialize `result` in-place instead */ \ + mozilla::AlignedStorage2<paramType> value; \ + if (!Servo_##type_##_Deserialize(&in, value.addr())) { \ + return result; \ + } \ + result = std::move(*value.addr()); \ + value.addr()->~paramType(); \ + return result; \ + } \ + }; + +IMPL_PARAMTRAITS_BY_SERDE(LengthPercentage) +IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetPath) +IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetRotate) +IMPL_PARAMTRAITS_BY_SERDE(StylePositionOrAuto) +IMPL_PARAMTRAITS_BY_SERDE(StyleRotate) +IMPL_PARAMTRAITS_BY_SERDE(StyleScale) +IMPL_PARAMTRAITS_BY_SERDE(StyleTranslate) +IMPL_PARAMTRAITS_BY_SERDE(StyleTransform) +IMPL_PARAMTRAITS_BY_SERDE(StyleComputedTimingFunction) + +} /* namespace IPC */ + +#endif /* mozilla_layers_LayersMessageUtils */ diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh new file mode 100644 index 0000000000..94abbcdde1 --- /dev/null +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -0,0 +1,362 @@ +/* -*- 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 LayersSurfaces; +include protocol PCompositorBridge; +include protocol PTexture; + +include "gfxipc/ShadowLayerUtils.h"; +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; + +using mozilla::gfx::Glyph from "mozilla/gfx/2D.h"; +using mozilla::gfx::SamplingFilter from "mozilla/gfx/2D.h"; +using struct mozilla::gfx::DeviceColor from "mozilla/gfx/2D.h"; +using mozilla::gfx::Point from "mozilla/gfx/Point.h"; +using mozilla::gfx::Point3D from "mozilla/gfx/Point.h"; +using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h"; +using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h"; +using mozilla::SideBits from "mozilla/gfx/Types.h"; +using nscolor from "nsColor.h"; +using nscoord from "nsCoord.h"; +using struct nsRect from "nsRect.h"; +using struct nsPoint from "nsPoint.h"; +using mozilla::TimeDuration from "mozilla/TimeStamp.h"; +using class mozilla::TimeStamp from "mozilla/TimeStamp.h"; +using mozilla::ScreenRotation from "mozilla/WidgetUtils.h"; +using nsCSSPropertyID from "nsCSSPropertyID.h"; +using mozilla::hal::ScreenOrientation from "mozilla/HalIPCUtils.h"; +using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h"; +using mozilla::CSSPoint from "Units.h"; +using mozilla::CSSRect from "Units.h"; +using mozilla::gfx::IntSize from "mozilla/gfx/2D.h"; +using mozilla::LayerMargin from "Units.h"; +using mozilla::LayerPoint from "Units.h"; +using mozilla::LayerCoord from "Units.h"; +using mozilla::LayerSize from "Units.h"; +using mozilla::LayerRect from "Units.h"; +using mozilla::LayerIntSize from "Units.h"; +using mozilla::LayerIntRect from "Units.h"; +using mozilla::LayerIntRegion from "Units.h"; +using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; +using mozilla::ParentLayerIntRect from "Units.h"; +using mozilla::ParentLayerRect from "Units.h"; +using mozilla::LayoutDeviceIntRect from "Units.h"; +using mozilla::LayoutDevicePoint from "Units.h"; +using mozilla::LayoutDeviceRect from "Units.h"; +using mozilla::layers::ScaleMode from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h"; +using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h"; +using mozilla::layers::ScrollDirection from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::CompositionPayload from "mozilla/layers/LayersTypes.h"; +[MoveOnly] using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h"; +using struct mozilla::void_t from "mozilla/ipc/IPCCore.h"; +using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::RemoteTextureId from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::RemoteTextureOwnerId from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h"; +using mozilla::LengthPercentage from "mozilla/ServoStyleConsts.h"; +using mozilla::RayReferenceData from "mozilla/MotionPathUtils.h"; +using mozilla::StyleOffsetPath from "mozilla/ServoStyleConsts.h"; +using mozilla::StyleOffsetRotate from "mozilla/ServoStyleConsts.h"; +using mozilla::StylePositionOrAuto from "mozilla/ServoStyleConsts.h"; +using mozilla::StyleRotate from "mozilla/ServoStyleConsts.h"; +using mozilla::StyleScale from "mozilla/ServoStyleConsts.h"; +using mozilla::StyleTranslate from "mozilla/ServoStyleConsts.h"; +using mozilla::StyleTransform from "mozilla/ServoStyleConsts.h"; +using mozilla::StyleComputedTimingFunction from "mozilla/ServoStyleConsts.h"; + +namespace mozilla { +namespace layers { + +struct TargetConfig { + IntRect naturalBounds; + ScreenRotation rotation; + ScreenOrientation orientation; + nsIntRegion clearRegion; +}; + +struct OpAttachCompositable { + LayerHandle layer; + CompositableHandle compositable; +}; + +struct OpAttachAsyncCompositable { + LayerHandle layer; + CompositableHandle compositable; +}; +struct LayerColor { DeviceColor value; }; + +[Comparable] union Animatable { + null_t; + float; + nscolor; + StyleRotate; + StyleScale; + StyleTranslate; + StyleTransform; + StyleOffsetPath; + LengthPercentage; + StyleOffsetRotate; + StylePositionOrAuto; +}; + +struct AnimationSegment { + Animatable startState; + Animatable endState; + float startPortion; + float endPortion; + uint8_t startComposite; + uint8_t endComposite; + StyleComputedTimingFunction? sampleFn; +}; + +[Comparable] struct MotionPathData { + // the transform-origin property for motion in css pixels + CSSPoint origin; + // the adjustment for the anchor point of motion path. + CSSPoint anchorAdjustment; + RayReferenceData rayReferenceData; +}; + +[Comparable] struct PartialPrerenderData { + LayoutDeviceRect rect; + SideBits overflowedSides; + // the scroll id of the nearest scrollable frame of this partial prerender + // data. + ViewID scrollId; + // The clip rectangle of the nearest scrollable frame. + // NOTE: This should be used only for fallback cases where APZ is not enabled. + ParentLayerRect clipRect; + // a transform from the coordinate space of the animated element to a + // coordinate space where the `clipRect` can be applied. + Matrix4x4 transformInClip; // Used only for WebRender. + // the position relative to the reference frame of the animated transform + // element in the element coordinate space. + LayoutDevicePoint position; // Used only for WebRender. +}; + +// Transforms need extra information to correctly convert the list of transform +// functions to a Matrix4x4 that can be applied directly to the layer. +[Comparable] struct TransformData { + // the origin of the frame being transformed in app units + nsPoint origin; + // the transform-origin property for the transform in device pixels + Point3D transformOrigin; + nsRect bounds; + int32_t appUnitsPerDevPixel; + MotionPathData? motionPathData; + PartialPrerenderData? partialPrerenderData; +}; + +// The scroll timeline information. +struct ScrollTimelineOptions { + // The source of the scroll-timeline. + ViewID source; + // The physical direction. + ScrollDirection axis; +}; + +struct Animation { + // The zero time of this Animation's timeline. May be null if isNotPlaying is + // true. + TimeStamp originTime; + // The start time is relative to the originTime. This allows us to represent + // start times in the distant past that cannot be expressed using a TimeStamp. + TimeDuration? startTime; + TimeDuration delay; + TimeDuration endDelay; + // The value of the animation's current time at the moment it was sent to the + // compositor. This value will be used for below cases: + // 1) Animations that are play-pending. Initially these animations will have a + // null |startTime|. Once the animation is ready to start (i.e. painting + // has finished), we calculate an appropriate value of |startTime| such + // that playback begins from |holdTime|. + // 2) Not playing animations (e.g. paused and finished animations). In this + // case the |holdTime| represents the current time the animation will + // maintain. + TimeDuration holdTime; + TimeDuration duration; + // For each frame, the interpolation point is computed based on the + // startTime, the direction, the duration, and the current time. + // The segments must uniquely cover the portion from 0.0 to 1.0 + AnimationSegment[] segments; + // Number of times to repeat the animation, including positive infinity. + // Values <= 0 mean the animation will not play (although events are still + // dispatched on the main thread). + float iterations; + float iterationStart; + // This uses the NS_STYLE_ANIMATION_DIRECTION_* constants. + uint8_t direction; + // This uses dom::FillMode. + uint8_t fillMode; + nsCSSPropertyID property; + float playbackRate; + // When performing an asynchronous update to the playbackRate, |playbackRate| + // above is the updated playbackRate while |previousPlaybackRate| is the + // existing playbackRate. This is used by AnimationInfo to update the + // startTime based on the 'readyTime' (timestamp at the end of painting) + // and is not used beyond that point. + // + // It is set to numeric_limits<float>::quiet_NaN() when no asynchronous update + // to the playbackRate is being performed. + float previousPlaybackRate; + // This is used in the transformed progress calculation. + StyleComputedTimingFunction? easingFunction; + uint8_t iterationComposite; + // True if the animation has a fixed current time (e.g. paused and + // forward-filling animations). + bool isNotPlaying; + // True if this is not an animating property. For some transform-like + // properties, we just send their baseStyles for merging with other animating + // properties. In this case, we don't have animation information on this + // property, and so don't need to do interpolation. + bool isNotAnimating; + // The base style that animations should composite with. This is only set for + // 1. scroll animations with positive delays, or + // 2. animations with a composite mode of additive or accumulate, and only for + // the first animation in the set (i.e. the animation that is lowest in the + // stack). + // In all other cases the value is null_t. + Animatable baseStyle; + // An optional data specific for transform like properies. + TransformData? transformData; + // If this is present, the animation is driven by a ScrollTimeline, and + // this structure contains information about that timeline. + ScrollTimelineOptions? scrollTimelineOptions; +}; + +struct CompositorAnimations { + Animation[] animations; + // This id is used to map the layer animations between content + // and compositor side + uint64_t id; +}; + +struct ShmemSection { + Shmem shmem; + uint32_t offset; + uint32_t size; +}; + +struct CrossProcessSemaphoreDescriptor { + CrossProcessSemaphoreHandle sem; +}; + +union ReadLockDescriptor { + ShmemSection; + CrossProcessSemaphoreDescriptor; + uintptr_t; + null_t; +}; + +/** + * Tells the CompositableHost to remove the corresponding TextureHost + */ +struct OpRemoveTexture { + PTexture texture; +}; + +struct TimedTexture { + PTexture texture; + TimeStamp timeStamp; + IntRect picture; + uint32_t frameID; + uint32_t producerID; + bool readLocked; +}; + +/** + * Tells the compositor-side which textures to use (for example, as front buffer + * if there are several textures for double buffering). + * This provides a list of textures with timestamps, ordered by timestamp. + * The newest texture whose timestamp is <= the current time is rendered + * (where null is considered less than every other timestamp). If there is no + * such texture, the first texture is rendered. + * The first timestamp value can be null, but the others must not be. + * The list must not be empty. + */ +struct OpUseTexture { + TimedTexture[] textures; +}; + +struct OpUseRemoteTexture { + RemoteTextureId textureId; + RemoteTextureOwnerId ownerId; + IntSize size; + TextureFlags textureFlags; +}; + +struct OpEnableRemoteTexturePushCallback { + RemoteTextureOwnerId ownerId; + IntSize size; + TextureFlags textureFlags; +}; + +struct OpNotifyNotUsed { + uint64_t TextureId; + uint64_t fwdTransactionId; +}; + +union CompositableOperationDetail { + OpRemoveTexture; + + OpUseTexture; + + OpUseRemoteTexture; + + OpEnableRemoteTexturePushCallback; +}; + +struct CompositableOperation { + CompositableHandle compositable; + CompositableOperationDetail detail; +}; + +// Operations related to destroying resources, always handled after the other +// operations for safety. +union OpDestroy { + PTexture; + CompositableHandle; +}; + +// Replies to operations + +struct OpContentBufferSwap { + CompositableHandle compositable; + nsIntRegion frontUpdatedRegion; +}; + +/** + * An ImageCompositeNotification is sent the first time a particular + * image is composited by an ImageHost. + */ +struct ImageCompositeNotification { + CompositableHandle compositable; + TimeStamp imageTimeStamp; + TimeStamp firstCompositeTimeStamp; + uint32_t frameID; + uint32_t producerID; +}; + +union AsyncParentMessageData { + OpNotifyNotUsed; +}; + +union OMTAValue { + null_t; + nscolor; + float; + Matrix4x4; +}; + +} // namespace +} // namespace diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh new file mode 100644 index 0000000000..c7f432ccb6 --- /dev/null +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -0,0 +1,205 @@ +/* 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 "gfxipc/ShadowLayerUtils.h"; +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; + +using gfxPoint from "gfxPoint.h"; +using nsIntRegion from "nsRegion.h"; +using mozilla::StereoMode from "ImageTypes.h"; +using struct mozilla::null_t from "mozilla/ipc/IPCCore.h"; +using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h"; +using mozilla::gfx::YUVColorSpace from "mozilla/gfx/Types.h"; +using mozilla::gfx::ChromaSubsampling from "mozilla/gfx/Types.h"; +using mozilla::gfx::ColorDepth from "mozilla/gfx/Types.h"; +using mozilla::gfx::ColorRange from "mozilla/gfx/Types.h"; +using mozilla::gfx::ColorSpace2 from "mozilla/gfx/Types.h"; +using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; +using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h"; +using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; +using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h"; +[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; +using gfxImageFormat from "gfxTypes.h"; +using mozilla::layers::MaybeVideoBridgeSource from "mozilla/layers/VideoBridgeUtils.h"; +using mozilla::layers::RemoteTextureId from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::RemoteTextureOwnerId from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::GpuProcessTextureId from "mozilla/layers/LayersTypes.h"; + +namespace mozilla { +namespace layers { + +[Comparable] struct SurfaceDescriptorD3D10 { + WindowsHandle handle; + GpuProcessTextureId? gpuProcessTextureId; + uint32_t arrayIndex; + SurfaceFormat format; + IntSize size; + ColorSpace2 colorSpace; + ColorRange colorRange; +}; + +[Comparable] struct SurfaceDescriptorDXGIYCbCr { + WindowsHandle handleY; + WindowsHandle handleCb; + WindowsHandle handleCr; + IntSize size; + IntSize sizeY; + IntSize sizeCbCr; + ColorDepth colorDepth; + YUVColorSpace yUVColorSpace; + ColorRange colorRange; +}; + +[Comparable] struct SurfaceDescriptorMacIOSurface { + uint32_t surfaceId; + bool isOpaque; + YUVColorSpace yUVColorSpace; +}; + +[Comparable] struct SurfaceDescriptorDMABuf { + uint32_t bufferType; + uint64_t[] modifier; + uint32_t flags; + FileDescriptor[] fds; + uint32_t[] width; + uint32_t[] height; + uint32_t[] widthAligned; + uint32_t[] heightAligned; + uint32_t[] format; + uint32_t[] strides; + uint32_t[] offsets; + YUVColorSpace yUVColorSpace; + ColorRange colorRange; + FileDescriptor[] fence; + uint32_t uid; + FileDescriptor[] refCount; +}; + +[Comparable] struct SurfaceTextureDescriptor { + uint64_t handle; + IntSize size; + SurfaceFormat format; + bool continuous; + Matrix4x4? transformOverride; +}; + +[Comparable] struct SurfaceDescriptorAndroidHardwareBuffer { + uint64_t bufferId; + IntSize size; + SurfaceFormat format; +}; + +[Comparable] struct EGLImageDescriptor { + uintptr_t image; // `EGLImage` is a `void*`. + uintptr_t fence; + IntSize size; + bool hasAlpha; +}; + +[Comparable] struct SurfaceDescriptorSharedGLTexture { + uint32_t texture; + uint32_t target; + uintptr_t fence; + IntSize size; + bool hasAlpha; +}; + +[Comparable] struct SurfaceDescriptorDcompSurface { + FileDescriptor handle; + IntSize size; + SurfaceFormat format; +}; + +[Comparable] union RemoteDecoderVideoSubDescriptor { + SurfaceDescriptorD3D10; + SurfaceDescriptorDXGIYCbCr; + SurfaceDescriptorDMABuf; + SurfaceDescriptorMacIOSurface; + SurfaceDescriptorDcompSurface; + null_t; +}; + +[Comparable] struct SurfaceDescriptorRemoteDecoder { + uint64_t handle; + RemoteDecoderVideoSubDescriptor subdesc; + MaybeVideoBridgeSource source; +}; + +[Comparable] union SurfaceDescriptorGPUVideo { + SurfaceDescriptorRemoteDecoder; +}; + +[Comparable] struct RGBDescriptor { + IntSize size; + SurfaceFormat format; +}; + +[Comparable] struct YCbCrDescriptor { + IntRect display; + IntSize ySize; + uint32_t yStride; + IntSize cbCrSize; + uint32_t cbCrStride; + uint32_t yOffset; + uint32_t cbOffset; + uint32_t crOffset; + StereoMode stereoMode; + ColorDepth colorDepth; + YUVColorSpace yUVColorSpace; + ColorRange colorRange; + ChromaSubsampling chromaSubsampling; +}; + +[Comparable] union BufferDescriptor { + RGBDescriptor; + YCbCrDescriptor; +}; + +[Comparable] union MemoryOrShmem { + uintptr_t; + Shmem; +}; + +[Comparable] struct SurfaceDescriptorBuffer { + BufferDescriptor desc; + MemoryOrShmem data; +}; + +[Comparable] struct SurfaceDescriptorShared +{ + IntSize size; + int32_t stride; + SurfaceFormat format; + Handle handle; +}; + +[Comparable] struct SurfaceDescriptorRecorded { + int64_t textureId; +}; + +[Comparable] struct SurfaceDescriptorRemoteTexture { + RemoteTextureId textureId; + RemoteTextureOwnerId ownerId; +}; + +[Comparable] union SurfaceDescriptor { + SurfaceDescriptorBuffer; + SurfaceDescriptorD3D10; + SurfaceDescriptorDXGIYCbCr; + SurfaceDescriptorDMABuf; + SurfaceTextureDescriptor; + SurfaceDescriptorAndroidHardwareBuffer; + EGLImageDescriptor; + SurfaceDescriptorMacIOSurface; + SurfaceDescriptorSharedGLTexture; + SurfaceDescriptorGPUVideo; + SurfaceDescriptorRecorded; + SurfaceDescriptorRemoteTexture; + SurfaceDescriptorDcompSurface; + null_t; +}; + +} // namespace +} // namespace diff --git a/gfx/layers/ipc/PAPZ.ipdl b/gfx/layers/ipc/PAPZ.ipdl new file mode 100644 index 0000000000..5525a25edd --- /dev/null +++ b/gfx/layers/ipc/PAPZ.ipdl @@ -0,0 +1,81 @@ +/* -*- 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 "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; +include "mozilla/layers/RemoteContentController.h"; + +include protocol PCompositorBridge; + +using mozilla::CSSRect from "Units.h"; +using struct mozilla::layers::RepaintRequest from "mozilla/layers/RepaintRequest.h"; +using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h"; +using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h"; +using mozilla::layers::GeckoContentController_APZStateChange from "mozilla/layers/GeckoContentControllerTypes.h"; +using mozilla::layers::ScrollDirection from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::MatrixMessage from "mozilla/layers/MatrixMessage.h"; +using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; +using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h"; +using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h"; +using class nsRegion from "nsRegion.h"; + +namespace mozilla { +namespace layers { + + +/** + * PAPZ is a protocol for remoting a GeckoContentController. PAPZ lives on the + * PCompositorBridge protocol which either connects to the compositor thread + * in the main process, or to the compositor thread in the gpu processs. + * + * PAPZParent lives in the compositor thread, while PAPZChild lives wherever the remoted + * GeckoContentController lives (generally the main thread of the main or content process). + * RemoteContentController implements PAPZParent, while APZChild implements PAPZChild. + * + * PAPZ is always used for ContentProcessController and only used for ChromeProcessController + * when there is a gpu process, otherwhise ChromeProcessController is used directly on the + * compositor thread. Only the methods that are used by the [Chrome,Content]ProcessController + * are implemented. If a new method is needed then PAPZ, APZChild, and RemoteContentController + * must be updated to handle it. + */ +[ManualDealloc, ParentImpl="RemoteContentController"] +sync protocol PAPZ +{ + manager PCompositorBridge; + +parent: + async __delete__(); + +child: + async LayerTransforms(MatrixMessage[] aTransforms); + + [Priority=control] + async RequestContentRepaint(RepaintRequest request); + + async UpdateOverscrollVelocity(ScrollableLayerGuid aGuid, float aX, float aY, bool aIsRootContent); + + async UpdateOverscrollOffset(ScrollableLayerGuid aGuid, float aX, float aY, bool aIsRootContent); + + async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent); + + async NotifyAPZStateChange(ScrollableLayerGuid aGuid, GeckoContentController_APZStateChange aChange, int aArg, uint64_t? aInputBlockId); + + [Priority=control] + async NotifyFlushComplete(); + + async NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId, ViewID aScrollId, ScrollDirection aDirection); + + async NotifyAsyncScrollbarDragRejected(ViewID aScrollId); + + async NotifyAsyncAutoscrollRejected(ViewID aScrollId); + +both: + async Destroy(); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PAPZCTreeManager.ipdl b/gfx/layers/ipc/PAPZCTreeManager.ipdl new file mode 100644 index 0000000000..9e814fa363 --- /dev/null +++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; +include "ipc/nsGUIEventIPC.h"; + +include protocol PCompositorBridge; + +using mozilla::CSSRect from "Units.h"; +using mozilla::LayoutDeviceCoord from "Units.h"; +using mozilla::LayoutDevicePoint from "Units.h"; +using mozilla::ScreenPoint from "Units.h"; +using mozilla::layers::ZoomTarget from "mozilla/layers/DoubleTapToZoom.h"; +using mozilla::layers::ZoomConstraints from "mozilla/layers/ZoomConstraints.h"; +using mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h"; +using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h"; +using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h"; +using mozilla::layers::GeckoContentController_TapType from "mozilla/layers/GeckoContentControllerTypes.h"; +using mozilla::layers::APZHandledResult from "mozilla/layers/APZInputBridge.h"; +using mozilla::layers::BrowserGestureResponse from "mozilla/layers/APZInputBridge.h"; +using class mozilla::layers::KeyboardMap from "mozilla/layers/KeyboardMap.h"; +using mozilla::wr::RenderRoot from "mozilla/webrender/WebRenderTypes.h"; + +using mozilla::Modifiers from "mozilla/EventForwards.h"; +using mozilla::PinchGestureInput::PinchGestureType from "InputData.h"; + +namespace mozilla { +namespace layers { + +/** + * PAPZCTreeManager is a protocol for remoting an IAPZCTreeManager. PAPZCTreeManager + * lives on the PCompositorBridge protocol which either connects to the compositor + * thread in the main process, or to the compositor thread in the gpu processs. + * + * PAPZCTreeManagerParent lives in the compositor thread, while PAPZCTreeManagerChild + * lives in the main thread of the main or the content process. APZCTreeManagerParent + * and APZCTreeManagerChild implement this protocol. + */ +[ManualDealloc] +protocol PAPZCTreeManager +{ +manager PCompositorBridge; + +parent: + + // These messages correspond to the methods + // on the IAPZCTreeManager interface + + async ZoomToRect(ScrollableLayerGuid aGuid, ZoomTarget aZoomTarget, uint32_t Flags); + + async ContentReceivedInputBlock(uint64_t aInputBlockId, bool PreventDefault); + + async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] Targets); + + async UpdateZoomConstraints(ScrollableLayerGuid aGuid, ZoomConstraints? aConstraints); + + async SetKeyboardMap(KeyboardMap aKeyboardMap); + + async SetDPI(float aDpiValue); + + async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aValues); + + async StartScrollbarDrag(ScrollableLayerGuid aGuid, AsyncDragMetrics aDragMetrics); + + async StartAutoscroll(ScrollableLayerGuid aGuid, ScreenPoint aAnchorLocation); + + async StopAutoscroll(ScrollableLayerGuid aGuid); + + async SetLongTapEnabled(bool aTapGestureEnabled); + + async SetBrowserGestureResponse(uint64_t aInputBlockId, + BrowserGestureResponse aResponse); + + async __delete__(); + +child: + + async HandleTap(GeckoContentController_TapType aType, LayoutDevicePoint point, Modifiers aModifiers, + ScrollableLayerGuid aGuid, uint64_t aInputBlockId); + + async NotifyPinchGesture(PinchGestureType aType, ScrollableLayerGuid aGuid, + LayoutDevicePoint aFocusPoint, LayoutDeviceCoord aSpanChange, + Modifiers aModifiers); + + async CancelAutoscroll(ViewID aScrollId); + + async NotifyScaleGestureComplete(ViewID aScrollId, float aScale); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/layers/ipc/PAPZInputBridge.ipdl b/gfx/layers/ipc/PAPZInputBridge.ipdl new file mode 100644 index 0000000000..20a569c1d2 --- /dev/null +++ b/gfx/layers/ipc/PAPZInputBridge.ipdl @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "ipc/nsGUIEventIPC.h"; + +using mozilla::LayoutDeviceIntPoint from "Units.h"; +using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h"; +using struct mozilla::layers::APZEventResult from "mozilla/layers/APZInputBridge.h"; +using struct mozilla::layers::APZHandledResult from "mozilla/layers/APZInputBridge.h"; + +using mozilla::EventMessage from "mozilla/EventForwards.h"; +using class mozilla::MultiTouchInput from "InputData.h"; +using class mozilla::MouseInput from "InputData.h"; +using class mozilla::PanGestureInput from "InputData.h"; +using class mozilla::PinchGestureInput from "InputData.h"; +using class mozilla::TapGestureInput from "InputData.h"; +using class mozilla::ScrollWheelInput from "InputData.h"; +using class mozilla::KeyboardInput from "InputData.h"; + +using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; + +namespace mozilla { +namespace layers { + +/** + * This protocol is used to send input events from the UI process to the + * GPU process for handling by APZ. There is one instance per top-level + * compositor, or in other words, one instance per concrete APZCTreeManager + * instance. The child side lives on the controller thread in the UI process, + * ie the main thread on most platforms, but the Android UI thread on Android. + * The parent side lives on the main thread in the GPU process. If there is no + * GPU process, then this protocol is not instantiated. + */ +sync protocol PAPZInputBridge +{ +parent: + // The following messages are used to + // implement the ReceiveInputEvent methods + + sync ReceiveMultiTouchInputEvent(MultiTouchInput aEvent, bool aWantsCallback) + returns(APZEventResult aOutResult, MultiTouchInput aOutEvent); + + sync ReceiveMouseInputEvent(MouseInput aEvent, bool aWantsCallback) + returns (APZEventResult aOutResult, + MouseInput aOutEvent); + + sync ReceivePanGestureInputEvent(PanGestureInput aEvent, bool aWantsCallback) + returns (APZEventResult aOutResult, + PanGestureInput aOutEvent); + + sync ReceivePinchGestureInputEvent(PinchGestureInput aEvent, + bool aWantsCallback) + returns (APZEventResult aOutResult, + PinchGestureInput aOutEvent); + + sync ReceiveTapGestureInputEvent(TapGestureInput aEvent, bool aWantsCallback) + returns (APZEventResult aOutResult, + TapGestureInput aOutEvent); + + sync ReceiveScrollWheelInputEvent(ScrollWheelInput aEvent, + bool aWantsCallback) + returns (APZEventResult aOutResult, + ScrollWheelInput aOutEvent); + + sync ReceiveKeyboardInputEvent(KeyboardInput aEvent, bool aWantsCallback) + returns (APZEventResult aOutResult, + KeyboardInput aOutEvent); + + async UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, + EventMessage aEventMessage, + ScrollableLayerGuid? aTargetGuid); + + sync ProcessUnhandledEvent(LayoutDeviceIntPoint aRefPoint) + returns (LayoutDeviceIntPoint aOutRefPoint, + ScrollableLayerGuid aOutTargetGuid, + uint64_t aOutFocusSequenceNumber, + LayersId aOutLayersId); + +child: + async CallInputBlockCallback(uint64_t aInputBlockId, + APZHandledResult aHandledResult); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/layers/ipc/PCanvas.ipdl b/gfx/layers/ipc/PCanvas.ipdl new file mode 100644 index 0000000000..6f068ac00c --- /dev/null +++ b/gfx/layers/ipc/PCanvas.ipdl @@ -0,0 +1,52 @@ +/* -*- 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 "mozilla/layers/LayersMessageUtils.h"; +include "mozilla/layers/CanvasTranslator.h"; + +[MoveOnly] using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h"; +using mozilla::layers::TextureType from "mozilla/layers/LayersTypes.h"; +[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; + +namespace mozilla { +namespace layers { + +/** + * PCanvas is the IPDL for recorded Canvas drawing. + */ +[NeedsOtherPid, ParentImpl="CanvasTranslator"] +async protocol PCanvas { +parent: + /** + * Initialize a CanvasTranslator for a particular TextureType, which + * translates events from a CanvasEventRingBuffer. aReadHandle is the shared + * memory handle for the ring buffer. aReaderSem and aWriterSem are handles + * for the semaphores to handle waiting on either side. + */ + async InitTranslator(TextureType aTextureType, Handle aReadHandle, + CrossProcessSemaphoreHandle aReaderSem, + CrossProcessSemaphoreHandle aWriterSem); + + /** + * Used to tell the CanvasTranslator to start translating again after it has + * stopped due to a timeout waiting for events. + */ + async ResumeTranslation(); + + child: + /** + * Notify that the canvas device used by the translator has changed. + */ + async NotifyDeviceChanged(); + + /** + * Deactivate remote canvas, which will cause fall back to software. + */ + async Deactivate(); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl new file mode 100644 index 0000000000..1340b7850f --- /dev/null +++ b/gfx/layers/ipc/PCompositorBridge.ipdl @@ -0,0 +1,214 @@ +/* -*- 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 LayersSurfaces; +include LayersMessages; +include PlatformWidgetTypes; +include PCompositorBridgeTypes; +include protocol PAPZ; +include protocol PAPZCTreeManager; +include protocol PBrowser; +include protocol PCanvas; +include protocol PCompositorManager; +include protocol PCompositorWidget; +include protocol PTexture; +include protocol PWebRenderBridge; +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; +include "mozilla/layers/WebRenderMessageUtils.h"; +include "mozilla/layers/CompositorBridgeParent.h"; + +using struct mozilla::null_t from "mozilla/ipc/IPCCore.h"; +using struct mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; +using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; +using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h"; +using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h"; +using mozilla::layers::WindowKind from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; +using mozilla::CSSIntRegion from "Units.h"; +using mozilla::LayoutDeviceIntPoint from "Units.h"; +using mozilla::LayoutDeviceIntRegion from "Units.h"; +using mozilla::LayoutDeviceIntSize from "Units.h"; +using class mozilla::TimeStamp from "mozilla/TimeStamp.h"; +using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; +using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h"; +using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::RenderReasons from "mozilla/webrender/webrender_ffi.h"; +using base::ProcessId from "base/process.h"; +using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h"; + +namespace mozilla { +namespace layers { + +struct FrameStats { + TransactionId id; + TimeStamp compositeStart; + TimeStamp renderStart; + TimeStamp compositeEnd; + int32_t contentFrameTime; + double resourceUploadTime; + double gpuCacheUploadTime; + TimeStamp transactionStart; + TimeStamp refreshStart; + TimeStamp fwdTime; + TimeStamp sceneBuiltTime; + uint32_t skippedComposites; + nsCString url; +}; + +/** + * The PCompositorBridge protocol is a top-level protocol for the compositor. + * There is an instance of the protocol for each compositor, plus one for each + * content process. In other words: + * - There is a CompositorBridgeParent/CompositorBridgeChild pair created + * for each "top level browser window", which has its own compositor. The + * CompositorBridgeChild instance lives in the UI process, and the + * CompositorBridgeParent lives in the GPU process (if there is one) or the + * UI process otherwise. + * - There is also a ContentCompositorBridgeParent/CompositorBridgeChild + * pair created for each content process. The ContentCompositorBridgeParent + * lives in the GPU process (if there is one) or the UI process otherwise. The + * CompositorBridgeChild is a singleton in the content process. Note that + * a given content process may have multiple browser instances (represented + * by BrowserChild instances), that are attached to different windows, and therefore + * rendered by different compositors. This means that when a browser instance + * sends messages via its CompositorBridgeChild, the corresponding + * ContentCompositorBridgeParent has to use the layers id in the message + * to find the correct compositor or CompositorBridgeParent to pass the message + * on to. + * + * One of the main goals of this protocol is to manage the PLayerTransaction sub- + * protocol, which is per-browser. A lot of the functions in the protocol are + * basically multiplexing/demultiplexing stuff in PLayerTransaction. + */ +[ParentImpl="CompositorBridgeParentBase"] +sync protocol PCompositorBridge +{ + manager PCompositorManager; + + manages PAPZ; + manages PAPZCTreeManager; + // A Compositor manages a single Layer Manager (PLayerTransaction) + manages PTexture; + manages PCompositorWidget; + manages PWebRenderBridge; + +child: + // The compositor completed a layers transaction. id is the layers id + // of the child layer tree that was composited (or 0 when notifying + // the root layer tree). + // transactionId is the id of the transaction before this composite, or 0 + // if there was no transaction since the last composite. + [Priority=control] async DidComposite(LayersId id, + TransactionId[] transactionId, + TimeStamp compositeStart, + TimeStamp compositeEnd); + + async NotifyFrameStats(FrameStats[] aFrameStats); + + async ParentAsyncMessages(AsyncParentMessageData[] aMessages); + + async ObserveLayersUpdate(LayersId aLayersId, LayersObserverEpoch aEpoch, bool aActive); + + async CompositorOptionsChanged(LayersId id, CompositorOptions newOptions); + + async NotifyJankedAnimations(LayersId id, uint64_t[] aJankedAnimations); + +parent: + async __delete__(); + + // Must be called before Initialize(). + async PCompositorWidget(CompositorWidgetInitData aInitData); + + // When out-of-process, this must be called to finish initialization. + sync Initialize(LayersId rootLayerTreeId); + + // Must be called after Initialize(), and only succeeds if AsyncPanZoomEnabled() is true. + async PAPZ(LayersId layersId); + async PAPZCTreeManager(LayersId layersId); + + // The child is about to be destroyed, so perform any necessary cleanup. + sync WillClose(); + + // Pause/resume the compositor. These are intended to be used on mobile, when + // the compositor needs to pause/resume in lockstep with the application. + sync Pause(); + sync Resume(); + async ResumeAsync(); + + // See bug 1316632 comment #33 for why this has to be sync. Otherwise, + // there are ordering issues with SendPLayerTransactionConstructor. + sync NotifyChildCreated(LayersId id) + returns (CompositorOptions compositorOptions); + + // This version of NotifyChildCreated also performs a layer tree mapping. + // + // See bug 1316632 comment #33 for why this has to be sync. Otherwise, + // there are ordering issues with SendPLayerTransactionConstructor. + sync MapAndNotifyChildCreated(LayersId id, ProcessId owner) + returns (CompositorOptions compositorOptions); + + async AdoptChild(LayersId id); + + // Same as NotifyChildCreated, but used when child processes need to + // reassociate layers. This must be synchronous to ensure that the + // association happens before PLayerTransactions are sent over the + // cross-process bridge. + sync NotifyChildRecreated(LayersId id) + returns (CompositorOptions compositorOptions); + + async NotifyMemoryPressure(); + + // Make sure any pending composites are started immediately and + // block until they are completed. + sync FlushRendering(RenderReasons aReasons); + + // Same as FlushRendering, but asynchronous, since not all platforms require + // synchronous repaints on resize. + async FlushRenderingAsync(RenderReasons aReasons); + + // Make sure any pending composites have been received. + sync WaitOnTransactionProcessed(); + + // Force an additional frame presentation to be executed. This is used to + // work around a windows presentation bug (See Bug 1232042) + async ForcePresent(RenderReasons aReasons); + + sync StartFrameTimeRecording(int32_t bufferSize) + returns (uint32_t startIndex); + + sync StopFrameTimeRecording(uint32_t startIndex) + returns (float[] intervals); + + async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend, TextureFlags aTextureFlags, LayersId id, uint64_t aSerial, MaybeExternalImageId aExternalImageId); + + async InitPCanvasParent(Endpoint<PCanvasParent> aEndpoint); + async ReleasePCanvasParent(); + + sync SyncWithCompositor(); + + // The pipelineId is the same as the layersId + async PWebRenderBridge(PipelineId pipelineId, LayoutDeviceIntSize aSize, WindowKind aKind); + + sync CheckContentOnlyTDR(uint32_t sequenceNum) + returns (bool isContentOnlyTDR); + + async BeginRecording(TimeStamp aRecordingStart) + returns (bool success); + + async EndRecording() + returns (FrameRecording? recording); + + // To set up sharing the composited output to Firefox Reality on Desktop + async RequestFxrOutput(); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh b/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh new file mode 100644 index 0000000000..f07a7a3fb0 --- /dev/null +++ b/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh @@ -0,0 +1,23 @@ +/* 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/. */ + +using mozilla::TimeStamp from "mozilla/TimeStamp.h"; +[MoveOnly] using class mozilla::ipc::BigBuffer from "mozilla/ipc/BigBuffer.h"; + +namespace mozilla { +namespace layers { + +struct RecordedFrameData { + TimeStamp timeOffset; + uint32_t length; +}; + +struct FrameRecording { + TimeStamp startTime; + RecordedFrameData[] frames; + BigBuffer bytes; +}; + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/PCompositorManager.ipdl b/gfx/layers/ipc/PCompositorManager.ipdl new file mode 100644 index 0000000000..3848ac2b16 --- /dev/null +++ b/gfx/layers/ipc/PCompositorManager.ipdl @@ -0,0 +1,97 @@ +/* -*- 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 protocol PCanvasManager; +include protocol PCompositorBridge; +include LayersSurfaces; +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/WebRenderMessageUtils.h"; + +using struct mozilla::void_t from "mozilla/ipc/IPCCore.h"; +using mozilla::TimeDuration from "mozilla/TimeStamp.h"; +using mozilla::CSSToLayoutDeviceScale from "Units.h"; +using mozilla::gfx::IntSize from "mozilla/gfx/2D.h"; +[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; +using mozilla::layers::CompositorOptions from "mozilla/layers/LayersMessageUtils.h"; +using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::MemoryReport from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::WebRenderError from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::layers::SharedSurfacesMemoryReport from "mozilla/layers/SharedSurfacesMemoryReport.h"; + +namespace mozilla { +namespace layers { + +struct WidgetCompositorOptions { + CSSToLayoutDeviceScale scale; + TimeDuration vsyncRate; + CompositorOptions options; + bool useExternalSurfaceSize; + IntSize surfaceSize; + uint64_t innerWindowId; +}; + +struct ContentCompositorOptions { +}; + +struct SameProcessWidgetCompositorOptions { +}; + +union CompositorBridgeOptions { + ContentCompositorOptions; + WidgetCompositorOptions; + SameProcessWidgetCompositorOptions; +}; + +/** + * The PCompositorManager protocol is the top-level protocol between the + * compositor thread and the creators of compositors. It exists in the + * following conditions: + * - One PCompositorManager between the GPU process and each content process. + * If the GPU/UI processes are combined, there is one PCompositorManager + * between the combined GPU/UI process and each content process. + * - One PCompositorManager between the GPU process and the UI process. If + * they are combined, there is still one PCompositorManager, but both the + * child and parent live in the same process. + * The intention for this protocol is to facilitate communication with the + * compositor thread for compositor data that is only shared once, rather than + * per PCompositorBridge instance. + */ +[NeedsOtherPid] +sync protocol PCompositorManager +{ + manages PCompositorBridge; + +parent: + /** + * There are three variants of a PCompositorBridge protocol, each of which can + * only be created by certain processes and configurations: + * - A "content" PCompositorBridge is requested by each content process, + * representing the drawable area for Web content. + * - A "widget" PCompositorBridge is requested by the UI process for each + * "top level browser window" for chrome and such. + * - A "same process widget" PCompositorBridge is requested by the combined + * GPU/UI process for each "top level browser window" as above. + * See gfx/layers/ipc/PCompositorBridge.ipdl for more details. + */ + async PCompositorBridge(CompositorBridgeOptions options); + + async AddSharedSurface(ExternalImageId aId, SurfaceDescriptorShared aDesc); + async RemoveSharedSurface(ExternalImageId aId); + async ReportSharedSurfacesMemory() returns (SharedSurfacesMemoryReport aReport); + + async NotifyMemoryPressure(); + + async ReportMemory() returns (MemoryReport aReport); + + async InitCanvasManager(Endpoint<PCanvasManagerParent> endpoint); + +child: + async NotifyWebRenderError(WebRenderError error); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PImageBridge.ipdl b/gfx/layers/ipc/PImageBridge.ipdl new file mode 100644 index 0000000000..07aed85e57 --- /dev/null +++ b/gfx/layers/ipc/PImageBridge.ipdl @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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 LayersSurfaces; +include LayersMessages; +include protocol PTexture; +include ProtocolTypes; +include protocol PMediaSystemResourceManager; + +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; +include "mozilla/layers/WebRenderMessageUtils.h"; + +using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h"; +using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; +using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h"; +using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h"; + +namespace mozilla { +namespace layers { + +/** + * The PImageBridge protocol is used to allow isolated threads or processes to push + * frames directly to the compositor thread/process without relying on the main thread + * which might be too busy dealing with content script. + */ +[NeedsOtherPid] +sync protocol PImageBridge +{ + manages PTexture; + manages PMediaSystemResourceManager; + +child: + async ParentAsyncMessages(AsyncParentMessageData[] aMessages); + + async DidComposite(ImageCompositeNotification[] aNotifications); + + // Report the number of frames dropped for the given CompositableHost. + async ReportFramesDropped(CompositableHandle aHandle, uint32_t aFrames); + +parent: + async Update(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId); + + // First step of the destruction sequence. This puts ImageBridge + // in a state in which it can't send asynchronous messages + // so as to not race with the channel getting closed. + // In the child side, the Closing the channel does not happen right after WillClose, + // it is scheduled in the ImageBridgeChild's message queue in order to ensure + // that all of the messages from the parent side have been received and processed + // before sending closing the channel. + async WillClose(); + + async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t aSerial, MaybeExternalImageId aExternalImageId); + async PMediaSystemResourceManager(); + + sync NewCompositable(CompositableHandle aHandle, TextureInfo aInfo); + async ReleaseCompositable(CompositableHandle aHandle); +}; + + +} // namespace +} // namespace + diff --git a/gfx/layers/ipc/PTexture.ipdl b/gfx/layers/ipc/PTexture.ipdl new file mode 100644 index 0000000000..ad6cf8fc13 --- /dev/null +++ b/gfx/layers/ipc/PTexture.ipdl @@ -0,0 +1,40 @@ +/* -*- 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 LayersSurfaces; +include protocol PCompositorBridge; +include protocol PImageBridge; +include protocol PVideoBridge; +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; + +using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; + +namespace mozilla { +namespace layers { + +/** + * PTexture is the IPDL glue between a TextureClient and a TextureHost. + */ +[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual] +sync protocol PTexture { + manager PImageBridge or PCompositorBridge or PVideoBridge; + +child: + async __delete__(); + +parent: + /** + * Asynchronously tell the compositor side to remove the texture. + */ + async Destroy(); + + async RecycleTexture(TextureFlags aTextureFlags); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PUiCompositorController.ipdl b/gfx/layers/ipc/PUiCompositorController.ipdl new file mode 100644 index 0000000000..46b733caa2 --- /dev/null +++ b/gfx/layers/ipc/PUiCompositorController.ipdl @@ -0,0 +1,48 @@ +/* -*- 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/. */ + +using mozilla::CSSRect from "Units.h"; +using mozilla::CSSToScreenScale from "Units.h"; +using mozilla::ScreenIntSize from "Units.h"; +using mozilla::ScreenPoint from "Units.h"; + +include "mozilla/GfxMessageUtils.h"; + +namespace mozilla { +namespace layers { + +/** + * The PUiCompositorController protocol is used to pause and resume the + * compositor from the UI thread. Primarily used on Android to coordinate registering and + * releasing the surface with the compositor. + */ +[NeedsOtherPid] +sync protocol PUiCompositorController +{ + +parent: + // Pause/resume the compositor. These are intended to be used on mobile, when + // the compositor needs to pause/resume in lockstep with the application. + sync Pause(); + sync Resume() returns (bool aOutResumed); + sync ResumeAndResize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight) + returns (bool aOutResumed); + + async InvalidateAndRender(); + async MaxToolbarHeight(int32_t aHeight); + async FixedBottomOffset(int32_t aOffset); + async DefaultClearColor(uint32_t aColor); + async RequestScreenPixels(); + async EnableLayerUpdateNotifications(bool aEnable); +child: + async ToolbarAnimatorMessageFromCompositor(int32_t aMessage); + async RootFrameMetrics(ScreenPoint aScrollOffset, CSSToScreenScale aZoom); + async ScreenPixels(Shmem aMem, ScreenIntSize aSize, bool aNeedsYFlip); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PVideoBridge.ipdl b/gfx/layers/ipc/PVideoBridge.ipdl new file mode 100644 index 0000000000..abb160d239 --- /dev/null +++ b/gfx/layers/ipc/PVideoBridge.ipdl @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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 LayersSurfaces; +include LayersMessages; +include protocol PTexture; + +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; + +using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h"; +using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; + +namespace mozilla { +namespace layers { + +/** + * The PVideoBridge protocol is used to share textures from the video decoders + * to the compositor. + */ +[NeedsOtherPid] +sync protocol PVideoBridge +{ + manages PTexture; + +parent: + /** + * Since PVideoBridge creates textures on behalf of another process, we also + * supply ContentParentId of the owning content process. + */ + async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend, + TextureFlags aTextureFlags, ContentParentId aContentId, uint64_t aSerial); +}; + +} // namespace +} // namespace + diff --git a/gfx/layers/ipc/PWebRenderBridge.ipdl b/gfx/layers/ipc/PWebRenderBridge.ipdl new file mode 100644 index 0000000000..e3e29112e1 --- /dev/null +++ b/gfx/layers/ipc/PWebRenderBridge.ipdl @@ -0,0 +1,139 @@ +/* -*- 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 LayersSurfaces; +include LayersMessages; +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/WebRenderMessageUtils.h"; + +include WebRenderMessages; +include protocol PCompositorBridge; +include protocol PTexture; + +using mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h"; +using mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h"; +using mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h"; +using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; +using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h"; +using mozilla::layers::CompositionPayload from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h"; +using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h"; +using mozilla::wr::RenderReasons from "mozilla/webrender/webrender_ffi.h"; +using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::MaybeIdNamespace from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::ExternalImageKeyPair from "mozilla/webrender/WebRenderTypes.h"; +[MoveOnly] using mozilla::layers::DisplayListData from "mozilla/layers/RenderRootTypes.h"; +[MoveOnly] using mozilla::layers::MaybeTransactionData from "mozilla/layers/RenderRootTypes.h"; +using mozilla::layers::FocusTarget from "mozilla/layers/FocusTarget.h"; +using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h"; +using mozilla::VsyncId from "mozilla/VsyncDispatcher.h"; + +namespace mozilla { +namespace layers { + +[ManualDealloc, ParentImpl=virtual] +sync protocol PWebRenderBridge +{ + manager PCompositorBridge; + +parent: + sync EnsureConnected() + returns (TextureFactoryIdentifier textureFactoryIdentifier, MaybeIdNamespace maybeIdNamespace, nsCString error); + + async NewCompositable(CompositableHandle handle, TextureInfo info); + async ReleaseCompositable(CompositableHandle compositable); + + async DeleteCompositorAnimations(uint64_t[] aIds); + async SetDisplayList(DisplayListData displayList, + OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId, + bool containsSVGGroup, + VsyncId vsyncId, TimeStamp vsyncStartTime, + TimeStamp refreshStartTime, TimeStamp txnStartTime, nsCString txnURL, TimeStamp fwdTime, + CompositionPayload[] payloads); + async EmptyTransaction(FocusTarget focusTarget, + MaybeTransactionData transationData, + OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId, + VsyncId vsyncId, TimeStamp vsyncStartTime, + TimeStamp refreshStartTime, TimeStamp txnStartTime, + nsCString txnURL, TimeStamp fwdTime, + CompositionPayload[] payloads); + async SetFocusTarget(FocusTarget focusTarget); + async UpdateResources(IdNamespace aIdNamespace, OpUpdateResource[] aResourceUpdates, + RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems); + async ParentCommands(IdNamespace aIdNamespace, WebRenderParentCommand[] commands); + sync GetSnapshot(PTexture texture) returns (bool aNeedsYFlip); + async SetLayersObserverEpoch(LayersObserverEpoch childEpoch); + async ClearCachedResources(); + async ClearAnimationResources(); + async SetDefaultClearColor(uint32_t aColor); + // Invalidate rendered frame + async InvalidateRenderedFrame(); + // Schedule a composite if one isn't already scheduled. + async ScheduleComposite(RenderReasons aReasons); + // Save the frame capture to disk + async Capture(); + + // Start capturing each frame to disk. See + // nsIDOMWindowUtils::wrStartCaptureSequence for documentation. + async StartCaptureSequence(nsCString aPath, uint32_t aFlags); + + // Stop the captures started by StartCaptureSequence. See + // nsIDOMWindowUtils::wrStopCaptureSequence for documentation. + async StopCaptureSequence(); + + // Replacement for PCompositorBridge::SyncWithCompositor, but for WR. We need + // it on PWebRenderBridge because it associated with a particular top-level + // window, and PCompositorBridge doesn't allow doing that in a secure manner. + sync SyncWithCompositor(); + + // Tell the compositor to notify APZ that a target scroll frame has been + // confirmed for an input event. + async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets); + + // Testing APIs + + // Enter test mode, set the sample time to sampleTime, and resample + // animations. sampleTime must not be null. + sync SetTestSampleTime(TimeStamp sampleTime); + // Leave test mode and resume normal compositing + sync LeaveTestMode(); + + // Returns the |OMTAValue| for the compositor animation with the given id. + sync GetAnimationValue(uint64_t aCompositorAnimationsId) returns (OMTAValue value); + + // The next time the display list tree is composited, add this async scroll offset + // in CSS pixels for the given ViewID. + // Useful for testing rendering of async scrolling. + sync SetAsyncScrollOffset(ViewID scrollId, float x, float y); + + // The next time the display list is composited, include this async zoom in + // for the given ViewID. + // Useful for testing rendering of async zooming. + sync SetAsyncZoom(ViewID scrollId, float zoom); + + // Flush any pending APZ repaints to the main thread. + async FlushApzRepaints(); + + // Get a copy of the compositor-side APZ test data instance for this + // layers id. + sync GetAPZTestData() returns (APZTestData data); + + // Child requests frame uniformity measurements + sync GetFrameUniformity() returns (FrameUniformityData data); + + + async Shutdown(); + sync ShutdownSync(); +child: + async WrUpdated(IdNamespace aNewIdNamespace, TextureFactoryIdentifier textureFactoryIdentifier); + async WrReleasedImages(ExternalImageKeyPair[] pairs); + async __delete__(); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/RefCountedShmem.cpp b/gfx/layers/ipc/RefCountedShmem.cpp new file mode 100644 index 0000000000..8eec82dc9a --- /dev/null +++ b/gfx/layers/ipc/RefCountedShmem.cpp @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "RefCountedShmem.h" + +#include "mozilla/Atomics.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/layers/WebRenderMessages.h" + +#define SHM_REFCOUNT_HEADER_SIZE 16 + +namespace mozilla { +namespace layers { + +uint8_t* RefCountedShm::GetBytes(const RefCountedShmem& aShm) { + uint8_t* data = aShm.buffer().get<uint8_t>(); + if (!data) { + return nullptr; + } + return data + SHM_REFCOUNT_HEADER_SIZE; +} + +size_t RefCountedShm::GetSize(const RefCountedShmem& aShm) { + if (!IsValid(aShm)) { + return 0; + } + size_t totalSize = aShm.buffer().Size<uint8_t>(); + if (totalSize < SHM_REFCOUNT_HEADER_SIZE) { + return 0; + } + + return totalSize - SHM_REFCOUNT_HEADER_SIZE; +} + +bool RefCountedShm::IsValid(const RefCountedShmem& aShm) { + return aShm.buffer().IsWritable() && + aShm.buffer().Size<uint8_t>() > SHM_REFCOUNT_HEADER_SIZE; +} + +int32_t RefCountedShm::GetReferenceCount(const RefCountedShmem& aShm) { + if (!IsValid(aShm)) { + return 0; + } + + return *aShm.buffer().get<Atomic<int32_t>>(); +} + +int32_t RefCountedShm::AddRef(const RefCountedShmem& aShm) { + if (!IsValid(aShm)) { + return 0; + } + + auto* counter = aShm.buffer().get<Atomic<int32_t>>(); + if (counter) { + return (*counter)++; + } + return 0; +} + +int32_t RefCountedShm::Release(const RefCountedShmem& aShm) { + if (!IsValid(aShm)) { + return 0; + } + + auto* counter = aShm.buffer().get<Atomic<int32_t>>(); + if (counter) { + return --(*counter); + } + + return 0; +} + +bool RefCountedShm::Alloc(mozilla::ipc::IProtocol* aAllocator, size_t aSize, + RefCountedShmem& aShm) { + MOZ_ASSERT(!IsValid(aShm)); + auto size = aSize + SHM_REFCOUNT_HEADER_SIZE; + if (!aAllocator->AllocUnsafeShmem(size, &aShm.buffer())) { + return false; + } + return true; +} + +void RefCountedShm::Dealloc(mozilla::ipc::IProtocol* aAllocator, + RefCountedShmem& aShm) { + aAllocator->DeallocShmem(aShm.buffer()); + aShm.buffer() = ipc::Shmem(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/RefCountedShmem.h b/gfx/layers/ipc/RefCountedShmem.h new file mode 100644 index 0000000000..390d6ea25f --- /dev/null +++ b/gfx/layers/ipc/RefCountedShmem.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_LAYERS_REFCOUNTED_SHMEM_H +#define MOZILLA_LAYERS_REFCOUNTED_SHMEM_H + +#include "mozilla/ipc/Shmem.h" +#include "chrome/common/ipc_message_utils.h" + +namespace mozilla { +namespace ipc { +class IProtocol; +} + +namespace layers { + +// This class is IPDL-defined +class RefCountedShmem; + +// This just implement the methods externally. +class RefCountedShm { + public: + static uint8_t* GetBytes(const RefCountedShmem& aShm); + + static size_t GetSize(const RefCountedShmem& aShm); + + static bool IsValid(const RefCountedShmem& aShm); + + static bool Alloc(mozilla::ipc::IProtocol* aAllocator, size_t aSize, + RefCountedShmem& aShm); + + static void Dealloc(mozilla::ipc::IProtocol* aAllocator, + RefCountedShmem& aShm); + + static int32_t GetReferenceCount(const RefCountedShmem& aShm); + + static int32_t AddRef(const RefCountedShmem& aShm); + + static int32_t Release(const RefCountedShmem& aShm); +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/RemoteContentController.cpp b/gfx/layers/ipc/RemoteContentController.cpp new file mode 100644 index 0000000000..d6137e4826 --- /dev/null +++ b/gfx/layers/ipc/RemoteContentController.cpp @@ -0,0 +1,519 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/RemoteContentController.h" + +#include "CompositorThread.h" +#include "MainThreadUtils.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/layers/APZCCallbackHelper.h" +#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/MatrixMessage.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/Unused.h" +#include "Units.h" +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/jni/Utils.h" +#endif + +static mozilla::LazyLogModule sApzRemoteLog("apz.cc.remote"); + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +RemoteContentController::RemoteContentController() + : mCompositorThread(NS_GetCurrentThread()), mCanSend(true) { + MOZ_ASSERT(CompositorThread()->IsOnCurrentThread()); +} + +RemoteContentController::~RemoteContentController() = default; + +void RemoteContentController::NotifyLayerTransforms( + nsTArray<MatrixMessage>&& aTransforms) { + if (!mCompositorThread->IsOnCurrentThread()) { + // We have to send messages from the compositor thread + mCompositorThread->Dispatch( + NewRunnableMethod<StoreCopyPassByRRef<nsTArray<MatrixMessage>>>( + "layers::RemoteContentController::NotifyLayerTransforms", this, + &RemoteContentController::NotifyLayerTransforms, + std::move(aTransforms))); + return; + } + + if (mCanSend) { + Unused << SendLayerTransforms(aTransforms); + } +} + +void RemoteContentController::RequestContentRepaint( + const RepaintRequest& aRequest) { + MOZ_ASSERT(IsRepaintThread()); + + if (mCanSend) { + Unused << SendRequestContentRepaint(aRequest); + } +} + +void RemoteContentController::HandleTapOnMainThread(TapType aTapType, + LayoutDevicePoint aPoint, + Modifiers aModifiers, + ScrollableLayerGuid aGuid, + uint64_t aInputBlockId) { + MOZ_LOG(sApzRemoteLog, LogLevel::Debug, + ("HandleTapOnMainThread(%d)", (int)aTapType)); + MOZ_ASSERT(NS_IsMainThread()); + + dom::BrowserParent* tab = + dom::BrowserParent::GetBrowserParentFromLayersId(aGuid.mLayersId); + if (tab) { + tab->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, aInputBlockId); + } +} + +void RemoteContentController::HandleTapOnCompositorThread( + TapType aTapType, LayoutDevicePoint aPoint, Modifiers aModifiers, + ScrollableLayerGuid aGuid, uint64_t aInputBlockId) { + MOZ_ASSERT(XRE_IsGPUProcess()); + MOZ_ASSERT(mCompositorThread->IsOnCurrentThread()); + + // The raw pointer to APZCTreeManagerParent is ok here because we are on the + // compositor thread. + APZCTreeManagerParent* apzctmp = + CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId); + if (apzctmp) { + Unused << apzctmp->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, + aInputBlockId); + } +} + +void RemoteContentController::HandleTap(TapType aTapType, + const LayoutDevicePoint& aPoint, + Modifiers aModifiers, + const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId) { + MOZ_LOG(sApzRemoteLog, LogLevel::Debug, ("HandleTap(%d)", (int)aTapType)); + APZThreadUtils::AssertOnControllerThread(); + + if (XRE_GetProcessType() == GeckoProcessType_GPU) { + if (mCompositorThread->IsOnCurrentThread()) { + HandleTapOnCompositorThread(aTapType, aPoint, aModifiers, aGuid, + aInputBlockId); + } else { + // We have to send messages from the compositor thread + mCompositorThread->Dispatch( + NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, + ScrollableLayerGuid, uint64_t>( + "layers::RemoteContentController::HandleTapOnCompositorThread", + this, &RemoteContentController::HandleTapOnCompositorThread, + aTapType, aPoint, aModifiers, aGuid, aInputBlockId)); + } + return; + } + + MOZ_ASSERT(XRE_IsParentProcess()); + + if (NS_IsMainThread()) { + HandleTapOnMainThread(aTapType, aPoint, aModifiers, aGuid, aInputBlockId); + } else { + // We must be on Android, running on the Java UI thread +#ifndef MOZ_WIDGET_ANDROID + MOZ_ASSERT(false); +#else + // We don't want to get the BrowserParent or call + // BrowserParent::SendHandleTap() from a non-main thread, so we need to + // redispatch to the main thread. However, we should use the same mechanism + // that the Android widget uses when dispatching input events to Gecko, + // which is nsAppShell::PostEvent. Note in particular that using + // NS_DispatchToMainThread would post to a different message loop, and + // introduces the possibility of this tap event getting processed out of + // order with respect to the touch events that synthesized it. + mozilla::jni::DispatchToGeckoPriorityQueue( + NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, + ScrollableLayerGuid, uint64_t>( + "layers::RemoteContentController::HandleTapOnMainThread", this, + &RemoteContentController::HandleTapOnMainThread, aTapType, aPoint, + aModifiers, aGuid, aInputBlockId)); +#endif + } +} + +void RemoteContentController::NotifyPinchGestureOnCompositorThread( + PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid, + const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange, + Modifiers aModifiers) { + MOZ_ASSERT(mCompositorThread->IsOnCurrentThread()); + + // The raw pointer to APZCTreeManagerParent is ok here because we are on the + // compositor thread. + APZCTreeManagerParent* apzctmp = + CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId); + if (apzctmp) { + Unused << apzctmp->SendNotifyPinchGesture(aType, aGuid, aFocusPoint, + aSpanChange, aModifiers); + } +} + +void RemoteContentController::NotifyPinchGesture( + PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid, + const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange, + Modifiers aModifiers) { + APZThreadUtils::AssertOnControllerThread(); + + // For now we only ever want to handle this NotifyPinchGesture message in + // the parent process, even if the APZ is sending it to a content process. + + // If we're in the GPU process, try to find a handle to the parent process + // and send it there. + if (XRE_IsGPUProcess()) { + if (mCompositorThread->IsOnCurrentThread()) { + NotifyPinchGestureOnCompositorThread(aType, aGuid, aFocusPoint, + aSpanChange, aModifiers); + } else { + mCompositorThread->Dispatch( + NewRunnableMethod<PinchGestureInput::PinchGestureType, + ScrollableLayerGuid, LayoutDevicePoint, + LayoutDeviceCoord, Modifiers>( + "layers::RemoteContentController::" + "NotifyPinchGestureOnCompositorThread", + this, + &RemoteContentController::NotifyPinchGestureOnCompositorThread, + aType, aGuid, aFocusPoint, aSpanChange, aModifiers)); + } + return; + } + + // If we're in the parent process, handle it directly. We don't have a handle + // to the widget though, so we fish out the ChromeProcessController and + // delegate to that instead. + if (XRE_IsParentProcess()) { + MOZ_ASSERT(NS_IsMainThread()); + RefPtr<GeckoContentController> rootController = + CompositorBridgeParent::GetGeckoContentControllerForRoot( + aGuid.mLayersId); + if (rootController) { + rootController->NotifyPinchGesture(aType, aGuid, aFocusPoint, aSpanChange, + aModifiers); + } + } +} + +bool RemoteContentController::IsRepaintThread() { + return mCompositorThread->IsOnCurrentThread(); +} + +void RemoteContentController::DispatchToRepaintThread( + already_AddRefed<Runnable> aTask) { + mCompositorThread->Dispatch(std::move(aTask)); +} + +void RemoteContentController::NotifyAPZStateChange( + const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg, + Maybe<uint64_t> aInputBlockId) { + if (!mCompositorThread->IsOnCurrentThread()) { + // We have to send messages from the compositor thread + mCompositorThread->Dispatch( + NewRunnableMethod<ScrollableLayerGuid, APZStateChange, int, + Maybe<uint64_t>>( + "layers::RemoteContentController::NotifyAPZStateChange", this, + &RemoteContentController::NotifyAPZStateChange, aGuid, aChange, + aArg, aInputBlockId)); + return; + } + + if (mCanSend) { + Unused << SendNotifyAPZStateChange(aGuid, aChange, aArg, aInputBlockId); + } +} + +void RemoteContentController::UpdateOverscrollVelocity( + const ScrollableLayerGuid& aGuid, float aX, float aY, bool aIsRootContent) { + if (XRE_IsParentProcess()) { +#ifdef MOZ_WIDGET_ANDROID + // We always want these to go to the parent process on Android + if (!NS_IsMainThread()) { + mozilla::jni::DispatchToGeckoPriorityQueue( + NewRunnableMethod<ScrollableLayerGuid, float, float, bool>( + "layers::RemoteContentController::UpdateOverscrollVelocity", this, + &RemoteContentController::UpdateOverscrollVelocity, aGuid, aX, aY, + aIsRootContent)); + return; + } +#endif + + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + RefPtr<GeckoContentController> rootController = + CompositorBridgeParent::GetGeckoContentControllerForRoot( + aGuid.mLayersId); + if (rootController) { + rootController->UpdateOverscrollVelocity(aGuid, aX, aY, aIsRootContent); + } + } else if (XRE_IsGPUProcess()) { + if (!mCompositorThread->IsOnCurrentThread()) { + mCompositorThread->Dispatch( + NewRunnableMethod<ScrollableLayerGuid, float, float, bool>( + "layers::RemoteContentController::UpdateOverscrollVelocity", this, + &RemoteContentController::UpdateOverscrollVelocity, aGuid, aX, aY, + aIsRootContent)); + return; + } + + MOZ_RELEASE_ASSERT(mCompositorThread->IsOnCurrentThread()); + GeckoContentController* rootController = + CompositorBridgeParent::GetGeckoContentControllerForRoot( + aGuid.mLayersId); + if (rootController) { + MOZ_RELEASE_ASSERT(rootController->IsRemote()); + Unused << static_cast<RemoteContentController*>(rootController) + ->SendUpdateOverscrollVelocity(aGuid, aX, aY, + aIsRootContent); + } + } +} + +void RemoteContentController::UpdateOverscrollOffset( + const ScrollableLayerGuid& aGuid, float aX, float aY, bool aIsRootContent) { + if (XRE_IsParentProcess()) { +#ifdef MOZ_WIDGET_ANDROID + // We always want these to go to the parent process on Android + if (!NS_IsMainThread()) { + mozilla::jni::DispatchToGeckoPriorityQueue( + NewRunnableMethod<ScrollableLayerGuid, float, float, bool>( + "layers::RemoteContentController::UpdateOverscrollOffset", this, + &RemoteContentController::UpdateOverscrollOffset, aGuid, aX, aY, + aIsRootContent)); + return; + } +#endif + + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + RefPtr<GeckoContentController> rootController = + CompositorBridgeParent::GetGeckoContentControllerForRoot( + aGuid.mLayersId); + if (rootController) { + rootController->UpdateOverscrollOffset(aGuid, aX, aY, aIsRootContent); + } + } else if (XRE_IsGPUProcess()) { + if (!mCompositorThread->IsOnCurrentThread()) { + mCompositorThread->Dispatch( + NewRunnableMethod<ScrollableLayerGuid, float, float, bool>( + "layers::RemoteContentController::UpdateOverscrollOffset", this, + &RemoteContentController::UpdateOverscrollOffset, aGuid, aX, aY, + aIsRootContent)); + return; + } + + MOZ_RELEASE_ASSERT(mCompositorThread->IsOnCurrentThread()); + GeckoContentController* rootController = + CompositorBridgeParent::GetGeckoContentControllerForRoot( + aGuid.mLayersId); + if (rootController) { + MOZ_RELEASE_ASSERT(rootController->IsRemote()); + Unused << static_cast<RemoteContentController*>(rootController) + ->SendUpdateOverscrollOffset(aGuid, aX, aY, aIsRootContent); + } + } +} + +void RemoteContentController::NotifyMozMouseScrollEvent( + const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent) { + if (!mCompositorThread->IsOnCurrentThread()) { + // We have to send messages from the compositor thread + mCompositorThread->Dispatch( + NewRunnableMethod<ScrollableLayerGuid::ViewID, nsString>( + "layers::RemoteContentController::NotifyMozMouseScrollEvent", this, + &RemoteContentController::NotifyMozMouseScrollEvent, aScrollId, + aEvent)); + return; + } + + if (mCanSend) { + Unused << SendNotifyMozMouseScrollEvent(aScrollId, aEvent); + } +} + +void RemoteContentController::NotifyFlushComplete() { + MOZ_ASSERT(IsRepaintThread()); + + if (mCanSend) { + Unused << SendNotifyFlushComplete(); + } +} + +void RemoteContentController::NotifyAsyncScrollbarDragInitiated( + uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId, + ScrollDirection aDirection) { + if (!mCompositorThread->IsOnCurrentThread()) { + // We have to send messages from the compositor thread + mCompositorThread->Dispatch(NewRunnableMethod<uint64_t, + ScrollableLayerGuid::ViewID, + ScrollDirection>( + "layers::RemoteContentController::NotifyAsyncScrollbarDragInitiated", + this, &RemoteContentController::NotifyAsyncScrollbarDragInitiated, + aDragBlockId, aScrollId, aDirection)); + return; + } + + if (mCanSend) { + Unused << SendNotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId, + aDirection); + } +} + +void RemoteContentController::NotifyAsyncScrollbarDragRejected( + const ScrollableLayerGuid::ViewID& aScrollId) { + if (!mCompositorThread->IsOnCurrentThread()) { + // We have to send messages from the compositor thread + mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>( + "layers::RemoteContentController::NotifyAsyncScrollbarDragRejected", + this, &RemoteContentController::NotifyAsyncScrollbarDragRejected, + aScrollId)); + return; + } + + if (mCanSend) { + Unused << SendNotifyAsyncScrollbarDragRejected(aScrollId); + } +} + +void RemoteContentController::NotifyAsyncAutoscrollRejected( + const ScrollableLayerGuid::ViewID& aScrollId) { + if (!mCompositorThread->IsOnCurrentThread()) { + // We have to send messages from the compositor thread + mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>( + "layers::RemoteContentController::NotifyAsyncAutoscrollRejected", this, + &RemoteContentController::NotifyAsyncAutoscrollRejected, aScrollId)); + return; + } + + if (mCanSend) { + Unused << SendNotifyAsyncAutoscrollRejected(aScrollId); + } +} + +void RemoteContentController::CancelAutoscroll( + const ScrollableLayerGuid& aGuid) { + if (XRE_GetProcessType() == GeckoProcessType_GPU) { + CancelAutoscrollCrossProcess(aGuid); + } else { + CancelAutoscrollInProcess(aGuid); + } +} + +void RemoteContentController::CancelAutoscrollInProcess( + const ScrollableLayerGuid& aGuid) { + MOZ_ASSERT(XRE_IsParentProcess()); + + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NewRunnableMethod<ScrollableLayerGuid>( + "layers::RemoteContentController::CancelAutoscrollInProcess", this, + &RemoteContentController::CancelAutoscrollInProcess, aGuid)); + return; + } + + APZCCallbackHelper::CancelAutoscroll(aGuid.mScrollId); +} + +void RemoteContentController::CancelAutoscrollCrossProcess( + const ScrollableLayerGuid& aGuid) { + MOZ_ASSERT(XRE_IsGPUProcess()); + + if (!mCompositorThread->IsOnCurrentThread()) { + mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid>( + "layers::RemoteContentController::CancelAutoscrollCrossProcess", this, + &RemoteContentController::CancelAutoscrollCrossProcess, aGuid)); + return; + } + + // The raw pointer to APZCTreeManagerParent is ok here because we are on the + // compositor thread. + if (APZCTreeManagerParent* parent = + CompositorBridgeParent::GetApzcTreeManagerParentForRoot( + aGuid.mLayersId)) { + Unused << parent->SendCancelAutoscroll(aGuid.mScrollId); + } +} + +void RemoteContentController::NotifyScaleGestureComplete( + const ScrollableLayerGuid& aGuid, float aScale) { + if (XRE_GetProcessType() == GeckoProcessType_GPU) { + NotifyScaleGestureCompleteCrossProcess(aGuid, aScale); + } else { + NotifyScaleGestureCompleteInProcess(aGuid, aScale); + } +} + +void RemoteContentController::NotifyScaleGestureCompleteInProcess( + const ScrollableLayerGuid& aGuid, float aScale) { + MOZ_ASSERT(XRE_IsParentProcess()); + + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NewRunnableMethod<ScrollableLayerGuid, float>( + "layers::RemoteContentController::NotifyScaleGestureCompleteInProcess", + this, &RemoteContentController::NotifyScaleGestureCompleteInProcess, + aGuid, aScale)); + return; + } + + RefPtr<GeckoContentController> rootController = + CompositorBridgeParent::GetGeckoContentControllerForRoot(aGuid.mLayersId); + if (rootController) { + MOZ_ASSERT(rootController != this); + if (rootController != this) { + rootController->NotifyScaleGestureComplete(aGuid, aScale); + } + } +} + +void RemoteContentController::NotifyScaleGestureCompleteCrossProcess( + const ScrollableLayerGuid& aGuid, float aScale) { + MOZ_ASSERT(XRE_IsGPUProcess()); + + if (!mCompositorThread->IsOnCurrentThread()) { + mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, float>( + "layers::RemoteContentController::" + "NotifyScaleGestureCompleteCrossProcess", + this, &RemoteContentController::NotifyScaleGestureCompleteCrossProcess, + aGuid, aScale)); + return; + } + + // The raw pointer to APZCTreeManagerParent is ok here because we are on the + // compositor thread. + if (APZCTreeManagerParent* parent = + CompositorBridgeParent::GetApzcTreeManagerParentForRoot( + aGuid.mLayersId)) { + Unused << parent->SendNotifyScaleGestureComplete(aGuid.mScrollId, aScale); + } +} + +void RemoteContentController::ActorDestroy(ActorDestroyReason aWhy) { + // This controller could possibly be kept alive longer after this + // by a RefPtr, but it is no longer valid to send messages. + mCanSend = false; +} + +void RemoteContentController::Destroy() { + if (mCanSend) { + mCanSend = false; + Unused << SendDestroy(); + } +} + +mozilla::ipc::IPCResult RemoteContentController::RecvDestroy() { + // The actor on the other side is about to get destroyed, so let's not send + // it any more messages. + mCanSend = false; + return IPC_OK(); +} + +bool RemoteContentController::IsRemote() { return true; } + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/RemoteContentController.h b/gfx/layers/ipc/RemoteContentController.h new file mode 100644 index 0000000000..168e924329 --- /dev/null +++ b/gfx/layers/ipc/RemoteContentController.h @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_layers_RemoteContentController_h +#define mozilla_layers_RemoteContentController_h + +#include "mozilla/layers/GeckoContentController.h" +#include "mozilla/layers/PAPZParent.h" + +namespace mozilla { + +namespace dom { +class BrowserParent; +} + +namespace layers { + +/** + * RemoteContentController implements PAPZChild and is used to access a + * GeckoContentController that lives in a different process. + * + * RemoteContentController lives on the compositor thread. All methods can + * be called off the compositor thread and will get dispatched to the right + * thread, with the exception of RequestContentRepaint and NotifyFlushComplete, + * which must be called on the repaint thread, which in this case is the + * compositor thread. + */ +class RemoteContentController : public GeckoContentController, + public PAPZParent { + using GeckoContentController::APZStateChange; + using GeckoContentController::TapType; + + public: + RemoteContentController(); + + virtual ~RemoteContentController(); + + void NotifyLayerTransforms(nsTArray<MatrixMessage>&& aTransforms) override; + + void RequestContentRepaint(const RepaintRequest& aRequest) override; + + void HandleTap(TapType aTapType, const LayoutDevicePoint& aPoint, + Modifiers aModifiers, const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId) override; + + void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType, + const ScrollableLayerGuid& aGuid, + const LayoutDevicePoint& aFocusPoint, + LayoutDeviceCoord aSpanChange, + Modifiers aModifiers) override; + + bool IsRepaintThread() override; + + void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override; + + void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, + APZStateChange aChange, int aArg, + Maybe<uint64_t> aInputBlockId) override; + + void UpdateOverscrollVelocity(const ScrollableLayerGuid& aGuid, float aX, + float aY, bool aIsRootContent) override; + + void UpdateOverscrollOffset(const ScrollableLayerGuid& aGuid, float aX, + float aY, bool aIsRootContent) override; + + void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId, + const nsString& aEvent) override; + + void NotifyFlushComplete() override; + + void NotifyAsyncScrollbarDragInitiated( + uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId, + ScrollDirection aDirection) override; + void NotifyAsyncScrollbarDragRejected( + const ScrollableLayerGuid::ViewID& aScrollId) override; + + void NotifyAsyncAutoscrollRejected( + const ScrollableLayerGuid::ViewID& aScrollId) override; + + void CancelAutoscroll(const ScrollableLayerGuid& aScrollId) override; + + void NotifyScaleGestureComplete(const ScrollableLayerGuid& aGuid, + float aScale) override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + + void Destroy() override; + mozilla::ipc::IPCResult RecvDestroy(); + + bool IsRemote() override; + + private: + nsCOMPtr<nsISerialEventTarget> mCompositorThread; + bool mCanSend; + + void HandleTapOnMainThread(TapType aType, LayoutDevicePoint aPoint, + Modifiers aModifiers, ScrollableLayerGuid aGuid, + uint64_t aInputBlockId); + void HandleTapOnCompositorThread(TapType aType, LayoutDevicePoint aPoint, + Modifiers aModifiers, + ScrollableLayerGuid aGuid, + uint64_t aInputBlockId); + void NotifyPinchGestureOnCompositorThread( + PinchGestureInput::PinchGestureType aType, + const ScrollableLayerGuid& aGuid, const LayoutDevicePoint& aFocusPoint, + LayoutDeviceCoord aSpanChange, Modifiers aModifiers); + + void CancelAutoscrollInProcess(const ScrollableLayerGuid& aScrollId); + void CancelAutoscrollCrossProcess(const ScrollableLayerGuid& aScrollId); + void NotifyScaleGestureCompleteInProcess(const ScrollableLayerGuid& aGuid, + float aScale); + void NotifyScaleGestureCompleteCrossProcess(const ScrollableLayerGuid& aGuid, + float aScale); +}; + +} // namespace layers + +} // namespace mozilla + +#endif // mozilla_layers_RemoteContentController_h diff --git a/gfx/layers/ipc/ShadowLayerUtils.h b/gfx/layers/ipc/ShadowLayerUtils.h new file mode 100644 index 0000000000..de07854b04 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerUtils.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef IPC_ShadowLayerUtils_h +#define IPC_ShadowLayerUtils_h + +#include "ipc/EnumSerializer.h" +#include "ipc/IPCMessageUtils.h" +#include "GLContextTypes.h" +#include "SurfaceDescriptor.h" +#include "SurfaceTypes.h" +#include "mozilla/WidgetUtils.h" + +namespace IPC { + +template <> +struct ParamTraits<mozilla::ScreenRotation> + : public ContiguousEnumSerializer<mozilla::ScreenRotation, + mozilla::ROTATION_0, + mozilla::ROTATION_COUNT> {}; + +} // namespace IPC + +#endif // IPC_ShadowLayerUtils_h diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp new file mode 100644 index 0000000000..5b40268b5f --- /dev/null +++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "SharedPlanarYCbCrImage.h" +#include <stddef.h> // for size_t +#include <stdio.h> // for printf +#include "gfx2DGlue.h" // for Moz2D transition helpers +#include "ISurfaceAllocator.h" // for ISurfaceAllocator, etc +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/gfx/Types.h" // for SurfaceFormat::SurfaceFormat::YUV +#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc +#include "mozilla/layers/ImageClient.h" // for ImageClient +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc +#include "mozilla/layers/TextureClient.h" +#include "mozilla/layers/TextureClientRecycleAllocator.h" +#include "mozilla/layers/BufferTexture.h" +#include "mozilla/layers/ImageDataSerializer.h" +#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild +#include "mozilla/mozalloc.h" // for operator delete +#include "nsISupportsImpl.h" // for Image::AddRef +#include "mozilla/ipc/Shmem.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::ipc; + +SharedPlanarYCbCrImage::SharedPlanarYCbCrImage(ImageClient* aCompositable) + : mCompositable(aCompositable) { + MOZ_COUNT_CTOR(SharedPlanarYCbCrImage); +} + +SharedPlanarYCbCrImage::SharedPlanarYCbCrImage( + TextureClientRecycleAllocator* aRecycleAllocator) + : mRecycleAllocator(aRecycleAllocator) { + MOZ_COUNT_CTOR(SharedPlanarYCbCrImage); +} + +SharedPlanarYCbCrImage::~SharedPlanarYCbCrImage() { + MOZ_COUNT_DTOR(SharedPlanarYCbCrImage); +} + +TextureClientRecycleAllocator* SharedPlanarYCbCrImage::RecycleAllocator() { + static const uint32_t MAX_POOLED_VIDEO_COUNT = 5; + + if (!mRecycleAllocator && mCompositable) { + if (!mCompositable->HasTextureClientRecycler()) { + // Initialize TextureClientRecycler + mCompositable->GetTextureClientRecycler()->SetMaxPoolSize( + MAX_POOLED_VIDEO_COUNT); + } + mRecycleAllocator = mCompositable->GetTextureClientRecycler(); + } + return mRecycleAllocator; +} + +size_t SharedPlanarYCbCrImage::SizeOfExcludingThis( + MallocSizeOf aMallocSizeOf) const { + // NB: Explicitly skipping mTextureClient, the memory is already reported + // at time of allocation in GfxMemoryImageReporter. + // Not owned: + // - mCompositable + return 0; +} + +TextureClient* SharedPlanarYCbCrImage::GetTextureClient( + KnowsCompositor* aKnowsCompositor) { + return mTextureClient.get(); +} + +already_AddRefed<gfx::SourceSurface> +SharedPlanarYCbCrImage::GetAsSourceSurface() { + if (!IsValid()) { + NS_WARNING("Can't get as surface"); + return nullptr; + } + return PlanarYCbCrImage::GetAsSourceSurface(); +} + +bool SharedPlanarYCbCrImage::CopyData(const PlanarYCbCrData& aData) { + // If mTextureClient has not already been allocated by CreateEmptyBuffer, + // allocate it. This code path is slower than the one used when + // CreateEmptyBuffer has been called since it will trigger a full copy. + if (!mTextureClient && + !CreateEmptyBuffer(aData, aData.YDataSize(), aData.CbCrDataSize())) { + return false; + } + + TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_WRITE_ONLY); + if (!autoLock.Succeeded()) { + MOZ_ASSERT(false, "Failed to lock the texture."); + return false; + } + + if (!UpdateYCbCrTextureClient(mTextureClient, aData)) { + MOZ_ASSERT(false, "Failed to copy YCbCr data into the TextureClient"); + return false; + } + mTextureClient->MarkImmutable(); + return true; +} + +bool SharedPlanarYCbCrImage::AdoptData(const Data& aData) { + MOZ_ASSERT(false, "This shouldn't be used."); + return false; +} + +bool SharedPlanarYCbCrImage::IsValid() const { + return mTextureClient && mTextureClient->IsValid(); +} + +bool SharedPlanarYCbCrImage::CreateEmptyBuffer(const PlanarYCbCrData& aData, + const gfx::IntSize& aYSize, + const gfx::IntSize& aCbCrSize) { + MOZ_ASSERT(!mTextureClient, "This image already has allocated data"); + + TextureFlags flags = + mCompositable ? mCompositable->GetTextureFlags() : TextureFlags::DEFAULT; + { + YCbCrTextureClientAllocationHelper helper(aData, aYSize, aCbCrSize, flags); + mTextureClient = RecycleAllocator()->CreateOrRecycle(helper); + } + + if (!mTextureClient) { + NS_WARNING("SharedPlanarYCbCrImage::Allocate failed."); + return false; + } + + MappedYCbCrTextureData mapped; + // The locking here is sort of a lie. The SharedPlanarYCbCrImage just pulls + // pointers out of the TextureClient and keeps them around, which works only + // because the underlyin BufferTextureData is always mapped in memory even + // outside of the lock/unlock interval. That's sad and new code should follow + // this example. + if (!mTextureClient->Lock(OpenMode::OPEN_READ) || + !mTextureClient->BorrowMappedYCbCrData(mapped)) { + MOZ_CRASH("GFX: Cannot lock or borrow mapped YCbCr"); + } + + // copy some of aData's values in mData (most of them) + mData.mYChannel = mapped.y.data; + mData.mCbChannel = mapped.cb.data; + mData.mCrChannel = mapped.cr.data; + mData.mPictureRect = aData.mPictureRect; + mData.mStereoMode = aData.mStereoMode; + mData.mYUVColorSpace = aData.mYUVColorSpace; + mData.mColorDepth = aData.mColorDepth; + mData.mChromaSubsampling = aData.mChromaSubsampling; + // those members are not always equal to aData's, due to potentially different + // packing. + mData.mYSkip = 0; + mData.mCbSkip = 0; + mData.mCrSkip = 0; + mData.mYStride = aData.mYStride; + mData.mCbCrStride = aData.mCbCrStride; + + // do not set mBuffer like in PlanarYCbCrImage because the later + // will try to manage this memory without knowing it belongs to a + // shmem. + mBufferSize = ImageDataSerializer::ComputeYCbCrBufferSize( + aYSize, mData.mYStride, aCbCrSize, mData.mCbCrStride); + mSize = mData.mPictureRect.Size(); + mOrigin = mData.mPictureRect.TopLeft(); + + mTextureClient->Unlock(); + + return mBufferSize > 0; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.h b/gfx/layers/ipc/SharedPlanarYCbCrImage.h new file mode 100644 index 0000000000..8f6263600d --- /dev/null +++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 <stdint.h> // for uint8_t, uint32_t +#include "ImageContainer.h" // for PlanarYCbCrImage, etc +#include "mozilla/Attributes.h" // for override +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/ipc/Shmem.h" // for Shmem +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsDebug.h" // for NS_WARNING +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR + +#ifndef MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H +# define MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H + +namespace mozilla { +namespace layers { + +class ImageClient; +class TextureClient; +class TextureClientRecycleAllocator; + +class SharedPlanarYCbCrImage : public PlanarYCbCrImage { + public: + explicit SharedPlanarYCbCrImage(ImageClient* aCompositable); + explicit SharedPlanarYCbCrImage( + TextureClientRecycleAllocator* aRecycleAllocator); + + protected: + virtual ~SharedPlanarYCbCrImage(); + + public: + TextureClient* GetTextureClient(KnowsCompositor* aKnowsCompositor) override; + + already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override; + bool CopyData(const PlanarYCbCrData& aData) override; + bool AdoptData(const Data& aData) override; + bool CreateEmptyBuffer(const Data& aData, const gfx::IntSize& aYSize, + const gfx::IntSize& aCbCrSize) override; + + bool IsValid() const override; + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); + } + + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override; + + TextureClientRecycleAllocator* RecycleAllocator(); + + private: + RefPtr<TextureClient> mTextureClient; + RefPtr<ImageClient> mCompositable; + RefPtr<TextureClientRecycleAllocator> mRecycleAllocator; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/SharedRGBImage.cpp b/gfx/layers/ipc/SharedRGBImage.cpp new file mode 100644 index 0000000000..392cb09610 --- /dev/null +++ b/gfx/layers/ipc/SharedRGBImage.cpp @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "SharedRGBImage.h" +#include "ImageTypes.h" // for ImageFormat::SHARED_RGB, etc +#include "mozilla/ipc/Shmem.h" // for Shmem +#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat, etc +#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat +#include "mozilla/gfx/Point.h" // for IntSIze +#include "mozilla/layers/BufferTexture.h" +#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator, etc +#include "mozilla/layers/ImageClient.h" // for ImageClient +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc +#include "mozilla/layers/TextureClient.h" // for BufferTextureClient, etc +#include "mozilla/layers/TextureClientRecycleAllocator.h" // for ITextureClientAllocationHelper +#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION +#include "nsISupportsImpl.h" // for Image::AddRef, etc +#include "nsProxyRelease.h" +#include "nsRect.h" // for mozilla::gfx::IntRect + +// Just big enough for a 1080p RGBA32 frame +#define MAX_FRAME_SIZE (16 * 1024 * 1024) + +namespace mozilla { +namespace layers { + +class TextureClientForRawBufferAccessAllocationHelper + : public ITextureClientAllocationHelper { + public: + TextureClientForRawBufferAccessAllocationHelper(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + TextureFlags aTextureFlags) + : ITextureClientAllocationHelper(aFormat, aSize, BackendSelector::Content, + aTextureFlags, ALLOC_DEFAULT) {} + + bool IsCompatible(TextureClient* aTextureClient) override { + bool ret = aTextureClient->GetFormat() == mFormat && + aTextureClient->GetSize() == mSize; + return ret; + } + + already_AddRefed<TextureClient> Allocate( + KnowsCompositor* aAllocator) override { + return TextureClient::CreateForRawBufferAccess( + aAllocator, mFormat, mSize, gfx::BackendType::NONE, mTextureFlags); + } +}; + +SharedRGBImage::SharedRGBImage(ImageClient* aCompositable) + : Image(nullptr, ImageFormat::SHARED_RGB), mCompositable(aCompositable) { + MOZ_COUNT_CTOR(SharedRGBImage); +} + +SharedRGBImage::SharedRGBImage(TextureClientRecycleAllocator* aRecycleAllocator) + : Image(nullptr, ImageFormat::SHARED_RGB), + mRecycleAllocator(aRecycleAllocator) { + MOZ_COUNT_CTOR(SharedRGBImage); +} + +SharedRGBImage::~SharedRGBImage() { + MOZ_COUNT_DTOR(SharedRGBImage); + NS_ReleaseOnMainThread("SharedRGBImage::mSourceSurface", + mSourceSurface.forget()); +} + +TextureClientRecycleAllocator* SharedRGBImage::RecycleAllocator() { + static const uint32_t MAX_POOLED_VIDEO_COUNT = 5; + + if (!mRecycleAllocator && mCompositable) { + if (!mCompositable->HasTextureClientRecycler()) { + // Initialize TextureClientRecycler + mCompositable->GetTextureClientRecycler()->SetMaxPoolSize( + MAX_POOLED_VIDEO_COUNT); + } + mRecycleAllocator = mCompositable->GetTextureClientRecycler(); + } + return mRecycleAllocator; +} + +bool SharedRGBImage::Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat) { + mSize = aSize; + + TextureFlags flags = + mCompositable ? mCompositable->GetTextureFlags() : TextureFlags::DEFAULT; + { + TextureClientForRawBufferAccessAllocationHelper helper(aFormat, aSize, + flags); + mTextureClient = RecycleAllocator()->CreateOrRecycle(helper); + } + + return !!mTextureClient; +} + +gfx::IntSize SharedRGBImage::GetSize() const { return mSize; } + +TextureClient* SharedRGBImage::GetTextureClient( + KnowsCompositor* aKnowsCompositor) { + return mTextureClient.get(); +} + +static void ReleaseTextureClient(void* aData) { + RELEASE_MANUALLY(static_cast<TextureClient*>(aData)); +} + +static gfx::UserDataKey sTextureClientKey; + +already_AddRefed<gfx::SourceSurface> SharedRGBImage::GetAsSourceSurface() { + NS_ASSERTION(NS_IsMainThread(), "Must be main thread"); + + if (mSourceSurface) { + RefPtr<gfx::SourceSurface> surface(mSourceSurface); + return surface.forget(); + } + + RefPtr<gfx::SourceSurface> surface; + { + // We are 'borrowing' the DrawTarget and retaining a permanent reference to + // the underlying data (via the surface). It is in this instance since we + // know that the TextureClient is always wrapping a BufferTextureData and + // therefore it won't go away underneath us. + BufferTextureData* decoded_buffer = + mTextureClient->GetInternalData()->AsBufferTextureData(); + RefPtr<gfx::DrawTarget> drawTarget = decoded_buffer->BorrowDrawTarget(); + + if (!drawTarget) { + return nullptr; + } + + surface = drawTarget->Snapshot(); + if (!surface) { + return nullptr; + } + + // The surface may outlive the owning TextureClient. So, we need to ensure + // that the surface keeps the TextureClient alive via a reference held in + // user data. The TextureClient's DrawTarget only has a weak reference to + // the surface, so we won't create any cycles by just referencing the + // TextureClient. + if (!surface->GetUserData(&sTextureClientKey)) { + surface->AddUserData(&sTextureClientKey, mTextureClient, + ReleaseTextureClient); + ADDREF_MANUALLY(mTextureClient); + } + } + + mSourceSurface = surface; + return surface.forget(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/SharedRGBImage.h b/gfx/layers/ipc/SharedRGBImage.h new file mode 100644 index 0000000000..ddec2dc9aa --- /dev/null +++ b/gfx/layers/ipc/SharedRGBImage.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef SHAREDRGBIMAGE_H_ +#define SHAREDRGBIMAGE_H_ + +#include <stddef.h> // for size_t +#include <stdint.h> // for uint8_t + +#include "ImageContainer.h" // for ISharedImage, Image, etc +#include "gfxTypes.h" +#include "mozilla/Attributes.h" // for override +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/gfx/Types.h" // for SurfaceFormat +#include "nsCOMPtr.h" // for already_AddRefed + +namespace mozilla { +namespace layers { + +class ImageClient; +class TextureClient; + +/** + * Stores RGB data in shared memory + * It is assumed that the image width and stride are equal + */ +class SharedRGBImage : public Image { + public: + explicit SharedRGBImage(ImageClient* aCompositable); + explicit SharedRGBImage(TextureClientRecycleAllocator* aRecycleAllocator); + + protected: + virtual ~SharedRGBImage(); + + public: + TextureClient* GetTextureClient(KnowsCompositor* aKnowsCompositor) override; + + gfx::IntSize GetSize() const override; + + already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override; + + bool Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat); + + private: + TextureClientRecycleAllocator* RecycleAllocator(); + + gfx::IntSize mSize; + RefPtr<TextureClient> mTextureClient; + RefPtr<ImageClient> mCompositable; + RefPtr<TextureClientRecycleAllocator> mRecycleAllocator; + RefPtr<gfx::SourceSurface> mSourceSurface; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/SharedSurfacesChild.cpp b/gfx/layers/ipc/SharedSurfacesChild.cpp new file mode 100644 index 0000000000..1949acb8ad --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesChild.cpp @@ -0,0 +1,587 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "SharedSurfacesChild.h" +#include "SharedSurfacesParent.h" +#include "CompositorManagerChild.h" +#include "mozilla/layers/IpcResourceUpdateQueue.h" +#include "mozilla/layers/SourceSurfaceSharedData.h" +#include "mozilla/layers/WebRenderBridgeChild.h" +#include "mozilla/layers/RenderRootStateManager.h" +#include "mozilla/SchedulerGroup.h" +#include "mozilla/StaticPrefs_image.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +/* static */ +UserDataKey SharedSurfacesChild::sSharedKey; + +SharedSurfacesChild::ImageKeyData::ImageKeyData( + RenderRootStateManager* aManager, const wr::ImageKey& aImageKey) + : mManager(aManager), mImageKey(aImageKey) {} + +SharedSurfacesChild::ImageKeyData::ImageKeyData( + SharedSurfacesChild::ImageKeyData&& aOther) + : mManager(std::move(aOther.mManager)), + mDirtyRect(std::move(aOther.mDirtyRect)), + mImageKey(aOther.mImageKey) {} + +SharedSurfacesChild::ImageKeyData& SharedSurfacesChild::ImageKeyData::operator=( + SharedSurfacesChild::ImageKeyData&& aOther) { + mManager = std::move(aOther.mManager); + mDirtyRect = std::move(aOther.mDirtyRect); + mImageKey = aOther.mImageKey; + return *this; +} + +SharedSurfacesChild::ImageKeyData::~ImageKeyData() = default; + +void SharedSurfacesChild::ImageKeyData::MergeDirtyRect( + const Maybe<IntRect>& aDirtyRect) { + if (mDirtyRect) { + if (aDirtyRect) { + mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect.ref()); + } + } else { + mDirtyRect = aDirtyRect; + } +} + +SharedSurfacesChild::SharedUserData::SharedUserData( + const wr::ExternalImageId& aId) + : Runnable("SharedSurfacesChild::SharedUserData"), + mId(aId), + mShared(false) {} + +SharedSurfacesChild::SharedUserData::~SharedUserData() { + // We may fail to dispatch during shutdown, and since it would be issued on + // the main thread, it releases the runnable instead of leaking it. + if (mShared || !mKeys.IsEmpty()) { + if (NS_IsMainThread()) { + SharedSurfacesChild::Unshare(mId, mShared, mKeys); + } else { + MOZ_ASSERT_UNREACHABLE("Shared resources not released!"); + } + } +} + +/* static */ +void SharedSurfacesChild::SharedUserData::Destroy(void* aClosure) { + MOZ_ASSERT(aClosure); + RefPtr<SharedUserData> data = + dont_AddRef(static_cast<SharedUserData*>(aClosure)); + if (data->mShared || !data->mKeys.IsEmpty()) { + SchedulerGroup::Dispatch(TaskCategory::Other, data.forget()); + } +} + +NS_IMETHODIMP SharedSurfacesChild::SharedUserData::Run() { + SharedSurfacesChild::Unshare(mId, mShared, mKeys); + mShared = false; + mKeys.Clear(); + return NS_OK; +} + +wr::ImageKey SharedSurfacesChild::SharedUserData::UpdateKey( + RenderRootStateManager* aManager, wr::IpcResourceUpdateQueue& aResources, + const Maybe<IntRect>& aDirtyRect) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(!aManager->IsDestroyed()); + + // We iterate through all of the items to ensure we clean up the old + // RenderRootStateManager references. Most of the time there will be few + // entries and this should not be particularly expensive compared to the + // cost of duplicating image keys. In an ideal world, we would generate a + // single key for the surface, and it would be usable on all of the + // renderer instances. For now, we must allocate a key for each WR bridge. + wr::ImageKey key; + bool found = false; + auto i = mKeys.Length(); + while (i > 0) { + --i; + ImageKeyData& entry = mKeys[i]; + if (entry.mManager->IsDestroyed()) { + mKeys.RemoveElementAt(i); + } else if (entry.mManager == aManager) { + WebRenderBridgeChild* wrBridge = aManager->WrBridge(); + MOZ_ASSERT(wrBridge); + + // Even if the manager is the same, its underlying WebRenderBridgeChild + // can change state. If our namespace differs, then our old key has + // already been discarded. + bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace; + if (!ownsKey) { + entry.mImageKey = wrBridge->GetNextImageKey(); + entry.TakeDirtyRect(); + aResources.AddSharedExternalImage(mId, entry.mImageKey); + } else { + entry.MergeDirtyRect(aDirtyRect); + Maybe<IntRect> dirtyRect = entry.TakeDirtyRect(); + if (dirtyRect) { + MOZ_ASSERT(mShared); + aResources.UpdateSharedExternalImage( + mId, entry.mImageKey, ViewAs<ImagePixel>(dirtyRect.ref())); + } + } + + key = entry.mImageKey; + found = true; + } else { + // We don't have the resource update queue for this manager, so just + // accumulate the dirty rects until it is requested. + entry.MergeDirtyRect(aDirtyRect); + } + } + + if (!found) { + key = aManager->WrBridge()->GetNextImageKey(); + ImageKeyData data(aManager, key); + mKeys.AppendElement(std::move(data)); + aResources.AddSharedExternalImage(mId, key); + } + + return key; +} + +/* static */ +SourceSurfaceSharedData* SharedSurfacesChild::AsSourceSurfaceSharedData( + SourceSurface* aSurface) { + MOZ_ASSERT(aSurface); + switch (aSurface->GetType()) { + case SurfaceType::DATA_SHARED: + case SurfaceType::DATA_RECYCLING_SHARED: + return static_cast<SourceSurfaceSharedData*>(aSurface); + default: + return nullptr; + } +} + +/* static */ +nsresult SharedSurfacesChild::ShareInternal(SourceSurfaceSharedData* aSurface, + SharedUserData** aUserData) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aSurface); + MOZ_ASSERT(aUserData); + + CompositorManagerChild* manager = CompositorManagerChild::GetInstance(); + if (NS_WARN_IF(!manager || !manager->CanSend())) { + // We cannot try to share the surface, most likely because the GPU process + // crashed. Ideally, we would retry when it is ready, but the handles may be + // a scarce resource, which can cause much more serious problems if we run + // out. Better to copy into a fresh buffer later. + aSurface->FinishedSharing(); + return NS_ERROR_NOT_INITIALIZED; + } + + SharedUserData* data = + static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey)); + if (!data) { + data = + MakeAndAddRef<SharedUserData>(manager->GetNextExternalImageId()).take(); + aSurface->AddUserData(&sSharedKey, data, SharedUserData::Destroy); + } else if (!manager->OwnsExternalImageId(data->Id())) { + // If the id isn't owned by us, that means the bridge was reinitialized, due + // to the GPU process crashing. All previous mappings have been released. + data->SetId(manager->GetNextExternalImageId()); + } else if (data->IsShared()) { + // It has already been shared with the GPU process. + *aUserData = data; + return NS_OK; + } + + // Ensure that the handle doesn't get released until after we have finished + // sending the buffer to the GPU process and/or reallocating it. + // FinishedSharing is not a sufficient condition because another thread may + // decide we are done while we are in the processing of sharing our newly + // reallocated handle. Once it goes out of scope, it may release the handle. + SourceSurfaceSharedData::HandleLock lock(aSurface); + + // If we live in the same process, then it is a simple matter of directly + // asking the parent instance to store a pointer to the same data, no need + // to map the data into our memory space twice. + if (manager->SameProcess()) { + SharedSurfacesParent::AddSameProcess(data->Id(), aSurface); + data->MarkShared(); + *aUserData = data; + return NS_OK; + } + + // Attempt to share a handle with the GPU process. The handle may or may not + // be available -- it will only be available if it is either not yet finalized + // and/or if it has been finalized but never used for drawing in process. + ipc::SharedMemoryBasic::Handle handle = ipc::SharedMemoryBasic::NULLHandle(); + nsresult rv = aSurface->CloneHandle(handle); + if (rv == NS_ERROR_NOT_AVAILABLE) { + // It is at least as expensive to copy the image to the GPU process if we + // have already closed the handle necessary to share, but if we reallocate + // the shared buffer to get a new handle, we can save some memory. + if (NS_WARN_IF(!aSurface->ReallocHandle())) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // Reattempt the sharing of the handle to the GPU process. + rv = aSurface->CloneHandle(handle); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_ASSERT(rv != NS_ERROR_NOT_AVAILABLE); + return rv; + } + + SurfaceFormat format = aSurface->GetFormat(); + MOZ_RELEASE_ASSERT( + format == SurfaceFormat::B8G8R8X8 || format == SurfaceFormat::B8G8R8A8, + "bad format"); + + data->MarkShared(); + manager->SendAddSharedSurface( + data->Id(), + SurfaceDescriptorShared(aSurface->GetSize(), aSurface->Stride(), format, + std::move(handle))); + *aUserData = data; + return NS_OK; +} + +/* static */ +void SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface) { + MOZ_ASSERT(aSurface); + + // The IPDL actor to do sharing can only be accessed on the main thread so we + // need to dispatch if off the main thread. However there is no real danger if + // we end up racing because if it is already shared, this method will do + // nothing. + if (!NS_IsMainThread()) { + class ShareRunnable final : public Runnable { + public: + explicit ShareRunnable(SourceSurfaceSharedData* aSurface) + : Runnable("SharedSurfacesChild::Share"), mSurface(aSurface) {} + + NS_IMETHOD Run() override { + SharedUserData* unused = nullptr; + SharedSurfacesChild::ShareInternal(mSurface, &unused); + return NS_OK; + } + + private: + RefPtr<SourceSurfaceSharedData> mSurface; + }; + + SchedulerGroup::Dispatch(TaskCategory::Other, + MakeAndAddRef<ShareRunnable>(aSurface)); + return; + } + + SharedUserData* unused = nullptr; + SharedSurfacesChild::ShareInternal(aSurface, &unused); +} + +/* static */ +nsresult SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::ImageKey& aKey) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aSurface); + MOZ_ASSERT(aManager); + + // Each time the surface changes, the producers of SourceSurfaceSharedData + // surfaces promise to increment the invalidation counter each time the + // surface has changed. We can use this counter to determine whether or not + // we should update our paired ImageKey. + Maybe<IntRect> dirtyRect = aSurface->TakeDirtyRect(); + SharedUserData* data = nullptr; + nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data); + if (NS_SUCCEEDED(rv)) { + MOZ_ASSERT(data); + aKey = data->UpdateKey(aManager, aResources, dirtyRect); + } + + return rv; +} + +/* static */ +nsresult SharedSurfacesChild::Share(SourceSurface* aSurface, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::ImageKey& aKey) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aSurface); + MOZ_ASSERT(aManager); + + auto sharedSurface = AsSourceSurfaceSharedData(aSurface); + if (!sharedSurface) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + return Share(sharedSurface, aManager, aResources, aKey); +} + +/* static */ +nsresult SharedSurfacesChild::Share(SourceSurface* aSurface, + wr::ExternalImageId& aId) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aSurface); + + auto sharedSurface = AsSourceSurfaceSharedData(aSurface); + if (!sharedSurface) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + // The external image ID does not change with the invalidation counter. The + // caller of this should be aware of the invalidations of the surface through + // another mechanism (e.g. imgRequestProxy listener notifications). + SharedUserData* data = nullptr; + nsresult rv = ShareInternal(sharedSurface, &data); + if (NS_SUCCEEDED(rv)) { + MOZ_ASSERT(data); + aId = data->Id(); + } + + return rv; +} + +/* static */ +void SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId, + bool aReleaseId, + nsTArray<ImageKeyData>& aKeys) { + MOZ_ASSERT(NS_IsMainThread()); + + for (const auto& entry : aKeys) { + if (!entry.mManager->IsDestroyed()) { + entry.mManager->AddImageKeyForDiscard(entry.mImageKey); + } + } + + if (!aReleaseId) { + // We don't own the external image ID itself. + return; + } + + CompositorManagerChild* manager = CompositorManagerChild::GetInstance(); + if (MOZ_UNLIKELY(!manager || !manager->CanSend())) { + return; + } + + if (manager->OwnsExternalImageId(aId)) { + // Only attempt to release current mappings in the compositor process. It is + // possible we had a surface that was previously shared, the compositor + // process crashed / was restarted, and then we freed the surface. In that + // case we know the mapping has already been freed. + manager->SendRemoveSharedSurface(aId); + } +} + +/* static */ Maybe<wr::ExternalImageId> SharedSurfacesChild::GetExternalId( + const SourceSurfaceSharedData* aSurface) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aSurface); + + SharedUserData* data = + static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey)); + if (!data || !data->IsShared()) { + return Nothing(); + } + + return Some(data->Id()); +} + +AnimationImageKeyData::AnimationImageKeyData(RenderRootStateManager* aManager, + const wr::ImageKey& aImageKey) + : SharedSurfacesChild::ImageKeyData(aManager, aImageKey) {} + +AnimationImageKeyData::AnimationImageKeyData(AnimationImageKeyData&& aOther) + : SharedSurfacesChild::ImageKeyData(std::move(aOther)), + mPendingRelease(std::move(aOther.mPendingRelease)) {} + +AnimationImageKeyData& AnimationImageKeyData::operator=( + AnimationImageKeyData&& aOther) { + mPendingRelease = std::move(aOther.mPendingRelease); + SharedSurfacesChild::ImageKeyData::operator=(std::move(aOther)); + return *this; +} + +AnimationImageKeyData::~AnimationImageKeyData() = default; + +SharedSurfacesAnimation::~SharedSurfacesAnimation() { + MOZ_ASSERT(mKeys.IsEmpty()); +} + +void SharedSurfacesAnimation::Destroy() { + if (!NS_IsMainThread()) { + nsCOMPtr<nsIRunnable> task = + NewRunnableMethod("SharedSurfacesAnimation::Destroy", this, + &SharedSurfacesAnimation::Destroy); + SchedulerGroup::Dispatch(TaskCategory::Other, task.forget()); + return; + } + + if (mKeys.IsEmpty()) { + return; + } + + for (const auto& entry : mKeys) { + MOZ_ASSERT(!entry.mManager->IsDestroyed()); + if (StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()) { + entry.mManager->DeregisterAsyncAnimation(entry.mImageKey); + } + entry.mManager->AddImageKeyForDiscard(entry.mImageKey); + } + + mKeys.Clear(); +} + +void SharedSurfacesAnimation::HoldSurfaceForRecycling( + AnimationImageKeyData& aEntry, SourceSurfaceSharedData* aSurface) { + if (aSurface->GetType() != SurfaceType::DATA_RECYCLING_SHARED) { + return; + } + + MOZ_ASSERT(StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()); + aEntry.mPendingRelease.AppendElement(aSurface); +} + +nsresult SharedSurfacesAnimation::SetCurrentFrame( + SourceSurfaceSharedData* aSurface, const gfx::IntRect& aDirtyRect) { + MOZ_ASSERT(aSurface); + + SharedSurfacesChild::SharedUserData* data = nullptr; + nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data); + if (NS_FAILED(rv)) { + return rv; + } + + MOZ_ASSERT(data); + mId = data->Id(); + + auto i = mKeys.Length(); + while (i > 0) { + --i; + AnimationImageKeyData& entry = mKeys[i]; + MOZ_ASSERT(!entry.mManager->IsDestroyed()); + + entry.MergeDirtyRect(Some(aDirtyRect)); + Maybe<IntRect> dirtyRect = entry.TakeDirtyRect(); + if (dirtyRect) { + HoldSurfaceForRecycling(entry, aSurface); + auto& resourceUpdates = entry.mManager->AsyncResourceUpdates(); + resourceUpdates.UpdateSharedExternalImage( + mId, entry.mImageKey, ViewAs<ImagePixel>(dirtyRect.ref())); + } + } + + return NS_OK; +} + +nsresult SharedSurfacesAnimation::UpdateKey( + SourceSurfaceSharedData* aSurface, RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) { + SharedSurfacesChild::SharedUserData* data = nullptr; + nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data); + if (NS_FAILED(rv)) { + return rv; + } + + MOZ_ASSERT(data); + if (wr::AsUint64(mId) != wr::AsUint64(data->Id())) { + mKeys.Clear(); + mId = data->Id(); + } + + // We iterate through all of the items to ensure we clean up the old + // RenderRootStateManager references. Most of the time there will be few + // entries and this should not be particularly expensive compared to the + // cost of duplicating image keys. In an ideal world, we would generate a + // single key for the surface, and it would be usable on all of the + // renderer instances. For now, we must allocate a key for each WR bridge. + bool found = false; + auto i = mKeys.Length(); + while (i > 0) { + --i; + AnimationImageKeyData& entry = mKeys[i]; + MOZ_ASSERT(!entry.mManager->IsDestroyed()); + if (entry.mManager == aManager) { + WebRenderBridgeChild* wrBridge = aManager->WrBridge(); + MOZ_ASSERT(wrBridge); + + // Even if the manager is the same, its underlying WebRenderBridgeChild + // can change state. If our namespace differs, then our old key has + // already been discarded. + bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace; + if (!ownsKey) { + entry.mImageKey = wrBridge->GetNextImageKey(); + HoldSurfaceForRecycling(entry, aSurface); + aResources.AddSharedExternalImage(mId, entry.mImageKey); + } else { + MOZ_ASSERT(entry.mDirtyRect.isNothing()); + } + + aKey = entry.mImageKey; + found = true; + break; + } + } + + if (!found) { + aKey = aManager->WrBridge()->GetNextImageKey(); + if (StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()) { + aManager->RegisterAsyncAnimation(aKey, this); + } + + AnimationImageKeyData data(aManager, aKey); + HoldSurfaceForRecycling(data, aSurface); + mKeys.AppendElement(std::move(data)); + aResources.AddSharedExternalImage(mId, aKey); + } + + return NS_OK; +} + +void SharedSurfacesAnimation::ReleasePreviousFrame( + RenderRootStateManager* aManager, const wr::ExternalImageId& aId) { + MOZ_ASSERT(aManager); + + auto i = mKeys.Length(); + while (i > 0) { + --i; + AnimationImageKeyData& entry = mKeys[i]; + MOZ_ASSERT(!entry.mManager->IsDestroyed()); + if (entry.mManager == aManager) { + size_t k; + for (k = 0; k < entry.mPendingRelease.Length(); ++k) { + Maybe<wr::ExternalImageId> extId = + SharedSurfacesChild::GetExternalId(entry.mPendingRelease[k]); + if (extId && extId.ref() == aId) { + break; + } + } + + if (k == entry.mPendingRelease.Length()) { + continue; + } + + entry.mPendingRelease.RemoveElementsAt(0, k + 1); + break; + } + } +} + +void SharedSurfacesAnimation::Invalidate(RenderRootStateManager* aManager) { + auto i = mKeys.Length(); + while (i > 0) { + --i; + AnimationImageKeyData& entry = mKeys[i]; + if (entry.mManager == aManager) { + mKeys.RemoveElementAt(i); + break; + } + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/SharedSurfacesChild.h b/gfx/layers/ipc/SharedSurfacesChild.h new file mode 100644 index 0000000000..6fb40931fb --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesChild.h @@ -0,0 +1,250 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_GFX_SHAREDSURFACESCHILD_H +#define MOZILLA_GFX_SHAREDSURFACESCHILD_H + +#include <stdint.h> // for uint32_t, uint64_t +#include "mozilla/Attributes.h" // for override +#include "mozilla/Maybe.h" // for Maybe +#include "mozilla/RefPtr.h" // for already_AddRefed +#include "mozilla/StaticPtr.h" // for StaticRefPtr +#include "mozilla/gfx/UserData.h" // for UserDataKey +#include "mozilla/webrender/WebRenderTypes.h" // for wr::ImageKey +#include "nsTArray.h" // for AutoTArray +#include "nsThreadUtils.h" // for Runnable +#include "ImageTypes.h" // for ContainerProducerID + +namespace mozilla { +namespace layers { +class AnimationImageKeyData; +} // namespace layers +} // namespace mozilla + +template <> +struct nsTArray_RelocationStrategy<mozilla::layers::AnimationImageKeyData> { + typedef nsTArray_RelocateUsingMoveConstructor< + mozilla::layers::AnimationImageKeyData> + Type; +}; + +namespace mozilla { +namespace gfx { +class SourceSurface; +class SourceSurfaceSharedData; +} // namespace gfx + +namespace wr { +class IpcResourceUpdateQueue; +} // namespace wr + +namespace layers { + +class CompositorManagerChild; +class RenderRootStateManager; + +class SharedSurfacesChild { + public: + /** + * Request that the surface be mapped into the compositor thread's memory + * space. This is useful for when the caller itself has no present need for + * the surface to be mapped, but knows there will be such a need in the + * future. This may be called from any thread, but it may cause a dispatch to + * the main thread. + */ + static void Share(gfx::SourceSurfaceSharedData* aSurface); + + /** + * Request that the surface be mapped into the compositor thread's memory + * space, and a valid ExternalImageId be generated for it for use with + * WebRender. This must be called from the main thread. + */ + static nsresult Share(gfx::SourceSurface* aSurface, wr::ExternalImageId& aId); + + /** + * Request that the surface be mapped into the compositor thread's memory + * space, and a valid ImageKey be generated for it for use with WebRender. + * This must be called from the main thread. + */ + static nsresult Share(gfx::SourceSurfaceSharedData* aSurface, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::ImageKey& aKey); + + /** + * Request that the surface be mapped into the compositor thread's memory + * space, and a valid ImageKey be generated for it for use with WebRender. + * This must be called from the main thread. + */ + static nsresult Share(gfx::SourceSurface* aSurface, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::ImageKey& aKey); + + /** + * Get the external ID, if any, bound to the shared surface. Used for memory + * reporting purposes. + */ + static Maybe<wr::ExternalImageId> GetExternalId( + const gfx::SourceSurfaceSharedData* aSurface); + + /** + * Get the surface (or its underlying surface) as a SourceSurfaceSharedData + * pointer, if valid. + */ + static gfx::SourceSurfaceSharedData* AsSourceSurfaceSharedData( + gfx::SourceSurface* aSurface); + + class ImageKeyData { + public: + ImageKeyData(RenderRootStateManager* aManager, + const wr::ImageKey& aImageKey); + virtual ~ImageKeyData(); + + ImageKeyData(ImageKeyData&& aOther); + ImageKeyData& operator=(ImageKeyData&& aOther); + ImageKeyData(const ImageKeyData&) = delete; + ImageKeyData& operator=(const ImageKeyData&) = delete; + + void MergeDirtyRect(const Maybe<gfx::IntRect>& aDirtyRect); + + Maybe<gfx::IntRect> TakeDirtyRect() { return std::move(mDirtyRect); } + + RefPtr<RenderRootStateManager> mManager; + Maybe<gfx::IntRect> mDirtyRect; + wr::ImageKey mImageKey; + }; + + private: + SharedSurfacesChild() = delete; + ~SharedSurfacesChild() = delete; + + friend class SharedSurfacesAnimation; + + class SharedUserData final : public Runnable { + public: + explicit SharedUserData(const wr::ExternalImageId& aId); + virtual ~SharedUserData(); + + SharedUserData(const SharedUserData& aOther) = delete; + SharedUserData& operator=(const SharedUserData& aOther) = delete; + + SharedUserData(SharedUserData&& aOther) = delete; + SharedUserData& operator=(SharedUserData&& aOther) = delete; + + static void Destroy(void* aClosure); + + NS_IMETHOD Run() override; + + const wr::ExternalImageId& Id() const { return mId; } + + void SetId(const wr::ExternalImageId& aId) { + mId = aId; + mKeys.Clear(); + mShared = false; + } + + bool IsShared() const { return mShared; } + + void MarkShared() { + MOZ_ASSERT(!mShared); + mShared = true; + } + + wr::ImageKey UpdateKey(RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + const Maybe<gfx::IntRect>& aDirtyRect); + + protected: + AutoTArray<ImageKeyData, 1> mKeys; + wr::ExternalImageId mId; + bool mShared : 1; + }; + + static nsresult ShareInternal(gfx::SourceSurfaceSharedData* aSurface, + SharedUserData** aUserData); + + static void Unshare(const wr::ExternalImageId& aId, bool aReleaseId, + nsTArray<ImageKeyData>& aKeys); + + static void DestroySharedUserData(void* aClosure); + + static gfx::UserDataKey sSharedKey; +}; + +class AnimationImageKeyData final : public SharedSurfacesChild::ImageKeyData { + public: + AnimationImageKeyData(RenderRootStateManager* aManager, + const wr::ImageKey& aImageKey); + + virtual ~AnimationImageKeyData(); + + AnimationImageKeyData(AnimationImageKeyData&& aOther); + AnimationImageKeyData& operator=(AnimationImageKeyData&& aOther); + + AutoTArray<RefPtr<gfx::SourceSurfaceSharedData>, 2> mPendingRelease; +}; + +/** + * This helper class owns a single ImageKey which will map to different external + * image IDs representing different frames in an animation. + */ +class SharedSurfacesAnimation final { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedSurfacesAnimation) + + SharedSurfacesAnimation() = default; + + void Destroy(); + + /** + * Set the animation to display the given frame. + * @param aSurface The current frame. + * @param aDirtyRect Dirty rect representing the change between the new frame + * and the previous frame. We will request only the delta + * be reuploaded by WebRender. + */ + nsresult SetCurrentFrame(gfx::SourceSurfaceSharedData* aSurface, + const gfx::IntRect& aDirtyRect); + + /** + * Generate an ImageKey for the given frame. + * @param aSurface The current frame. This should match what was cached via + * SetCurrentFrame, but if it does not, it will need to + * regenerate the cached ImageKey. + */ + nsresult UpdateKey(gfx::SourceSurfaceSharedData* aSurface, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::ImageKey& aKey); + + /** + * Release our reference to all frames up to and including the frame which + * has an external image ID which matches aId. + */ + void ReleasePreviousFrame(RenderRootStateManager* aManager, + const wr::ExternalImageId& aId); + + /** + * Destroy any state information bound for the given layer manager. Any + * image keys are already invalid. + */ + void Invalidate(RenderRootStateManager* aManager); + + private: + ~SharedSurfacesAnimation(); + + void HoldSurfaceForRecycling(AnimationImageKeyData& aEntry, + gfx::SourceSurfaceSharedData* aSurface); + + AutoTArray<AnimationImageKeyData, 1> mKeys; + wr::ExternalImageId mId; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/SharedSurfacesMemoryReport.h b/gfx/layers/ipc/SharedSurfacesMemoryReport.h new file mode 100644 index 0000000000..81baf1349f --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesMemoryReport.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_GFX_SHAREDSURFACESMEMORYREPORT_H +#define MOZILLA_GFX_SHAREDSURFACESMEMORYREPORT_H + +#include <cstdint> // for uint32_t +#include <unordered_map> +#include "base/process.h" +#include "ipc/IPCMessageUtils.h" +#include "ipc/IPCMessageUtilsSpecializations.h" +#include "mozilla/gfx/Point.h" // for IntSize + +namespace mozilla { +namespace layers { + +class SharedSurfacesMemoryReport final { + public: + class SurfaceEntry final { + public: + base::ProcessId mCreatorPid; + gfx::IntSize mSize; + int32_t mStride; + uint32_t mConsumers; + bool mCreatorRef; + }; + + std::unordered_map<uint64_t, SurfaceEntry> mSurfaces; +}; + +} // namespace layers +} // namespace mozilla + +namespace IPC { + +template <> +struct ParamTraits<mozilla::layers::SharedSurfacesMemoryReport> { + typedef mozilla::layers::SharedSurfacesMemoryReport paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mSurfaces); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mSurfaces); + } +}; + +template <> +struct ParamTraits<mozilla::layers::SharedSurfacesMemoryReport::SurfaceEntry> + : public PlainOldDataSerializer< + mozilla::layers::SharedSurfacesMemoryReport::SurfaceEntry> {}; + +} // namespace IPC + +#endif diff --git a/gfx/layers/ipc/SharedSurfacesParent.cpp b/gfx/layers/ipc/SharedSurfacesParent.cpp new file mode 100644 index 0000000000..73d985db4c --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesParent.cpp @@ -0,0 +1,385 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "SharedSurfacesParent.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/StaticPrefs_image.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/layers/SharedSurfacesMemoryReport.h" +#include "mozilla/layers/SourceSurfaceSharedData.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/webrender/RenderSharedSurfaceTextureHost.h" +#include "mozilla/webrender/RenderThread.h" +#include "nsThreadUtils.h" // for GetCurrentSerialEventTarget + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +StaticMonitor SharedSurfacesParent::sMonitor; +StaticAutoPtr<SharedSurfacesParent> SharedSurfacesParent::sInstance; + +// Short wait to allow for a surface to be added, where the consumer has a +// different thread route. +static const TimeDuration kGetTimeout = TimeDuration::FromMilliseconds(50); + +void SharedSurfacesParent::MappingTracker::NotifyExpiredLocked( + SourceSurfaceSharedDataWrapper* aSurface, + const StaticMonitorAutoLock& aAutoLock) { + RemoveObjectLocked(aSurface, aAutoLock); + mExpired.AppendElement(aSurface); +} + +void SharedSurfacesParent::MappingTracker::TakeExpired( + nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired, + const StaticMonitorAutoLock& aAutoLock) { + aExpired = std::move(mExpired); +} + +void SharedSurfacesParent::MappingTracker::NotifyHandlerEnd() { + nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>> expired; + { + StaticMonitorAutoLock lock(sMonitor); + TakeExpired(expired, lock); + } + + SharedSurfacesParent::ExpireMap(expired); +} + +SharedSurfacesParent::SharedSurfacesParent() + : mTracker( + StaticPrefs::image_mem_shared_unmap_min_expiration_ms_AtStartup(), + mozilla::GetCurrentSerialEventTarget()) {} + +/* static */ +void SharedSurfacesParent::Initialize() { + MOZ_ASSERT(NS_IsMainThread()); + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + sInstance = new SharedSurfacesParent(); + } +} + +/* static */ +void SharedSurfacesParent::ShutdownRenderThread() { + // The main thread should blocked on waiting for the render thread to + // complete so this should be safe to release off the main thread. + MOZ_ASSERT(wr::RenderThread::IsInRenderThread()); + StaticMonitorAutoLock lock(sMonitor); + MOZ_ASSERT(sInstance); + + for (const auto& key : sInstance->mSurfaces.Keys()) { + // There may be lingering consumers of the surfaces that didn't get shutdown + // yet but since we are here, we know the render thread is finished and we + // can unregister everything. + wr::RenderThread::Get()->UnregisterExternalImageDuringShutdown( + wr::ToExternalImageId(key)); + } +} + +/* static */ +void SharedSurfacesParent::Shutdown() { + // The compositor thread and render threads are shutdown, so this is the last + // thread that could use it. The expiration tracker needs to be freed on the + // main thread. + MOZ_ASSERT(NS_IsMainThread()); + StaticMonitorAutoLock lock(sMonitor); + sInstance = nullptr; +} + +/* static */ +already_AddRefed<DataSourceSurface> SharedSurfacesParent::Get( + const wr::ExternalImageId& aId) { + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd"; + return nullptr; + } + + RefPtr<SourceSurfaceSharedDataWrapper> surface; + while ( + !sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface))) { + CVStatus status = lock.Wait(kGetTimeout); + if (status == CVStatus::Timeout) { + return nullptr; + } + } + return surface.forget(); +} + +/* static */ +already_AddRefed<DataSourceSurface> SharedSurfacesParent::Acquire( + const wr::ExternalImageId& aId) { + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + gfxCriticalNote << "SSP:Acq " << wr::AsUint64(aId) << " shtd"; + return nullptr; + } + + RefPtr<SourceSurfaceSharedDataWrapper> surface; + sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); + + if (surface) { + DebugOnly<bool> rv = surface->AddConsumer(); + MOZ_ASSERT(!rv); + } + return surface.forget(); +} + +/* static */ +bool SharedSurfacesParent::Release(const wr::ExternalImageId& aId, + bool aForCreator) { + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + return false; + } + + uint64_t id = wr::AsUint64(aId); + RefPtr<SourceSurfaceSharedDataWrapper> surface; + sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); + if (!surface) { + return false; + } + + if (surface->RemoveConsumer(aForCreator)) { + RemoveTrackingLocked(surface, lock); + wr::RenderThread::Get()->UnregisterExternalImage(wr::ToExternalImageId(id)); + sInstance->mSurfaces.Remove(id); + } + + return true; +} + +/* static */ +void SharedSurfacesParent::AddSameProcess(const wr::ExternalImageId& aId, + SourceSurfaceSharedData* aSurface) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + gfxCriticalNote << "SSP:Ads " << wr::AsUint64(aId) << " shtd"; + return; + } + + // If the child bridge detects it is in the combined UI/GPU process, then it + // will insert a wrapper surface holding the shared memory buffer directly. + // This is good because we avoid mapping the same shared memory twice, but + // still allow the original surface to be freed and remove the wrapper from + // the table when it is no longer needed. + RefPtr<SourceSurfaceSharedDataWrapper> surface = + new SourceSurfaceSharedDataWrapper(); + surface->Init(aSurface); + + uint64_t id = wr::AsUint64(aId); + MOZ_ASSERT(!sInstance->mSurfaces.Contains(id)); + + auto texture = MakeRefPtr<wr::RenderSharedSurfaceTextureHost>(surface); + wr::RenderThread::Get()->RegisterExternalImage(aId, texture.forget()); + + surface->AddConsumer(); + sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface)); + lock.NotifyAll(); +} + +/* static */ +void SharedSurfacesParent::DestroyProcess(base::ProcessId aPid) { + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + return; + } + + // Note that the destruction of a parent may not be cheap if it still has a + // lot of surfaces still bound that require unmapping. + for (auto i = sInstance->mSurfaces.Iter(); !i.Done(); i.Next()) { + SourceSurfaceSharedDataWrapper* surface = i.Data(); + if (surface->GetCreatorPid() == aPid && surface->HasCreatorRef() && + surface->RemoveConsumer(/* aForCreator */ true)) { + RemoveTrackingLocked(surface, lock); + wr::RenderThread::Get()->UnregisterExternalImage( + wr::ToExternalImageId(i.Key())); + i.Remove(); + } + } +} + +/* static */ +void SharedSurfacesParent::Add(const wr::ExternalImageId& aId, + SurfaceDescriptorShared&& aDesc, + base::ProcessId aPid) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(aPid != base::GetCurrentProcId()); + + RefPtr<SourceSurfaceSharedDataWrapper> surface = + new SourceSurfaceSharedDataWrapper(); + + // We preferentially map in new surfaces when they are initially received + // because we are likely to reference them in a display list soon. The unmap + // will ensure we add the surface to the expiration tracker. We do it outside + // the mutex to ensure we always lock the surface mutex first, and our mutex + // second, to avoid deadlock. + // + // Note that the surface wrapper maps in the given handle as read only. + surface->Init(aDesc.size(), aDesc.stride(), aDesc.format(), + std::move(aDesc.handle()), aPid); + + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " shtd"; + return; + } + + uint64_t id = wr::AsUint64(aId); + MOZ_ASSERT(!sInstance->mSurfaces.Contains(id)); + + auto texture = MakeRefPtr<wr::RenderSharedSurfaceTextureHost>(surface); + wr::RenderThread::Get()->RegisterExternalImage(aId, texture.forget()); + + surface->AddConsumer(); + sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface)); + lock.NotifyAll(); +} + +/* static */ +void SharedSurfacesParent::Remove(const wr::ExternalImageId& aId) { + DebugOnly<bool> rv = Release(aId, /* aForCreator */ true); + MOZ_ASSERT(rv); +} + +/* static */ +void SharedSurfacesParent::AddTrackingLocked( + SourceSurfaceSharedDataWrapper* aSurface, + const StaticMonitorAutoLock& aAutoLock) { + MOZ_ASSERT(!aSurface->GetExpirationState()->IsTracked()); + sInstance->mTracker.AddObjectLocked(aSurface, aAutoLock); +} + +/* static */ +void SharedSurfacesParent::AddTracking( + SourceSurfaceSharedDataWrapper* aSurface) { + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + return; + } + + AddTrackingLocked(aSurface, lock); +} + +/* static */ +void SharedSurfacesParent::RemoveTrackingLocked( + SourceSurfaceSharedDataWrapper* aSurface, + const StaticMonitorAutoLock& aAutoLock) { + if (!aSurface->GetExpirationState()->IsTracked()) { + return; + } + + sInstance->mTracker.RemoveObjectLocked(aSurface, aAutoLock); +} + +/* static */ +void SharedSurfacesParent::RemoveTracking( + SourceSurfaceSharedDataWrapper* aSurface) { + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + return; + } + + RemoveTrackingLocked(aSurface, lock); +} + +/* static */ +bool SharedSurfacesParent::AgeOneGenerationLocked( + nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired, + const StaticMonitorAutoLock& aAutoLock) { + if (sInstance->mTracker.IsEmptyLocked(aAutoLock)) { + return false; + } + + sInstance->mTracker.AgeOneGenerationLocked(aAutoLock); + sInstance->mTracker.TakeExpired(aExpired, aAutoLock); + return true; +} + +/* static */ +bool SharedSurfacesParent::AgeOneGeneration( + nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired) { + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + return false; + } + + return AgeOneGenerationLocked(aExpired, lock); +} + +/* static */ +bool SharedSurfacesParent::AgeAndExpireOneGeneration() { + nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>> expired; + bool aged = AgeOneGeneration(expired); + ExpireMap(expired); + return aged; +} + +/* static */ +void SharedSurfacesParent::ExpireMap( + nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired) { + for (auto& surface : aExpired) { + surface->ExpireMap(); + } +} + +/* static */ +void SharedSurfacesParent::AccumulateMemoryReport( + base::ProcessId aPid, SharedSurfacesMemoryReport& aReport) { + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + return; + } + + for (const auto& entry : sInstance->mSurfaces) { + SourceSurfaceSharedDataWrapper* surface = entry.GetData(); + if (surface->GetCreatorPid() == aPid) { + aReport.mSurfaces.insert(std::make_pair( + entry.GetKey(), + SharedSurfacesMemoryReport::SurfaceEntry{ + aPid, surface->GetSize(), surface->Stride(), + surface->GetConsumers(), surface->HasCreatorRef()})); + } + } +} + +/* static */ +bool SharedSurfacesParent::AccumulateMemoryReport( + SharedSurfacesMemoryReport& aReport) { + if (XRE_IsParentProcess()) { + GPUProcessManager* gpm = GPUProcessManager::Get(); + if (!gpm || gpm->GPUProcessPid() != base::kInvalidProcessId) { + return false; + } + } else if (!XRE_IsGPUProcess()) { + return false; + } + + StaticMonitorAutoLock lock(sMonitor); + if (!sInstance) { + return true; + } + + for (const auto& entry : sInstance->mSurfaces) { + SourceSurfaceSharedDataWrapper* surface = entry.GetData(); + aReport.mSurfaces.insert(std::make_pair( + entry.GetKey(), + SharedSurfacesMemoryReport::SurfaceEntry{ + surface->GetCreatorPid(), surface->GetSize(), surface->Stride(), + surface->GetConsumers(), surface->HasCreatorRef()})); + } + + return true; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/SharedSurfacesParent.h b/gfx/layers/ipc/SharedSurfacesParent.h new file mode 100644 index 0000000000..1ccc99b2f0 --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesParent.h @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_GFX_SHAREDSURFACESPARENT_H +#define MOZILLA_GFX_SHAREDSURFACESPARENT_H + +#include <stdint.h> // for uint32_t +#include "mozilla/Attributes.h" // for override +#include "mozilla/StaticMonitor.h" // for StaticMutex +#include "mozilla/StaticPtr.h" // for StaticAutoPtr +#include "mozilla/RefPtr.h" // for already_AddRefed +#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc +#include "mozilla/gfx/2D.h" // for SurfaceFormat +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptorShared +#include "mozilla/layers/SourceSurfaceSharedData.h" +#include "mozilla/webrender/WebRenderTypes.h" // for wr::ExternalImageId +#include "nsExpirationTracker.h" +#include "nsRefPtrHashtable.h" + +namespace mozilla { +namespace gfx { +class DataSourceSurface; +} // namespace gfx + +namespace layers { + +class SharedSurfacesChild; +class SharedSurfacesMemoryReport; + +class SharedSurfacesParent final { + public: + static void Initialize(); + static void ShutdownRenderThread(); + static void Shutdown(); + + // Get without increasing the consumer count. + static already_AddRefed<gfx::DataSourceSurface> Get( + const wr::ExternalImageId& aId); + + // Get but also increase the consumer count. Must call Release after finished. + static already_AddRefed<gfx::DataSourceSurface> Acquire( + const wr::ExternalImageId& aId); + + static bool Release(const wr::ExternalImageId& aId, bool aForCreator = false); + + static void Add(const wr::ExternalImageId& aId, + SurfaceDescriptorShared&& aDesc, base::ProcessId aPid); + + static void Remove(const wr::ExternalImageId& aId); + + static void DestroyProcess(base::ProcessId aPid); + + static void AccumulateMemoryReport(base::ProcessId aPid, + SharedSurfacesMemoryReport& aReport); + + static bool AccumulateMemoryReport(SharedSurfacesMemoryReport& aReport); + + static void AddTracking(gfx::SourceSurfaceSharedDataWrapper* aSurface); + + static void RemoveTracking(gfx::SourceSurfaceSharedDataWrapper* aSurface); + + static bool AgeOneGeneration( + nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired); + + static bool AgeAndExpireOneGeneration(); + + private: + friend class SharedSurfacesChild; + friend class gfx::SourceSurfaceSharedDataWrapper; + + SharedSurfacesParent(); + + static void AddSameProcess(const wr::ExternalImageId& aId, + gfx::SourceSurfaceSharedData* aSurface); + + static void AddTrackingLocked(gfx::SourceSurfaceSharedDataWrapper* aSurface, + const StaticMonitorAutoLock& aAutoLock); + + static void RemoveTrackingLocked( + gfx::SourceSurfaceSharedDataWrapper* aSurface, + const StaticMonitorAutoLock& aAutoLock); + + static bool AgeOneGenerationLocked( + nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired, + const StaticMonitorAutoLock& aAutoLock); + + static void ExpireMap( + nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired); + + static StaticMonitor sMonitor MOZ_UNANNOTATED; + + static StaticAutoPtr<SharedSurfacesParent> sInstance; + + nsRefPtrHashtable<nsUint64HashKey, gfx::SourceSurfaceSharedDataWrapper> + mSurfaces; + + class MappingTracker final + : public ExpirationTrackerImpl<gfx::SourceSurfaceSharedDataWrapper, 4, + StaticMonitor, StaticMonitorAutoLock> { + public: + explicit MappingTracker(uint32_t aExpirationTimeoutMS, + nsIEventTarget* aEventTarget) + : ExpirationTrackerImpl<gfx::SourceSurfaceSharedDataWrapper, 4, + StaticMonitor, StaticMonitorAutoLock>( + aExpirationTimeoutMS, "SharedMappingTracker", aEventTarget) {} + + void TakeExpired( + nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired, + const StaticMonitorAutoLock& aAutoLock); + + protected: + void NotifyExpiredLocked(gfx::SourceSurfaceSharedDataWrapper* aSurface, + const StaticMonitorAutoLock& aAutoLock) override; + + void NotifyHandlerEndLocked( + const StaticMonitorAutoLock& aAutoLock) override {} + + void NotifyHandlerEnd() override; + + StaticMonitor& GetMutex() override { return sMonitor; } + + nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>> mExpired; + }; + + MappingTracker mTracker; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/SurfaceDescriptor.h b/gfx/layers/ipc/SurfaceDescriptor.h new file mode 100644 index 0000000000..6d4c2d3271 --- /dev/null +++ b/gfx/layers/ipc/SurfaceDescriptor.h @@ -0,0 +1,14 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef IPC_SurfaceDescriptor_h +#define IPC_SurfaceDescriptor_h + +#if defined(XP_MACOSX) +# define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS +#endif + +#endif // IPC_SurfaceDescriptor_h diff --git a/gfx/layers/ipc/SynchronousTask.h b/gfx/layers/ipc/SynchronousTask.h new file mode 100644 index 0000000000..06f14caafc --- /dev/null +++ b/gfx/layers/ipc/SynchronousTask.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_GFX_SYNCHRONOUSTASK_H +#define MOZILLA_GFX_SYNCHRONOUSTASK_H + +#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc + +namespace mozilla { +namespace layers { + +// Helper that creates a monitor and a "done" flag, then enters the monitor. +class MOZ_STACK_CLASS SynchronousTask { + friend class AutoCompleteTask; + + public: + explicit SynchronousTask(const char* name) + : mMonitor(name), mAutoEnter(mMonitor), mDone(false) {} + + nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT) { + // For indefinite timeouts, wait in a while loop to handle spurious + // wakeups. + while (aInterval == PR_INTERVAL_NO_TIMEOUT && !mDone) { + mMonitor.Wait(); + } + + // For finite timeouts, we only check once for completion, and otherwise + // rely on the ReentrantMonitor to manage the interval. If the monitor + // returns too early, we'll never know, but we can check if the mDone + // flag was set to true, indicating that the task finished successfully. + if (!mDone) { + // We ignore the return value from ReentrantMonitor::Wait, because it's + // always NS_OK, even in the case of timeout. + mMonitor.Wait(aInterval); + + if (!mDone) { + return NS_ERROR_ABORT; + } + } + + return NS_OK; + } + + private: + void Complete() { + mDone = true; + mMonitor.NotifyAll(); + } + + private: + ReentrantMonitor mMonitor MOZ_UNANNOTATED; + ReentrantMonitorAutoEnter mAutoEnter; + bool mDone; +}; + +class MOZ_STACK_CLASS AutoCompleteTask final { + public: + explicit AutoCompleteTask(SynchronousTask* aTask) + : mTask(aTask), mAutoEnter(aTask->mMonitor) {} + ~AutoCompleteTask() { mTask->Complete(); } + + private: + SynchronousTask* mTask; + ReentrantMonitorAutoEnter mAutoEnter; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/TextureForwarder.h b/gfx/layers/ipc/TextureForwarder.h new file mode 100644 index 0000000000..af84390143 --- /dev/null +++ b/gfx/layers/ipc/TextureForwarder.h @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_LAYERS_TEXTUREFORWARDER +#define MOZILLA_LAYERS_TEXTUREFORWARDER + +#include <stdint.h> // for int32_t, uint64_t +#include "gfxTypes.h" +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/layers/LayersMessages.h" // for Edit, etc +#include "mozilla/layers/LayersTypes.h" // for LayersBackend +#include "mozilla/layers/TextureClient.h" // for TextureClient +#include "mozilla/layers/KnowsCompositor.h" +#include "nsISerialEventTarget.h" + +namespace mozilla { +namespace layers { +class CanvasChild; + +/** + * An abstract interface for classes that implement the autogenerated + * IPDL actor class. Lets us check if they are still valid for IPC. + */ +class LayersIPCActor { + public: + virtual bool IPCOpen() const { return true; } +}; + +/** + * An abstract interface for LayersIPCActors that implement a top-level + * IPDL protocol so also have their own channel. + * Has their own MessageLoop for message dispatch, and can allocate + * shmem. + */ +class LayersIPCChannel : public LayersIPCActor, + public mozilla::ipc::IShmemAllocator { + public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + virtual bool IsSameProcess() const = 0; + + virtual bool UsesImageBridge() const { return false; } + + virtual base::ProcessId GetParentPid() const = 0; + + virtual nsISerialEventTarget* GetThread() const = 0; + + virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() { + return nullptr; + } + + virtual void CancelWaitForNotifyNotUsed(uint64_t aTextureId) = 0; + + virtual wr::MaybeExternalImageId GetNextExternalImageId() { + return Nothing(); + } + + protected: + virtual ~LayersIPCChannel() = default; +}; + +/** + * An abstract interface for classes that can allocate PTexture objects + * across IPDL. Currently a sub-class of LayersIPCChannel for simplicity + * since all our implementations use both, but could be independant if needed. + */ +class TextureForwarder : public LayersIPCChannel { + public: + /** + * Create a TextureChild/Parent pair as as well as the TextureHost on the + * parent side. + */ + virtual PTextureChild* CreateTexture( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, + const dom::ContentParentId& aContentId, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId) = 0; + + /** + * Returns the CanvasChild for this TextureForwarder. + */ + virtual already_AddRefed<CanvasChild> GetCanvasChild() { return nullptr; }; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/UiCompositorControllerChild.cpp b/gfx/layers/ipc/UiCompositorControllerChild.cpp new file mode 100644 index 0000000000..c1bf49bc27 --- /dev/null +++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp @@ -0,0 +1,350 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/layers/UiCompositorControllerChild.h" + +#include "mozilla/dom/ContentChild.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/SynchronousTask.h" +#include "mozilla/layers/UiCompositorControllerMessageTypes.h" +#include "mozilla/layers/UiCompositorControllerParent.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/StaticPtr.h" +#include "nsBaseWidget.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" + +#if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/widget/AndroidUiThread.h" + +static RefPtr<nsThread> GetUiThread() { return mozilla::GetAndroidUiThread(); } +#else +static RefPtr<nsThread> GetUiThread() { + MOZ_CRASH("Platform does not support UiCompositorController"); + return nullptr; +} +#endif // defined(MOZ_WIDGET_ANDROID) + +namespace mozilla { +namespace layers { + +// public: +/* static */ +RefPtr<UiCompositorControllerChild> +UiCompositorControllerChild::CreateForSameProcess( + const LayersId& aRootLayerTreeId, nsBaseWidget* aWidget) { + RefPtr<UiCompositorControllerChild> child = + new UiCompositorControllerChild(0, aWidget); + child->mParent = new UiCompositorControllerParent(aRootLayerTreeId); + GetUiThread()->Dispatch( + NewRunnableMethod( + "layers::UiCompositorControllerChild::OpenForSameProcess", child, + &UiCompositorControllerChild::OpenForSameProcess), + nsIThread::DISPATCH_NORMAL); + return child; +} + +/* static */ +RefPtr<UiCompositorControllerChild> +UiCompositorControllerChild::CreateForGPUProcess( + const uint64_t& aProcessToken, + Endpoint<PUiCompositorControllerChild>&& aEndpoint, nsBaseWidget* aWidget) { + RefPtr<UiCompositorControllerChild> child = + new UiCompositorControllerChild(aProcessToken, aWidget); + + RefPtr<nsIRunnable> task = + NewRunnableMethod<Endpoint<PUiCompositorControllerChild>&&>( + "layers::UiCompositorControllerChild::OpenForGPUProcess", child, + &UiCompositorControllerChild::OpenForGPUProcess, + std::move(aEndpoint)); + + GetUiThread()->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL); + return child; +} + +bool UiCompositorControllerChild::Pause() { + if (!mIsOpen) { + return false; + } + return SendPause(); +} + +bool UiCompositorControllerChild::Resume() { + if (!mIsOpen) { + return false; + } + bool resumed = false; + return SendResume(&resumed) && resumed; +} + +bool UiCompositorControllerChild::ResumeAndResize(const int32_t& aX, + const int32_t& aY, + const int32_t& aWidth, + const int32_t& aHeight) { + if (!mIsOpen) { + mResize = Some(gfx::IntRect(aX, aY, aWidth, aHeight)); + // Since we are caching these values, pretend the call succeeded. + return true; + } + bool resumed = false; + return SendResumeAndResize(aX, aY, aWidth, aHeight, &resumed) && resumed; +} + +bool UiCompositorControllerChild::InvalidateAndRender() { + if (!mIsOpen) { + return false; + } + return SendInvalidateAndRender(); +} + +bool UiCompositorControllerChild::SetMaxToolbarHeight(const int32_t& aHeight) { + if (!mIsOpen) { + mMaxToolbarHeight = Some(aHeight); + // Since we are caching this value, pretend the call succeeded. + return true; + } + return SendMaxToolbarHeight(aHeight); +} + +bool UiCompositorControllerChild::SetFixedBottomOffset(int32_t aOffset) { + return SendFixedBottomOffset(aOffset); +} + +bool UiCompositorControllerChild::ToolbarAnimatorMessageFromUI( + const int32_t& aMessage) { + if (!mIsOpen) { + return false; + } + + if (aMessage == IS_COMPOSITOR_CONTROLLER_OPEN) { + RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN); + } + + return true; +} + +bool UiCompositorControllerChild::SetDefaultClearColor(const uint32_t& aColor) { + if (!mIsOpen) { + mDefaultClearColor = Some(aColor); + // Since we are caching this value, pretend the call succeeded. + return true; + } + + return SendDefaultClearColor(aColor); +} + +bool UiCompositorControllerChild::RequestScreenPixels() { + if (!mIsOpen) { + return false; + } + + return SendRequestScreenPixels(); +} + +bool UiCompositorControllerChild::EnableLayerUpdateNotifications( + const bool& aEnable) { + if (!mIsOpen) { + mLayerUpdateEnabled = Some(aEnable); + // Since we are caching this value, pretend the call succeeded. + return true; + } + + return SendEnableLayerUpdateNotifications(aEnable); +} + +void UiCompositorControllerChild::Destroy() { + MOZ_ASSERT(NS_IsMainThread()); + + layers::SynchronousTask task("UiCompositorControllerChild::Destroy"); + GetUiThread()->Dispatch(NS_NewRunnableFunction( + "layers::UiCompositorControllerChild::Destroy", [&]() { + MOZ_ASSERT(GetUiThread()->IsOnCurrentThread()); + AutoCompleteTask complete(&task); + + // Clear the process token so that we don't notify the GPUProcessManager + // about an abnormal shutdown, thereby tearing down the GPU process. + mProcessToken = 0; + + if (mWidget) { + // Dispatch mWidget to main thread to prevent it from being destructed + // by the ui thread. + RefPtr<nsIWidget> widget = std::move(mWidget); + NS_ReleaseOnMainThread("UiCompositorControllerChild::mWidget", + widget.forget()); + } + + if (mIsOpen) { + // Close the underlying IPC channel. + PUiCompositorControllerChild::Close(); + mIsOpen = false; + } + })); + + task.Wait(); +} + +bool UiCompositorControllerChild::DeallocPixelBuffer(Shmem& aMem) { + return DeallocShmem(aMem); +} + +// protected: +void UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy) { + mIsOpen = false; + mParent = nullptr; + + if (mProcessToken) { + gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); + mProcessToken = 0; + } +} + +void UiCompositorControllerChild::ProcessingError(Result aCode, + const char* aReason) { + if (aCode != MsgDropped) { + gfxDevCrash(gfx::LogReason::ProcessingError) + << "Processing error in UiCompositorControllerChild: " << int(aCode); + } +} + +void UiCompositorControllerChild::HandleFatalError(const char* aMsg) { + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); +} + +mozilla::ipc::IPCResult +UiCompositorControllerChild::RecvToolbarAnimatorMessageFromCompositor( + const int32_t& aMessage) { +#if defined(MOZ_WIDGET_ANDROID) + if (mWidget) { + mWidget->RecvToolbarAnimatorMessageFromCompositor(aMessage); + } +#endif // defined(MOZ_WIDGET_ANDROID) + + return IPC_OK(); +} + +mozilla::ipc::IPCResult UiCompositorControllerChild::RecvRootFrameMetrics( + const ScreenPoint& aScrollOffset, const CSSToScreenScale& aZoom) { +#if defined(MOZ_WIDGET_ANDROID) + if (mWidget) { + mWidget->UpdateRootFrameMetrics(aScrollOffset, aZoom); + } +#endif // defined(MOZ_WIDGET_ANDROID) + + return IPC_OK(); +} + +mozilla::ipc::IPCResult UiCompositorControllerChild::RecvScreenPixels( + ipc::Shmem&& aMem, const ScreenIntSize& aSize, bool aNeedsYFlip) { +#if defined(MOZ_WIDGET_ANDROID) + if (mWidget) { + mWidget->RecvScreenPixels(std::move(aMem), aSize, aNeedsYFlip); + } +#endif // defined(MOZ_WIDGET_ANDROID) + + return IPC_OK(); +} + +// private: +UiCompositorControllerChild::UiCompositorControllerChild( + const uint64_t& aProcessToken, nsBaseWidget* aWidget) + : mIsOpen(false), mProcessToken(aProcessToken), mWidget(aWidget) {} + +UiCompositorControllerChild::~UiCompositorControllerChild() = default; + +void UiCompositorControllerChild::OpenForSameProcess() { + MOZ_ASSERT(GetUiThread()->IsOnCurrentThread()); + + mIsOpen = Open(mParent, mozilla::layers::CompositorThread(), + mozilla::ipc::ChildSide); + + if (!mIsOpen) { + mParent = nullptr; + return; + } + + mParent->InitializeForSameProcess(); + SendCachedValues(); + // Let Ui thread know the connection is open; + RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN); +} + +void UiCompositorControllerChild::OpenForGPUProcess( + Endpoint<PUiCompositorControllerChild>&& aEndpoint) { + MOZ_ASSERT(GetUiThread()->IsOnCurrentThread()); + + mIsOpen = aEndpoint.Bind(this); + + if (!mIsOpen) { + // The GPU Process Manager might be gone if we receive ActorDestroy very + // late in shutdown. + if (gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get()) { + gpm->NotifyRemoteActorDestroyed(mProcessToken); + } + return; + } + + SendCachedValues(); + // Let Ui thread know the connection is open; + RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN); +} + +void UiCompositorControllerChild::SendCachedValues() { + MOZ_ASSERT(mIsOpen); + if (mResize) { + bool resumed; + SendResumeAndResize(mResize.ref().x, mResize.ref().y, mResize.ref().width, + mResize.ref().height, &resumed); + mResize.reset(); + } + if (mMaxToolbarHeight) { + SendMaxToolbarHeight(mMaxToolbarHeight.ref()); + mMaxToolbarHeight.reset(); + } + if (mDefaultClearColor) { + SendDefaultClearColor(mDefaultClearColor.ref()); + mDefaultClearColor.reset(); + } + if (mLayerUpdateEnabled) { + SendEnableLayerUpdateNotifications(mLayerUpdateEnabled.ref()); + mLayerUpdateEnabled.reset(); + } +} + +#ifdef MOZ_WIDGET_ANDROID +void UiCompositorControllerChild::SetCompositorSurfaceManager( + java::CompositorSurfaceManager::Param aCompositorSurfaceManager) { + MOZ_ASSERT(!mCompositorSurfaceManager, + "SetCompositorSurfaceManager must only be called once."); + MOZ_ASSERT(mProcessToken != 0, + "SetCompositorSurfaceManager must only be called for GPU process " + "controllers."); + mCompositorSurfaceManager = aCompositorSurfaceManager; +}; + +void UiCompositorControllerChild::OnCompositorSurfaceChanged( + int32_t aWidgetId, java::sdk::Surface::Param aSurface) { + // If mCompositorSurfaceManager is not set then there is no GPU process and + // we do not need to do anything. + if (mCompositorSurfaceManager == nullptr) { + return; + } + + nsresult result = + mCompositorSurfaceManager->OnSurfaceChanged(aWidgetId, aSurface); + + // If our remote binder has died then notify the GPU process manager. + if (NS_FAILED(result)) { + if (mProcessToken) { + gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); + mProcessToken = 0; + } + } +} +#endif + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/UiCompositorControllerChild.h b/gfx/layers/ipc/UiCompositorControllerChild.h new file mode 100644 index 0000000000..bf543ee6a3 --- /dev/null +++ b/gfx/layers/ipc/UiCompositorControllerChild.h @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ +#ifndef include_gfx_ipc_UiCompositorControllerChild_h +#define include_gfx_ipc_UiCompositorControllerChild_h + +#include "mozilla/layers/PUiCompositorControllerChild.h" + +#include "mozilla/gfx/2D.h" +#include "mozilla/Maybe.h" +#include "mozilla/ipc/Shmem.h" +#include "mozilla/layers/UiCompositorControllerParent.h" +#include "mozilla/RefPtr.h" +#include "nsThread.h" +#ifdef MOZ_WIDGET_ANDROID +# include "SurfaceTexture.h" +# include "mozilla/java/CompositorSurfaceManagerWrappers.h" +#endif + +class nsBaseWidget; + +namespace mozilla { +namespace layers { + +class UiCompositorControllerChild final + : protected PUiCompositorControllerChild { + friend class PUiCompositorControllerChild; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerChild, final) + + static RefPtr<UiCompositorControllerChild> CreateForSameProcess( + const LayersId& aRootLayerTreeId, nsBaseWidget* aWidget); + static RefPtr<UiCompositorControllerChild> CreateForGPUProcess( + const uint64_t& aProcessToken, + Endpoint<PUiCompositorControllerChild>&& aEndpoint, + nsBaseWidget* aWidget); + + bool Pause(); + bool Resume(); + bool ResumeAndResize(const int32_t& aX, const int32_t& aY, + const int32_t& aHeight, const int32_t& aWidth); + bool InvalidateAndRender(); + bool SetMaxToolbarHeight(const int32_t& aHeight); + bool SetFixedBottomOffset(int32_t aOffset); + bool ToolbarAnimatorMessageFromUI(const int32_t& aMessage); + bool SetDefaultClearColor(const uint32_t& aColor); + bool RequestScreenPixels(); + bool EnableLayerUpdateNotifications(const bool& aEnable); + + void Destroy(); + + bool DeallocPixelBuffer(Shmem& aMem); + +#ifdef MOZ_WIDGET_ANDROID + // Set mCompositorSurfaceManager. Must be called straight after initialization + // for GPU process controllers. Do not call for in-process controllers. This + // is separate from CreateForGPUProcess to avoid cluttering its declaration + // with JNI types. + void SetCompositorSurfaceManager( + java::CompositorSurfaceManager::Param aCompositorSurfaceManager); + + // Send a Surface to the GPU process that a given widget ID should be + // composited in to. If not using a GPU process this function does nothing, as + // the InProcessCompositorWidget can read the Surface directly from the + // widget. + // + // Note that this function does not actually use the PUiCompositorController + // IPDL protocol, and instead uses Android's binder IPC mechanism via + // mCompositorSurfaceManager. It can be called from any thread. + void OnCompositorSurfaceChanged(int32_t aWidgetId, + java::sdk::Surface::Param aSurface); +#endif + + protected: + void ActorDestroy(ActorDestroyReason aWhy) override; + void ProcessingError(Result aCode, const char* aReason) override; + void HandleFatalError(const char* aMsg) override; + mozilla::ipc::IPCResult RecvToolbarAnimatorMessageFromCompositor( + const int32_t& aMessage); + mozilla::ipc::IPCResult RecvRootFrameMetrics(const ScreenPoint& aScrollOffset, + const CSSToScreenScale& aZoom); + mozilla::ipc::IPCResult RecvScreenPixels(Shmem&& aMem, + const ScreenIntSize& aSize, + bool aNeedsYFlip); + + private: + explicit UiCompositorControllerChild(const uint64_t& aProcessToken, + nsBaseWidget* aWidget); + virtual ~UiCompositorControllerChild(); + void OpenForSameProcess(); + void OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint); + void SendCachedValues(); + + bool mIsOpen; + uint64_t mProcessToken; + Maybe<gfx::IntRect> mResize; + Maybe<int32_t> mMaxToolbarHeight; + Maybe<uint32_t> mDefaultClearColor; + Maybe<bool> mLayerUpdateEnabled; + RefPtr<nsBaseWidget> mWidget; + // Should only be set when compositor is in process. + RefPtr<UiCompositorControllerParent> mParent; + +#ifdef MOZ_WIDGET_ANDROID + // Android interface to send Surfaces to the GPU process. This uses Android + // binder rather than IPDL because Surfaces cannot be sent via IPDL. It lives + // here regardless because it is a conceptually logical location, even if the + // underlying IPC mechanism is different. + // This will be null if there is no GPU process. + mozilla::java::CompositorSurfaceManager::GlobalRef mCompositorSurfaceManager; +#endif +}; + +} // namespace layers +} // namespace mozilla + +#endif // include_gfx_ipc_UiCompositorControllerChild_h diff --git a/gfx/layers/ipc/UiCompositorControllerMessageTypes.h b/gfx/layers/ipc/UiCompositorControllerMessageTypes.h new file mode 100644 index 0000000000..3e68e7be31 --- /dev/null +++ b/gfx/layers/ipc/UiCompositorControllerMessageTypes.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef include_gfx_ipc_UiCompositorControllerMessageTypes_h +#define include_gfx_ipc_UiCompositorControllerMessageTypes_h + +namespace mozilla { +namespace layers { + +// +// NOTE: These values are also defined in +// mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java +// and must be kept in sync. Any new message added here must also be added +// there. +// + +// clang-format off +enum UiCompositorControllerMessageTypes { + FIRST_PAINT = 0, // Sent from compositor after first paint + LAYERS_UPDATED = 1, // Sent from the compositor when any layer has been updated + COMPOSITOR_CONTROLLER_OPEN = 2, // Compositor controller IPC is open + IS_COMPOSITOR_CONTROLLER_OPEN = 3 // Special message sent from controller to query if the compositor controller is open +}; +// clang-format on + +} // namespace layers +} // namespace mozilla + +#endif // include_gfx_ipc_UiCompositorControllerMessageTypes_h diff --git a/gfx/layers/ipc/UiCompositorControllerParent.cpp b/gfx/layers/ipc/UiCompositorControllerParent.cpp new file mode 100644 index 0000000000..1e34969e84 --- /dev/null +++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "UiCompositorControllerParent.h" + +#if defined(MOZ_WIDGET_ANDROID) +# include "apz/src/APZCTreeManager.h" +# include "mozilla/widget/AndroidCompositorWidget.h" +#endif +#include <utility> + +#include "FrameMetrics.h" +#include "SynchronousTask.h" +#include "mozilla/Unused.h" +#include "mozilla/gfx/Types.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/Compositor.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/UiCompositorControllerMessageTypes.h" +#include "mozilla/layers/WebRenderBridgeParent.h" + +namespace mozilla { +namespace layers { + +typedef CompositorBridgeParent::LayerTreeState LayerTreeState; + +/* static */ +RefPtr<UiCompositorControllerParent> +UiCompositorControllerParent::GetFromRootLayerTreeId( + const LayersId& aRootLayerTreeId) { + RefPtr<UiCompositorControllerParent> controller; + CompositorBridgeParent::CallWithIndirectShadowTree( + aRootLayerTreeId, [&](LayerTreeState& aState) -> void { + controller = aState.mUiControllerParent; + }); + return controller; +} + +/* static */ +RefPtr<UiCompositorControllerParent> UiCompositorControllerParent::Start( + const LayersId& aRootLayerTreeId, + Endpoint<PUiCompositorControllerParent>&& aEndpoint) { + RefPtr<UiCompositorControllerParent> parent = + new UiCompositorControllerParent(aRootLayerTreeId); + + RefPtr<Runnable> task = + NewRunnableMethod<Endpoint<PUiCompositorControllerParent>&&>( + "layers::UiCompositorControllerParent::Open", parent, + &UiCompositorControllerParent::Open, std::move(aEndpoint)); + CompositorThread()->Dispatch(task.forget()); + + return parent; +} + +mozilla::ipc::IPCResult UiCompositorControllerParent::RecvPause() { + CompositorBridgeParent* parent = + CompositorBridgeParent::GetCompositorBridgeParentFromLayersId( + mRootLayerTreeId); + if (parent) { + parent->PauseComposition(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult UiCompositorControllerParent::RecvResume( + bool* aOutResumed) { + *aOutResumed = false; + CompositorBridgeParent* parent = + CompositorBridgeParent::GetCompositorBridgeParentFromLayersId( + mRootLayerTreeId); + if (parent) { + *aOutResumed = parent->ResumeComposition(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult UiCompositorControllerParent::RecvResumeAndResize( + const int32_t& aX, const int32_t& aY, const int32_t& aWidth, + const int32_t& aHeight, bool* aOutResumed) { + *aOutResumed = false; + CompositorBridgeParent* parent = + CompositorBridgeParent::GetCompositorBridgeParentFromLayersId( + mRootLayerTreeId); + if (parent) { + // Front-end expects a first paint callback upon resume/resize. + parent->ForceIsFirstPaint(); +#if defined(MOZ_WIDGET_ANDROID) + parent->GetWidget()->AsAndroid()->NotifyClientSizeChanged( + LayoutDeviceIntSize(aWidth, aHeight)); +#endif + *aOutResumed = parent->ResumeCompositionAndResize(aX, aY, aWidth, aHeight); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult +UiCompositorControllerParent::RecvInvalidateAndRender() { + CompositorBridgeParent* parent = + CompositorBridgeParent::GetCompositorBridgeParentFromLayersId( + mRootLayerTreeId); + if (parent) { + parent->ScheduleComposition(wr::RenderReasons::OTHER); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult UiCompositorControllerParent::RecvMaxToolbarHeight( + const int32_t& aHeight) { + mMaxToolbarHeight = aHeight; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult UiCompositorControllerParent::RecvFixedBottomOffset( + const int32_t& aOffset) { +#if defined(MOZ_WIDGET_ANDROID) + CompositorBridgeParent* parent = + CompositorBridgeParent::GetCompositorBridgeParentFromLayersId( + mRootLayerTreeId); + if (parent) { + parent->SetFixedLayerMargins(0, aOffset); + } +#endif // defined(MOZ_WIDGET_ANDROID) + + return IPC_OK(); +} + +mozilla::ipc::IPCResult UiCompositorControllerParent::RecvDefaultClearColor( + const uint32_t& aColor) { + LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId); + + if (state && state->mWrBridge) { + state->mWrBridge->SetClearColor(gfx::DeviceColor::UnusualFromARGB(aColor)); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +UiCompositorControllerParent::RecvRequestScreenPixels() { +#if defined(MOZ_WIDGET_ANDROID) + LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId); + + if (state && state->mWrBridge) { + state->mWrBridge->RequestScreenPixels(this); + state->mWrBridge->ScheduleForcedGenerateFrame(wr::RenderReasons::OTHER); + } +#endif // defined(MOZ_WIDGET_ANDROID) + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +UiCompositorControllerParent::RecvEnableLayerUpdateNotifications( + const bool& aEnable) { +#if defined(MOZ_WIDGET_ANDROID) + // Layers updates are need by Robocop test which enables them + mCompositorLayersUpdateEnabled = aEnable; +#endif // defined(MOZ_WIDGET_ANDROID) + + return IPC_OK(); +} + +void UiCompositorControllerParent::ActorDestroy(ActorDestroyReason aWhy) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + Shutdown(); +} + +void UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor( + int32_t aMessage) { + // This function can be call from ether compositor or controller thread. + if (!CompositorThreadHolder::IsInCompositorThread()) { + CompositorThread()->Dispatch(NewRunnableMethod<int32_t>( + "layers::UiCompositorControllerParent::" + "ToolbarAnimatorMessageFromCompositor", + this, + &UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor, + aMessage)); + return; + } + + Unused << SendToolbarAnimatorMessageFromCompositor(aMessage); +} + +bool UiCompositorControllerParent::AllocPixelBuffer(const int32_t aSize, + ipc::Shmem* aMem) { + MOZ_ASSERT(aSize > 0); + return AllocShmem(aSize, aMem); +} + +void UiCompositorControllerParent::NotifyLayersUpdated() { +#ifdef MOZ_WIDGET_ANDROID + if (mCompositorLayersUpdateEnabled) { + ToolbarAnimatorMessageFromCompositor(LAYERS_UPDATED); + } +#endif +} + +void UiCompositorControllerParent::NotifyFirstPaint() { + ToolbarAnimatorMessageFromCompositor(FIRST_PAINT); +} + +void UiCompositorControllerParent::NotifyUpdateScreenMetrics( + const GeckoViewMetrics& aMetrics) { +#if defined(MOZ_WIDGET_ANDROID) + // TODO: Need to handle different x-and y-scales. + CSSToScreenScale scale = ViewTargetAs<ScreenPixel>( + aMetrics.mZoom, PixelCastJustification::ScreenIsParentLayerForRoot); + ScreenPoint scrollOffset = aMetrics.mVisualScrollOffset * scale; + CompositorThread()->Dispatch(NewRunnableMethod<ScreenPoint, CSSToScreenScale>( + "UiCompositorControllerParent::SendRootFrameMetrics", this, + &UiCompositorControllerParent::SendRootFrameMetrics, scrollOffset, + scale)); +#endif +} + +UiCompositorControllerParent::UiCompositorControllerParent( + const LayersId& aRootLayerTreeId) + : mRootLayerTreeId(aRootLayerTreeId) +#ifdef MOZ_WIDGET_ANDROID + , + mCompositorLayersUpdateEnabled(false) +#endif + , + mMaxToolbarHeight(0) { + MOZ_COUNT_CTOR(UiCompositorControllerParent); +} + +UiCompositorControllerParent::~UiCompositorControllerParent() { + MOZ_COUNT_DTOR(UiCompositorControllerParent); +} + +void UiCompositorControllerParent::InitializeForSameProcess() { + // This function is called by UiCompositorControllerChild in the main thread. + // So dispatch to the compositor thread to Initialize. + if (!CompositorThreadHolder::IsInCompositorThread()) { + SetOtherProcessId(base::GetCurrentProcId()); + SynchronousTask task( + "UiCompositorControllerParent::InitializeForSameProcess"); + + CompositorThread()->Dispatch(NS_NewRunnableFunction( + "UiCompositorControllerParent::InitializeForSameProcess", [&]() { + AutoCompleteTask complete(&task); + InitializeForSameProcess(); + })); + + task.Wait(); + return; + } + + Initialize(); +} + +void UiCompositorControllerParent::InitializeForOutOfProcess() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + Initialize(); +} + +void UiCompositorControllerParent::Initialize() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId); + MOZ_ASSERT(state); + MOZ_ASSERT(state->mParent); + if (!state || !state->mParent) { + return; + } + state->mUiControllerParent = this; +} + +void UiCompositorControllerParent::Open( + Endpoint<PUiCompositorControllerParent>&& aEndpoint) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (!aEndpoint.Bind(this)) { + // We can't recover from this. + MOZ_CRASH("Failed to bind UiCompositorControllerParent to endpoint"); + } + InitializeForOutOfProcess(); +} + +void UiCompositorControllerParent::Shutdown() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId); + if (state) { + state->mUiControllerParent = nullptr; + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/UiCompositorControllerParent.h b/gfx/layers/ipc/UiCompositorControllerParent.h new file mode 100644 index 0000000000..e6284e7dfb --- /dev/null +++ b/gfx/layers/ipc/UiCompositorControllerParent.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ +#ifndef include_gfx_ipc_UiCompositorControllerParent_h +#define include_gfx_ipc_UiCompositorControllerParent_h + +#include "mozilla/layers/PUiCompositorControllerParent.h" +#include "mozilla/layers/APZUtils.h" +#include "mozilla/ipc/Shmem.h" +#include "mozilla/RefPtr.h" + +namespace mozilla { +namespace layers { + +struct FrameMetrics; + +class UiCompositorControllerParent final + : public PUiCompositorControllerParent { + // UiCompositorControllerChild needs to call the private constructor when + // running in process. + friend class UiCompositorControllerChild; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerParent, final) + + static RefPtr<UiCompositorControllerParent> GetFromRootLayerTreeId( + const LayersId& aRootLayerTreeId); + static RefPtr<UiCompositorControllerParent> Start( + const LayersId& aRootLayerTreeId, + Endpoint<PUiCompositorControllerParent>&& aEndpoint); + + // PUiCompositorControllerParent functions + mozilla::ipc::IPCResult RecvPause(); + mozilla::ipc::IPCResult RecvResume(bool* aOutResumed); + mozilla::ipc::IPCResult RecvResumeAndResize(const int32_t& aX, + const int32_t& aY, + const int32_t& aHeight, + const int32_t& aWidth, + bool* aOutResumed); + mozilla::ipc::IPCResult RecvInvalidateAndRender(); + mozilla::ipc::IPCResult RecvMaxToolbarHeight(const int32_t& aHeight); + mozilla::ipc::IPCResult RecvFixedBottomOffset(const int32_t& aOffset); + mozilla::ipc::IPCResult RecvDefaultClearColor(const uint32_t& aColor); + mozilla::ipc::IPCResult RecvRequestScreenPixels(); + mozilla::ipc::IPCResult RecvEnableLayerUpdateNotifications( + const bool& aEnable); + void ActorDestroy(ActorDestroyReason aWhy) override; + + // Class specific functions + void ToolbarAnimatorMessageFromCompositor(int32_t aMessage); + bool AllocPixelBuffer(const int32_t aSize, Shmem* aMem); + + // Called when a layer has been updated so the UI thread may be notified if + // necessary. + void NotifyLayersUpdated(); + void NotifyFirstPaint(); + void NotifyUpdateScreenMetrics(const GeckoViewMetrics& aMetrics); + + private: + explicit UiCompositorControllerParent(const LayersId& aRootLayerTreeId); + virtual ~UiCompositorControllerParent(); + void InitializeForSameProcess(); + void InitializeForOutOfProcess(); + void Initialize(); + void Open(Endpoint<PUiCompositorControllerParent>&& aEndpoint); + void Shutdown(); + + LayersId mRootLayerTreeId; + +#if defined(MOZ_WIDGET_ANDROID) + bool mCompositorLayersUpdateEnabled; // Flag set to true when the UI thread + // is expecting to be notified when a + // layer has been updated +#endif // defined(MOZ_WIDGET_ANDROID) + + int32_t mMaxToolbarHeight; +}; + +} // namespace layers +} // namespace mozilla + +#endif // include_gfx_ipc_UiCompositorControllerParent_h diff --git a/gfx/layers/ipc/VideoBridgeChild.cpp b/gfx/layers/ipc/VideoBridgeChild.cpp new file mode 100644 index 0000000000..9d956013a0 --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeChild.cpp @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "VideoBridgeChild.h" +#include "VideoBridgeParent.h" +#include "CompositorThread.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/StaticMutex.h" +#include "transport/runnable_utils.h" +#include "SynchronousTask.h" + +namespace mozilla { +namespace layers { + +// Singleton +static StaticMutex sVideoBridgeLock MOZ_UNANNOTATED; +StaticRefPtr<VideoBridgeChild> sVideoBridge; + +/* static */ +void VideoBridgeChild::StartupForGPUProcess() { + ipc::Endpoint<PVideoBridgeParent> parentPipe; + ipc::Endpoint<PVideoBridgeChild> childPipe; + + PVideoBridge::CreateEndpoints(base::GetCurrentProcId(), + base::GetCurrentProcId(), &parentPipe, + &childPipe); + + VideoBridgeChild::Open(std::move(childPipe)); + VideoBridgeParent::Open(std::move(parentPipe), VideoBridgeSource::GpuProcess); +} + +/* static */ +void VideoBridgeChild::Open(Endpoint<PVideoBridgeChild>&& aEndpoint) { + StaticMutexAutoLock lock(sVideoBridgeLock); + MOZ_ASSERT(!sVideoBridge || !sVideoBridge->CanSend()); + sVideoBridge = new VideoBridgeChild(); + + if (!aEndpoint.Bind(sVideoBridge)) { + // We can't recover from this. + MOZ_CRASH("Failed to bind VideoBridgeChild to endpoint"); + } +} + +/* static */ +void VideoBridgeChild::Shutdown() { + StaticMutexAutoLock lock(sVideoBridgeLock); + if (sVideoBridge) { + sVideoBridge->Close(); + sVideoBridge = nullptr; + } +} + +VideoBridgeChild::VideoBridgeChild() + : mThread(GetCurrentSerialEventTarget()), mCanSend(true) {} + +VideoBridgeChild::~VideoBridgeChild() = default; + +VideoBridgeChild* VideoBridgeChild::GetSingleton() { + StaticMutexAutoLock lock(sVideoBridgeLock); + return sVideoBridge; +} + +bool VideoBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) { + if (!mThread->IsOnCurrentThread()) { + return DispatchAllocShmemInternal(aSize, aShmem, true); // true: unsafe + } + + if (!CanSend()) { + return false; + } + + return PVideoBridgeChild::AllocUnsafeShmem(aSize, aShmem); +} + +bool VideoBridgeChild::AllocShmem(size_t aSize, ipc::Shmem* aShmem) { + MOZ_ASSERT(CanSend()); + return PVideoBridgeChild::AllocShmem(aSize, aShmem); +} + +void VideoBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize, + ipc::Shmem* aShmem, bool aUnsafe, + bool* aSuccess) { + AutoCompleteTask complete(aTask); + + if (!CanSend()) { + return; + } + + bool ok = false; + if (aUnsafe) { + ok = AllocUnsafeShmem(aSize, aShmem); + } else { + ok = AllocShmem(aSize, aShmem); + } + *aSuccess = ok; +} + +bool VideoBridgeChild::DispatchAllocShmemInternal(size_t aSize, + ipc::Shmem* aShmem, + bool aUnsafe) { + SynchronousTask task("AllocatorProxy alloc"); + + bool success = false; + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<VideoBridgeChild>(this), &VideoBridgeChild::ProxyAllocShmemNow, + &task, aSize, aShmem, aUnsafe, &success); + GetThread()->Dispatch(runnable.forget()); + + task.Wait(); + + return success; +} + +void VideoBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask, + ipc::Shmem* aShmem, bool* aResult) { + AutoCompleteTask complete(aTask); + + if (!CanSend()) { + return; + } + *aResult = DeallocShmem(*aShmem); +} + +bool VideoBridgeChild::DeallocShmem(ipc::Shmem& aShmem) { + if (GetThread()->IsOnCurrentThread()) { + if (!CanSend()) { + return false; + } + return PVideoBridgeChild::DeallocShmem(aShmem); + } + + SynchronousTask task("AllocatorProxy Dealloc"); + bool result = false; + + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<VideoBridgeChild>(this), &VideoBridgeChild::ProxyDeallocShmemNow, + &task, &aShmem, &result); + GetThread()->Dispatch(runnable.forget()); + + task.Wait(); + return result; +} + +PTextureChild* VideoBridgeChild::AllocPTextureChild( + const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&, + const TextureFlags&, const dom::ContentParentId& aContentId, + const uint64_t& aSerial) { + MOZ_ASSERT(CanSend()); + return TextureClient::CreateIPDLActor(); +} + +bool VideoBridgeChild::DeallocPTextureChild(PTextureChild* actor) { + return TextureClient::DestroyIPDLActor(actor); +} + +void VideoBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { + mCanSend = false; +} + +PTextureChild* VideoBridgeChild::CreateTexture( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, + const dom::ContentParentId& aContentId, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId) { + MOZ_ASSERT(CanSend()); + return SendPTextureConstructor(aSharedData, std::move(aReadLock), + aLayersBackend, aFlags, aContentId, aSerial); +} + +bool VideoBridgeChild::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void VideoBridgeChild::HandleFatalError(const char* aMsg) { + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/VideoBridgeChild.h b/gfx/layers/ipc/VideoBridgeChild.h new file mode 100644 index 0000000000..378fa9e337 --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeChild.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZILLA_GFX_VIDEOBRIDGECHILD_H +#define MOZILLA_GFX_VIDEOBRIDGECHILD_H + +#include "mozilla/layers/PVideoBridgeChild.h" +#include "mozilla/layers/VideoBridgeUtils.h" +#include "ISurfaceAllocator.h" +#include "TextureForwarder.h" + +namespace mozilla { +namespace layers { + +class SynchronousTask; + +class VideoBridgeChild final : public PVideoBridgeChild, + public TextureForwarder { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoBridgeChild, override); + + static void StartupForGPUProcess(); + static void Shutdown(); + + static VideoBridgeChild* GetSingleton(); + + // PVideoBridgeChild + PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData, + ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const dom::ContentParentId& aContentId, + const uint64_t& aSerial); + bool DeallocPTextureChild(PTextureChild* actor); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + // ISurfaceAllocator + bool AllocUnsafeShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override; + bool AllocShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override; + bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + + // TextureForwarder + PTextureChild* CreateTexture( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, + const dom::ContentParentId& aContentId, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId) override; + + // ClientIPCAllocator + base::ProcessId GetParentPid() const override { return OtherPid(); } + nsISerialEventTarget* GetThread() const override { return mThread; } + void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override { + MOZ_ASSERT(false, "NO RECYCLING HERE"); + } + + // ISurfaceAllocator + bool IsSameProcess() const override; + + bool CanSend() { return mCanSend; } + + static void Open(Endpoint<PVideoBridgeChild>&& aEndpoint); + + protected: + void HandleFatalError(const char* aMsg) override; + bool DispatchAllocShmemInternal(size_t aSize, mozilla::ipc::Shmem* aShmem, + bool aUnsafe); + void ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize, + mozilla::ipc::Shmem* aShmem, bool aUnsafe, + bool* aSuccess); + void ProxyDeallocShmemNow(SynchronousTask* aTask, mozilla::ipc::Shmem* aShmem, + bool* aResult); + + private: + VideoBridgeChild(); + virtual ~VideoBridgeChild(); + + nsCOMPtr<nsISerialEventTarget> mThread; + bool mCanSend; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/VideoBridgeParent.cpp b/gfx/layers/ipc/VideoBridgeParent.cpp new file mode 100644 index 0000000000..89cc85aee3 --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeParent.cpp @@ -0,0 +1,233 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "VideoBridgeParent.h" +#include "CompositorThread.h" +#include "mozilla/DataMutex.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/TextureHost.h" +#include "mozilla/layers/VideoBridgeUtils.h" +#include "mozilla/webrender/RenderThread.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::ipc; +using namespace mozilla::gfx; + +using VideoBridgeTable = + EnumeratedArray<VideoBridgeSource, VideoBridgeSource::_Count, + VideoBridgeParent*>; + +static StaticDataMutex<VideoBridgeTable> sVideoBridgeFromProcess( + "VideoBridges"); +static Atomic<bool> sVideoBridgeParentShutDown(false); + +VideoBridgeParent::VideoBridgeParent(VideoBridgeSource aSource) + : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()), + mClosed(false) { + auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); + switch (aSource) { + case VideoBridgeSource::RddProcess: + case VideoBridgeSource::GpuProcess: + case VideoBridgeSource::MFMediaEngineCDMProcess: + (*videoBridgeFromProcess)[aSource] = this; + break; + default: + MOZ_CRASH("Unhandled case"); + } +} + +VideoBridgeParent::~VideoBridgeParent() { + auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); + for (auto& bridgeParent : *videoBridgeFromProcess) { + if (bridgeParent == this) { + bridgeParent = nullptr; + } + } +} + +/* static */ +void VideoBridgeParent::Open(Endpoint<PVideoBridgeParent>&& aEndpoint, + VideoBridgeSource aSource) { + RefPtr<VideoBridgeParent> parent = new VideoBridgeParent(aSource); + + CompositorThread()->Dispatch( + NewRunnableMethod<Endpoint<PVideoBridgeParent>&&>( + "gfx::layers::VideoBridgeParent::Bind", parent, + &VideoBridgeParent::Bind, std::move(aEndpoint))); +} + +void VideoBridgeParent::Bind(Endpoint<PVideoBridgeParent>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + // We can't recover from this. + MOZ_CRASH("Failed to bind VideoBridgeParent to endpoint"); + } +} + +/* static */ +VideoBridgeParent* VideoBridgeParent::GetSingleton( + const Maybe<VideoBridgeSource>& aSource) { + MOZ_ASSERT(aSource.isSome()); + auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); + switch (aSource.value()) { + case VideoBridgeSource::RddProcess: + case VideoBridgeSource::GpuProcess: + case VideoBridgeSource::MFMediaEngineCDMProcess: + MOZ_ASSERT((*videoBridgeFromProcess)[aSource.value()]); + return (*videoBridgeFromProcess)[aSource.value()]; + default: + MOZ_CRASH("Unhandled case"); + } +} + +TextureHost* VideoBridgeParent::LookupTexture( + const dom::ContentParentId& aContentId, uint64_t aSerial) { + MOZ_DIAGNOSTIC_ASSERT(CompositorThread() && + CompositorThread()->IsOnCurrentThread()); + auto* actor = mTextureMap[aSerial]; + if (NS_WARN_IF(!actor)) { + return nullptr; + } + + if (NS_WARN_IF(aContentId != TextureHost::GetTextureContentId(actor))) { + return nullptr; + } + + return TextureHost::AsTextureHost(mTextureMap[aSerial]); +} + +void VideoBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { + bool shutdown = sVideoBridgeParentShutDown; + + if (!shutdown && aWhy == AbnormalShutdown) { + gfxCriticalNote + << "VideoBridgeParent receives IPC close with reason=AbnormalShutdown"; + } + // Can't alloc/dealloc shmems from now on. + mClosed = true; + + mCompositorThreadHolder = nullptr; + ReleaseCompositorThread(); +} + +/* static */ +void VideoBridgeParent::Shutdown() { + sVideoBridgeParentShutDown = true; + + auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); + for (auto& bridgeParent : *videoBridgeFromProcess) { + if (bridgeParent) { + bridgeParent->ReleaseCompositorThread(); + } + } +} + +/* static */ +void VideoBridgeParent::UnregisterExternalImages() { + MOZ_ASSERT(sVideoBridgeParentShutDown); + + auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock(); + for (auto& bridgeParent : *videoBridgeFromProcess) { + if (bridgeParent) { + bridgeParent->DoUnregisterExternalImages(); + } + } +} + +void VideoBridgeParent::DoUnregisterExternalImages() { + const ManagedContainer<PTextureParent>& textures = ManagedPTextureParent(); + for (const auto& key : textures) { + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(key); + + if (texture) { + texture->MaybeDestroyRenderTexture(); + } + } +} + +void VideoBridgeParent::ReleaseCompositorThread() { + mCompositorThreadHolder = nullptr; +} + +PTextureParent* VideoBridgeParent::AllocPTextureParent( + const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const dom::ContentParentId& aContentId, const uint64_t& aSerial) { + PTextureParent* parent = TextureHost::CreateIPDLActor( + this, aSharedData, std::move(aReadLock), aLayersBackend, aFlags, + aContentId, aSerial, Nothing()); + + if (!parent) { + return nullptr; + } + + mTextureMap[aSerial] = parent; + return parent; +} + +bool VideoBridgeParent::DeallocPTextureParent(PTextureParent* actor) { + mTextureMap.erase(TextureHost::GetTextureSerial(actor)); + return TextureHost::DestroyIPDLActor(actor); +} + +void VideoBridgeParent::SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) { + MOZ_ASSERT(false, "AsyncMessages not supported"); +} + +bool VideoBridgeParent::AllocShmem(size_t aSize, ipc::Shmem* aShmem) { + if (mClosed) { + return false; + } + return PVideoBridgeParent::AllocShmem(aSize, aShmem); +} + +bool VideoBridgeParent::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) { + if (mClosed) { + return false; + } + return PVideoBridgeParent::AllocUnsafeShmem(aSize, aShmem); +} + +bool VideoBridgeParent::DeallocShmem(ipc::Shmem& aShmem) { + if (mClosed) { + return false; + } + return PVideoBridgeParent::DeallocShmem(aShmem); +} + +bool VideoBridgeParent::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void VideoBridgeParent::NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) {} + +void VideoBridgeParent::OnChannelError() { + bool shutdown = sVideoBridgeParentShutDown; + if (!shutdown) { + // Destory RenderBufferTextureHosts. Shmems of ShmemTextureHosts are going + // to be destroyed + std::vector<wr::ExternalImageId> ids; + auto& ptextures = ManagedPTextureParent(); + for (const auto& ptexture : ptextures) { + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(ptexture); + if (texture && texture->AsShmemTextureHost() && + texture->GetMaybeExternalImageId().isSome()) { + ids.emplace_back(texture->GetMaybeExternalImageId().ref()); + } + } + if (!ids.empty()) { + wr::RenderThread::Get()->DestroyExternalImagesSyncWait(std::move(ids)); + } + } + + PVideoBridgeParent::OnChannelError(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/VideoBridgeParent.h b/gfx/layers/ipc/VideoBridgeParent.h new file mode 100644 index 0000000000..9a3466f59a --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeParent.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef gfx_layers_ipc_VideoBridgeParent_h_ +#define gfx_layers_ipc_VideoBridgeParent_h_ + +#include "mozilla/layers/ISurfaceAllocator.h" +#include "mozilla/layers/PVideoBridgeParent.h" + +namespace mozilla { +namespace layers { + +enum class VideoBridgeSource : uint8_t; +class CompositorThreadHolder; + +class VideoBridgeParent final : public PVideoBridgeParent, + public HostIPCAllocator, + public mozilla::ipc::IShmemAllocator { + public: + NS_INLINE_DECL_REFCOUNTING_INHERITED(VideoBridgeParent, HostIPCAllocator) + + static VideoBridgeParent* GetSingleton( + const Maybe<VideoBridgeSource>& aSource); + + static void Open(Endpoint<PVideoBridgeParent>&& aEndpoint, + VideoBridgeSource aSource); + static void Shutdown(); + static void UnregisterExternalImages(); + + TextureHost* LookupTexture(const dom::ContentParentId& aContentId, + uint64_t aSerial); + + // PVideoBridgeParent + void ActorDestroy(ActorDestroyReason aWhy) override; + PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, + ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const dom::ContentParentId& aContentId, + const uint64_t& aSerial); + bool DeallocPTextureParent(PTextureParent* actor); + + // HostIPCAllocator + base::ProcessId GetChildProcessId() override { return OtherPid(); } + void NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) override; + void SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) override; + + // ISurfaceAllocator + IShmemAllocator* AsShmemAllocator() override { return this; } + bool IsSameProcess() const override; + bool IPCOpen() const override { return !mClosed; } + + // IShmemAllocator + bool AllocShmem(size_t aSize, ipc::Shmem* aShmem) override; + + bool AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) override; + + bool DeallocShmem(ipc::Shmem& aShmem) override; + + void OnChannelError() override; + + private: + ~VideoBridgeParent(); + + explicit VideoBridgeParent(VideoBridgeSource aSource); + void Bind(Endpoint<PVideoBridgeParent>&& aEndpoint); + + void ReleaseCompositorThread(); + void DoUnregisterExternalImages(); + + RefPtr<CompositorThreadHolder> mCompositorThreadHolder; + + std::map<uint64_t, PTextureParent*> mTextureMap; + + bool mClosed; +}; + +} // namespace layers +} // namespace mozilla + +#endif // gfx_layers_ipc_VideoBridgeParent_h_ diff --git a/gfx/layers/ipc/VideoBridgeUtils.h b/gfx/layers/ipc/VideoBridgeUtils.h new file mode 100644 index 0000000000..4163b5e08f --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeUtils.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef IPC_VideoBridgeUtils_h +#define IPC_VideoBridgeUtils_h + +#include "ipc/EnumSerializer.h" + +namespace mozilla { +namespace layers { + +enum class VideoBridgeSource : uint8_t { + RddProcess, + GpuProcess, + MFMediaEngineCDMProcess, + _Count, +}; + +typedef Maybe<VideoBridgeSource> MaybeVideoBridgeSource; + +} // namespace layers +} // namespace mozilla + +namespace IPC { + +template <> +struct ParamTraits<mozilla::layers::VideoBridgeSource> + : public ContiguousEnumSerializer< + mozilla::layers::VideoBridgeSource, + mozilla::layers::VideoBridgeSource::RddProcess, + mozilla::layers::VideoBridgeSource::_Count> {}; + +} // namespace IPC + +#endif // IPC_VideoBridgeUtils_h diff --git a/gfx/layers/ipc/WebRenderMessages.ipdlh b/gfx/layers/ipc/WebRenderMessages.ipdlh new file mode 100644 index 0000000000..eb5633a506 --- /dev/null +++ b/gfx/layers/ipc/WebRenderMessages.ipdlh @@ -0,0 +1,199 @@ +/* -*- 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 "mozilla/layers/LayersMessageUtils.h"; +include "mozilla/layers/WebRenderMessageUtils.h"; + +include LayersSurfaces; +include LayersMessages; +include protocol PTexture; + +using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::CompositableHandleOwner from "mozilla/layers/LayersTypes.h"; +using mozilla::wr::LayoutSize from "mozilla/webrender/webrender_ffi.h"; +using mozilla::wr::ImageDescriptor from "mozilla/webrender/webrender_ffi.h"; +using mozilla::wr::ImageRendering from "mozilla/webrender/webrender_ffi.h"; +using mozilla::wr::MixBlendMode from "mozilla/webrender/webrender_ffi.h"; +using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::MaybeFontInstanceOptions from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::MaybeFontInstancePlatformOptions from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::FontInstanceKey from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::BlobImageKey from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::gfx::MaybeIntSize from "mozilla/gfx/Point.h"; +using mozilla::LayoutDeviceRect from "Units.h"; +using mozilla::LayoutDeviceSize from "Units.h"; +using mozilla::ImageIntRect from "Units.h"; +using mozilla::gfx::Rect from "mozilla/gfx/Rect.h"; +using mozilla::VideoInfo::Rotation from "MediaInfo.h"; +using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h"; +using struct mozilla::void_t from "mozilla/ipc/IPCCore.h"; + +namespace mozilla { +namespace layers { + +struct RefCountedShmem { + Shmem buffer; +}; + +struct OpAddSharedExternalImage { + ExternalImageId externalImageId; + ImageKey key; +}; + +struct OpPushExternalImageForTexture { + ExternalImageId externalImageId; + ImageKey key; + PTexture texture; + bool isUpdate; +}; + +struct OpAddCompositorAnimations { + CompositorAnimations data; +}; + +struct OpAddPipelineIdForCompositable { + PipelineId pipelineId; + CompositableHandle handle; + CompositableHandleOwner owner; +}; + +struct OpRemovePipelineIdForCompositable { + PipelineId pipelineId; +}; + +struct OpReleaseTextureOfImage { + ImageKey key; +}; + +struct OpUpdateAsyncImagePipeline { + PipelineId pipelineId; + LayoutDeviceRect scBounds; + Rotation rotation; + ImageRendering filter; + MixBlendMode mixBlendMode; +}; + +struct OpUpdatedAsyncImagePipeline { + PipelineId pipelineId; +}; + +union WebRenderParentCommand { + OpAddPipelineIdForCompositable; + OpRemovePipelineIdForCompositable; + OpReleaseTextureOfImage; + OpUpdateAsyncImagePipeline; + OpUpdatedAsyncImagePipeline; + CompositableOperation; + OpAddCompositorAnimations; +}; + +struct OffsetRange { + uint32_t source; + uint32_t start; + uint32_t length; +}; + +struct OpAddImage { + ImageDescriptor descriptor; + OffsetRange bytes; + uint16_t tiling; + ImageKey key; +}; + +struct OpAddBlobImage { + ImageDescriptor descriptor; + OffsetRange bytes; + ImageIntRect visibleRect; + uint16_t tiling; + BlobImageKey key; +}; + +struct OpUpdateImage { + ImageDescriptor descriptor; + OffsetRange bytes; + ImageKey key; +}; + +struct OpUpdateBlobImage { + ImageDescriptor descriptor; + OffsetRange bytes; + BlobImageKey key; + ImageIntRect visibleRect; + ImageIntRect dirtyRect; +}; + +struct OpSetBlobImageVisibleArea { + ImageIntRect area; + BlobImageKey key; +}; + +struct OpUpdateSharedExternalImage { + ExternalImageId externalImageId; + ImageKey key; + ImageIntRect dirtyRect; +}; + +struct OpDeleteImage { + ImageKey key; +}; + +struct OpDeleteBlobImage { + BlobImageKey key; +}; + +struct OpAddRawFont { + OffsetRange bytes; + uint32_t fontIndex; + FontKey key; +}; + +struct OpAddFontDescriptor { + OffsetRange bytes; + uint32_t fontIndex; + FontKey key; +}; + +struct OpDeleteFont { + FontKey key; +}; + +struct OpAddFontInstance { + MaybeFontInstanceOptions options; + MaybeFontInstancePlatformOptions platformOptions; + OffsetRange variations; + FontInstanceKey instanceKey; + FontKey fontKey; + float glyphSize; +}; + +struct OpDeleteFontInstance { + FontInstanceKey key; +}; + +union OpUpdateResource { + OpAddImage; + OpAddBlobImage; + OpUpdateImage; + OpUpdateBlobImage; + OpSetBlobImageVisibleArea; + OpDeleteImage; + OpDeleteBlobImage; + OpAddRawFont; + OpAddFontDescriptor; + OpDeleteFont; + OpAddFontInstance; + OpDeleteFontInstance; + OpAddSharedExternalImage; + OpPushExternalImageForTexture; + OpUpdateSharedExternalImage; +}; + +} // namespace +} // namespace |