diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/ipc | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
103 files changed, 25494 insertions, 0 deletions
diff --git a/gfx/layers/ipc/APZCTreeManagerChild.cpp b/gfx/layers/ipc/APZCTreeManagerChild.cpp new file mode 100644 index 0000000000..b9fc37efb9 --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp @@ -0,0 +1,181 @@ +/* -*- 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 + +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) { + SendSetKeyboardMap(aKeyboardMap); +} + +void APZCTreeManagerChild::ZoomToRect(const ScrollableLayerGuid& aGuid, + const CSSRect& aRect, + const uint32_t aFlags) { + SendZoomToRect(aGuid, aRect, aFlags); +} + +void APZCTreeManagerChild::ContentReceivedInputBlock(uint64_t aInputBlockId, + bool aPreventDefault) { + SendContentReceivedInputBlock(aInputBlockId, aPreventDefault); +} + +void APZCTreeManagerChild::SetTargetAPZC( + uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) { + SendSetTargetAPZC(aInputBlockId, aTargets); +} + +void APZCTreeManagerChild::UpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const Maybe<ZoomConstraints>& aConstraints) { + if (mIPCOpen) { + SendUpdateZoomConstraints(aGuid, aConstraints); + } +} + +void APZCTreeManagerChild::SetDPI(float aDpiValue) { SendSetDPI(aDpiValue); } + +void APZCTreeManagerChild::SetAllowedTouchBehavior( + uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) { + SendSetAllowedTouchBehavior(aInputBlockId, aValues); +} + +void APZCTreeManagerChild::StartScrollbarDrag( + const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) { + SendStartScrollbarDrag(aGuid, aDragMetrics); +} + +bool APZCTreeManagerChild::StartAutoscroll(const ScrollableLayerGuid& aGuid, + const ScreenPoint& aAnchorLocation) { + return SendStartAutoscroll(aGuid, aAnchorLocation); +} + +void APZCTreeManagerChild::StopAutoscroll(const ScrollableLayerGuid& aGuid) { + SendStopAutoscroll(aGuid); +} + +void APZCTreeManagerChild::SetLongTapEnabled(bool aTapGestureEnabled) { + SendSetLongTapEnabled(aTapGestureEnabled); +} + +APZInputBridge* APZCTreeManagerChild::InputBridge() { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(mInputBridge); + + return mInputBridge.get(); +} + +void APZCTreeManagerChild::AddInputBlockCallback( + uint64_t aInputBlockId, InputBlockCallback&& aCallback) { + MOZ_RELEASE_ASSERT(false, + "Remoting of input block callbacks is not implemented"); +} + +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) { + tab->SendHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId); + } + 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(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/APZCTreeManagerChild.h b/gfx/layers/ipc/APZCTreeManagerChild.h new file mode 100644 index 0000000000..dcba794bfa --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerChild.h @@ -0,0 +1,99 @@ +/* -*- 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" + +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 CSSRect& aRect, + 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 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 AddInputBlockCallback(uint64_t aInputBlockId, + InputBlockCallback&& aCallback) 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); + + 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..1dcd3fc275 --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp @@ -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/. */ + +#include "mozilla/layers/APZCTreeManagerParent.h" + +#include "apz/src/APZCTreeManager.h" +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layers/APZUpdater.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->RunOnControllerThread( + mLayersId, NewRunnableMethod<KeyboardMap>( + "layers::IAPZCTreeManager::SetKeyboardMap", mTreeManager, + &IAPZCTreeManager::SetKeyboardMap, aKeyboardMap)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvZoomToRect( + const ScrollableLayerGuid& aGuid, const CSSRect& aRect, + const uint32_t& aFlags) { + if (!IsGuidValid(aGuid)) { + return IPC_FAIL_NO_REASON(this); + } + + mUpdater->RunOnControllerThread( + aGuid.mLayersId, + NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>( + "layers::IAPZCTreeManager::ZoomToRect", mTreeManager, + &IAPZCTreeManager::ZoomToRect, aGuid, aRect, aFlags)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvContentReceivedInputBlock( + const uint64_t& aInputBlockId, const bool& aPreventDefault) { + mUpdater->RunOnControllerThread( + 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->RunOnControllerThread( + 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 MaybeZoomConstraints& 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->RunOnControllerThread( + 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->RunOnControllerThread( + mLayersId, + NewRunnableMethod<uint64_t, + StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>( + "layers::IAPZCTreeManager::SetAllowedTouchBehavior", mTreeManager, + &IAPZCTreeManager::SetAllowedTouchBehavior, aInputBlockId, + std::move(aValues))); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartScrollbarDrag( + const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) { + if (!IsGuidValid(aGuid)) { + return IPC_FAIL_NO_REASON(this); + } + + mUpdater->RunOnControllerThread( + 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->RunOnControllerThread( + 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..de54d4dbfd --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerParent.h @@ -0,0 +1,78 @@ +/* -*- 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 CSSRect& aRect, + 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 MaybeZoomConstraints& aConstraints); + + mozilla::ipc::IPCResult RecvSetDPI(const float& aDpiValue); + + mozilla::ipc::IPCResult RecvSetAllowedTouchBehavior( + const uint64_t& aInputBlockId, nsTArray<TouchBehaviorFlags>&& aValues); + + 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..a1754e1613 --- /dev/null +++ b/gfx/layers/ipc/APZChild.cpp @@ -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/. */ + +#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 (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()); + + mController->RequestContentRepaint(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) { + mController->NotifyAPZStateChange(aGuid, aChange, aArg); + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZChild::RecvNotifyFlushComplete() { + MOZ_ASSERT(mController->IsRepaintThread()); + + mController->NotifyFlushComplete(); + 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..a87fd7b7aa --- /dev/null +++ b/gfx/layers/ipc/APZChild.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_APZChild_h +#define mozilla_layers_APZChild_h + +#include "mozilla/layers/PAPZChild.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); + + 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: + RefPtr<GeckoContentController> mController; +}; + +} // 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..d1bb57a52c --- /dev/null +++ b/gfx/layers/ipc/APZInputBridgeChild.cpp @@ -0,0 +1,124 @@ +/* -*- 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 + +namespace mozilla { +namespace layers { + +APZInputBridgeChild::APZInputBridgeChild() : mDestroyed(false) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); +} + +APZInputBridgeChild::~APZInputBridgeChild() = default; + +void APZInputBridgeChild::Destroy() { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (mDestroyed) { + return; + } + + Send__delete__(this); + mDestroyed = true; +} + +void APZInputBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { + mDestroyed = true; +} + +APZEventResult APZInputBridgeChild::ReceiveInputEvent(InputData& aEvent) { + APZEventResult res; + switch (aEvent.mInputType) { + case MULTITOUCH_INPUT: { + MultiTouchInput& event = aEvent.AsMultiTouchInput(); + MultiTouchInput processedEvent; + + SendReceiveMultiTouchInputEvent(event, &res, &processedEvent); + + event = processedEvent; + return res; + } + case MOUSE_INPUT: { + MouseInput& event = aEvent.AsMouseInput(); + MouseInput processedEvent; + + SendReceiveMouseInputEvent(event, &res, &processedEvent); + + event = processedEvent; + return res; + } + case PANGESTURE_INPUT: { + PanGestureInput& event = aEvent.AsPanGestureInput(); + PanGestureInput processedEvent; + + SendReceivePanGestureInputEvent(event, &res, &processedEvent); + + event = processedEvent; + return res; + } + case PINCHGESTURE_INPUT: { + PinchGestureInput& event = aEvent.AsPinchGestureInput(); + PinchGestureInput processedEvent; + + SendReceivePinchGestureInputEvent(event, &res, &processedEvent); + + event = processedEvent; + return res; + } + case TAPGESTURE_INPUT: { + TapGestureInput& event = aEvent.AsTapGestureInput(); + TapGestureInput processedEvent; + + SendReceiveTapGestureInputEvent(event, &res, &processedEvent); + + event = processedEvent; + return res; + } + case SCROLLWHEEL_INPUT: { + ScrollWheelInput& event = aEvent.AsScrollWheelInput(); + ScrollWheelInput processedEvent; + + SendReceiveScrollWheelInputEvent(event, &res, &processedEvent); + + event = processedEvent; + return res; + } + case KEYBOARD_INPUT: { + KeyboardInput& event = aEvent.AsKeyboardInput(); + KeyboardInput processedEvent; + + SendReceiveKeyboardInputEvent(event, &res, &processedEvent); + + event = processedEvent; + return res; + } + default: { + MOZ_ASSERT_UNREACHABLE("Invalid InputData type."); + res.mStatus = nsEventStatus_eConsumeNoDefault; + return res; + } + } +} + +void APZInputBridgeChild::ProcessUnhandledEvent( + LayoutDeviceIntPoint* aRefPoint, ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutFocusSequenceNumber, LayersId* aOutLayersId) { + SendProcessUnhandledEvent(*aRefPoint, aRefPoint, aOutTargetGuid, + aOutFocusSequenceNumber, aOutLayersId); +} + +void APZInputBridgeChild::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, + EventMessage aEventMessage) { + SendUpdateWheelTransaction(aRefPoint, aEventMessage); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/APZInputBridgeChild.h b/gfx/layers/ipc/APZInputBridgeChild.h new file mode 100644 index 0000000000..88e1d28c0a --- /dev/null +++ b/gfx/layers/ipc/APZInputBridgeChild.h @@ -0,0 +1,44 @@ +/* -*- 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_REFCOUNTING(APZInputBridgeChild, final) + + public: + APZInputBridgeChild(); + void Destroy(); + + APZEventResult ReceiveInputEvent(InputData& aEvent) override; + + protected: + void ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutFocusSequenceNumber, + LayersId* aOutLayersId) override; + + void UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, + EventMessage aEventMessage) override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + virtual ~APZInputBridgeChild(); + + private: + bool mDestroyed; +}; + +} // 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..23fcb1cf88 --- /dev/null +++ b/gfx/layers/ipc/APZInputBridgeParent.cpp @@ -0,0 +1,128 @@ +/* -*- 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/layers/APZInputBridge.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "InputData.h" + +namespace mozilla { +namespace layers { + +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, APZEventResult* aOutResult, + MultiTouchInput* aOutEvent) { + MultiTouchInput event = aEvent; + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveMouseInputEvent( + const MouseInput& aEvent, APZEventResult* aOutResult, + MouseInput* aOutEvent) { + MouseInput event = aEvent; + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceivePanGestureInputEvent( + const PanGestureInput& aEvent, APZEventResult* aOutResult, + PanGestureInput* aOutEvent) { + PanGestureInput event = aEvent; + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceivePinchGestureInputEvent( + const PinchGestureInput& aEvent, APZEventResult* aOutResult, + PinchGestureInput* aOutEvent) { + PinchGestureInput event = aEvent; + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveTapGestureInputEvent( + const TapGestureInput& aEvent, APZEventResult* aOutResult, + TapGestureInput* aOutEvent) { + TapGestureInput event = aEvent; + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveScrollWheelInputEvent( + const ScrollWheelInput& aEvent, APZEventResult* aOutResult, + ScrollWheelInput* aOutEvent) { + ScrollWheelInput event = aEvent; + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveKeyboardInputEvent( + const KeyboardInput& aEvent, APZEventResult* aOutResult, + KeyboardInput* aOutEvent) { + KeyboardInput event = aEvent; + + *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event); + *aOutEvent = event; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult APZInputBridgeParent::RecvUpdateWheelTransaction( + const LayoutDeviceIntPoint& aRefPoint, const EventMessage& aEventMessage) { + mTreeManager->InputBridge()->UpdateWheelTransaction(aRefPoint, aEventMessage); + 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..94d917a80f --- /dev/null +++ b/gfx/layers/ipc/APZInputBridgeParent.h @@ -0,0 +1,71 @@ +/* -*- 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: + explicit APZInputBridgeParent(const LayersId& aLayersId); + + mozilla::ipc::IPCResult RecvReceiveMultiTouchInputEvent( + const MultiTouchInput& aEvent, APZEventResult* aOutResult, + MultiTouchInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceiveMouseInputEvent(const MouseInput& aEvent, + APZEventResult* aOutResult, + MouseInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceivePanGestureInputEvent( + const PanGestureInput& aEvent, APZEventResult* aOutResult, + PanGestureInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceivePinchGestureInputEvent( + const PinchGestureInput& aEvent, APZEventResult* aOutResult, + PinchGestureInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceiveTapGestureInputEvent( + const TapGestureInput& aEvent, APZEventResult* aOutResult, + TapGestureInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceiveScrollWheelInputEvent( + const ScrollWheelInput& aEvent, APZEventResult* aOutResult, + ScrollWheelInput* aOutEvent); + + mozilla::ipc::IPCResult RecvReceiveKeyboardInputEvent( + const KeyboardInput& aEvent, APZEventResult* aOutResult, + KeyboardInput* aOutEvent); + + mozilla::ipc::IPCResult RecvUpdateWheelTransaction( + const LayoutDeviceIntPoint& aRefPoint, const EventMessage& aEventMessage); + + mozilla::ipc::IPCResult RecvProcessUnhandledEvent( + const LayoutDeviceIntPoint& aRefPoint, LayoutDeviceIntPoint* aOutRefPoint, + ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutFocusSequenceNumber, + LayersId* aOutLayersId); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + protected: + 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..144d383d89 --- /dev/null +++ b/gfx/layers/ipc/CanvasChild.cpp @@ -0,0 +1,325 @@ +/* -*- 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) { + mRecorder->RecordEvent(RecordedAddSurfaceAlias(this, aRecordedSuface)); + mRecorder->AddStoredObject(this); + } + + ~SourceSurfaceCanvasRecording() { + ReleaseOnMainThread(std::move(mRecorder), this, std::move(mRecordedSurface), + std::move(mCanvasChild)); + } + + 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); + } + + protected: + void GuaranteePersistance() final { EnsureDataSurfaceOnMainThread(); } + + 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) { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NewRunnableFunction( + "SourceSurfaceCanvasRecording::ReleaseOnMainThread", + SourceSurfaceCanvasRecording::ReleaseOnMainThread, + std::move(aRecorder), aSurfaceAlias, std::move(aAliasedSurface), + std::move(aCanvasChild))); + return; + } + + 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, handle, readerSem, 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; + } +} + +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 = 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..68ac863b35 --- /dev/null +++ b/gfx/layers/ipc/CanvasChild.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 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(); + 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..e91492c1ac --- /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 MakeAndAddRef<TaskQueue>(do_AddRef(mCanvasWorkers)); +} + +} // 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..065bfacfd4 --- /dev/null +++ b/gfx/layers/ipc/CanvasTranslator.cpp @@ -0,0 +1,534 @@ +/* -*- 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 "mozilla/gfx/2D.h" +#include "mozilla/gfx/GPUParent.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/TextureClient.h" +#include "mozilla/SyncRunnable.h" +#include "mozilla/Telemetry.h" +#include "nsTHashtable.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); + +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 nsTHashtable<nsRefPtrHashKey<CanvasTranslator>> CanvasTranslatorSet; + +static CanvasTranslatorSet& CanvasTranslators() { + MOZ_ASSERT(CanvasThreadHolder::IsInCanvasThread()); + static CanvasTranslatorSet* sCanvasTranslator = new CanvasTranslatorSet(); + return *sCanvasTranslator; +} + +static void EnsureAllClosed() { + for (auto iter = CanvasTranslators().Iter(); !iter.Done(); iter.Next()) { + iter.Get()->GetKey()->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() { + if (mReferenceTextureData) { + mReferenceTextureData->Unlock(); + } +} + +void CanvasTranslator::Bind(Endpoint<PCanvasParent>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + return; + } + + CanvasTranslators().PutEntry(this); +} + +mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator( + const TextureType& aTextureType, + const ipc::SharedMemoryBasic::Handle& aReadHandle, + const CrossProcessSemaphoreHandle& aReaderSem, + const CrossProcessSemaphoreHandle& aWriterSem) { + 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(aReadHandle, aReaderSem, 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.RemoveEntry(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. + gfx::AutoSerializeWithMoz2D serializeWithMoz2D(GetBackendType()); + 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(GetBackendType()); + 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) { + 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(GetMainThreadEventTarget(), 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 { + // It is important that AutoSerializeWithMoz2D is called within the loop + // and doesn't hold during calls to CheckForFreshCanvasDevice, because that + // might cause a deadlock with device reset code on the main thread. + gfx::AutoSerializeWithMoz2D serializeWithMoz2D(GetBackendType()); + 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) { + gfx::AutoSerializeWithMoz2D serializeWithMoz2D(GetBackendType()); + 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; + } + + mSurfaceDescriptorsMonitor.Wait(); + } + + UniquePtr<SurfaceDescriptor> descriptor = std::move(result->second); + mSurfaceDescriptors.erase(aTextureId); + return descriptor; +} + +gfx::DataSourceSurface* CanvasTranslator::LookupDataSurface( + gfx::ReferencePtr aRefPtr) { + return mDataSurfaces.GetWeak(aRefPtr); +} + +void CanvasTranslator::AddDataSurface( + gfx::ReferencePtr aRefPtr, RefPtr<gfx::DataSourceSurface>&& aSurface) { + mDataSurfaces.Put(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..eb010bbeb7 --- /dev/null +++ b/gfx/layers/ipc/CanvasTranslator.h @@ -0,0 +1,284 @@ +/* -*- 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, + const ipc::SharedMemoryBasic::Handle& aReadHandle, + const CrossProcessSemaphoreHandle& aReaderSem, + const 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; + + /** + * 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); + + /** + * 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 { + RemoveDataSurface(aRefPtr); + InlineTranslator::RemoveSourceSurface(aRefPtr); + } + + /** + * 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); + + /** + * @return the BackendType being used for translation + */ + gfx::BackendType GetBackendType() { return mBackendType; } + + 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; + 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..412af3e859 --- /dev/null +++ b/gfx/layers/ipc/CompositableForwarder.h @@ -0,0 +1,125 @@ +/* -*- 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 ShadowLayerForwarder; +class SurfaceDescriptorTiles; +class TextureClient; +class ThebesBufferData; + +/** + * 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; + + /** + * Tell the CompositableHost on the compositor side what TiledLayerBuffer to + * use for the next composition. + */ + virtual void UseTiledLayerBuffer( + CompositableClient* aCompositable, + const SurfaceDescriptorTiles& aTiledDescriptor) = 0; + + /** + * Communicate to the compositor that aRegion in the texture identified by + * aCompositable and aIdentifier has been updated to aThebesBuffer. + */ + virtual void UpdateTextureRegion(CompositableClient* aCompositable, + const ThebesBufferData& aThebesBufferData, + const nsIntRegion& aUpdatedRegion) = 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 UseComponentAlphaTextures(CompositableClient* aCompositable, + TextureClient* aClientOnBlack, + TextureClient* aClientOnWhite) = 0; + + virtual void UpdateFwdTransactionId() = 0; + virtual uint64_t GetFwdTransactionId() = 0; + + virtual bool InForwarderThread() = 0; + + void AssertInForwarderThread() { MOZ_ASSERT(InForwarderThread()); } + + static uint32_t GetMaxFileDescriptorsPerMessage(); + + virtual ShadowLayerForwarder* AsLayerForwarder() { return nullptr; } + + 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..5ca2453c54 --- /dev/null +++ b/gfx/layers/ipc/CompositableTransactionParent.cpp @@ -0,0 +1,337 @@ +/* -*- 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 "GLContext.h" // for GLContext +#include "Layers.h" // for Layer +#include "RenderTrace.h" // for RenderTraceInvalidateEnd, etc +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/ContentHost.h" // for ContentHostBase +#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent +#include "mozilla/layers/LayerManagerComposite.h" +#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/TextureHostOGL.h" // for TextureHostOGL +#include "mozilla/layers/TiledContentHost.h" +#include "mozilla/layers/PaintedLayerComposite.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 + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/layers/AndroidHardwareBuffer.h" +#endif + +namespace mozilla { +namespace layers { + +class ClientTiledLayerBuffer; +class Compositor; + +// This function can in some cases fail and return false without it being a bug. +// This can theoretically happen if the ImageBridge sends frames before +// we created the layer tree. Since we can't enforce that the layer +// tree is already created before ImageBridge operates, there isn't much +// we can do about it, but in practice it is very rare. +// Typically when a tab with a video is dragged from a window to another, +// there can be a short time when the video is still sending frames +// asynchonously while the layer tree is not reconstructed. It's not a +// big deal. +// Note that Layers transactions do not need to call this because they always +// schedule the composition, in LayerManagerComposite::EndTransaction. +static bool ScheduleComposition(CompositableHost* aCompositable) { + uint64_t id = aCompositable->GetCompositorBridgeID(); + if (!id) { + return false; + } + CompositorBridgeParent* cp = + CompositorBridgeParent::GetCompositorBridgeParent(id); + if (!cp) { + return false; + } + cp->ScheduleComposition(); + return true; +} + +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)); +} + +bool CompositableParentManager::ReceiveCompositableUpdate( + const CompositableOperationDetail& aDetail, + NotNull<CompositableHost*> aCompositable) { + if (TextureSourceProvider* provider = + aCompositable->GetTextureSourceProvider()) { + if (!provider->IsValid()) { + return false; + } + } + + switch (aDetail.type()) { + case CompositableOperationDetail::TOpPaintTextureRegion: { + MOZ_LAYERS_LOG(("[ParentSide] Paint PaintedLayer")); + + const OpPaintTextureRegion& op = aDetail.get_OpPaintTextureRegion(); + Layer* layer = aCompositable->GetLayer(); + if (!layer || layer->GetType() != Layer::TYPE_PAINTED) { + return false; + } + PaintedLayerComposite* thebes = + static_cast<PaintedLayerComposite*>(layer); + + const ThebesBufferData& bufferData = op.bufferData(); + + RenderTraceInvalidateStart(thebes, "FF00FF", + op.updatedRegion().GetBounds()); + + if (!aCompositable->UpdateThebes(bufferData, op.updatedRegion(), + thebes->GetValidRegion())) { + return false; + } + + RenderTraceInvalidateEnd(thebes, "FF00FF"); + break; + } + case CompositableOperationDetail::TOpUseTiledLayerBuffer: { + MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer")); + const OpUseTiledLayerBuffer& op = aDetail.get_OpUseTiledLayerBuffer(); + TiledContentHost* tiledHost = aCompositable->AsTiledContentHost(); + + NS_ASSERTION(tiledHost, "The compositable is not tiled"); + + const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor(); + + bool success = tiledHost->UseTiledLayerBuffer(this, tileDesc); + + const nsTArray<TileDescriptor>& tileDescriptors = tileDesc.tiles(); + for (size_t i = 0; i < tileDescriptors.Length(); i++) { + const TileDescriptor& tileDesc = tileDescriptors[i]; + if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) { + continue; + } + const TexturedTileDescriptor& texturedDesc = + tileDesc.get_TexturedTileDescriptor(); + RefPtr<TextureHost> texture = + TextureHost::AsTextureHost(texturedDesc.textureParent()); + 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); + } + if (texturedDesc.textureOnWhiteParent().isSome()) { + texture = TextureHost::AsTextureHost( + texturedDesc.textureOnWhiteParent().ref()); + 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); + } + } + } + if (!success) { + return false; + } + break; + } + case CompositableOperationDetail::TOpRemoveTexture: { + const OpRemoveTexture& op = aDetail.get_OpRemoveTexture(); + + RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent()); + + 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.textureParent()); + 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.textureParent()); + 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); + } + } + } + + if (UsesImageBridge() && aCompositable->GetLayer()) { + ScheduleComposition(aCompositable); + } + break; + } + case CompositableOperationDetail::TOpUseComponentAlphaTextures: { + const OpUseComponentAlphaTextures& op = + aDetail.get_OpUseComponentAlphaTextures(); + RefPtr<TextureHost> texOnBlack = + TextureHost::AsTextureHost(op.textureOnBlackParent()); + RefPtr<TextureHost> texOnWhite = + TextureHost::AsTextureHost(op.textureOnWhiteParent()); + if (op.readLockedBlack()) { + texOnBlack->SetReadLocked(); + } + if (op.readLockedWhite()) { + texOnWhite->SetReadLocked(); + } + + MOZ_ASSERT(texOnBlack && texOnWhite); + aCompositable->UseComponentAlphaTextures(texOnBlack, texOnWhite); + + if (texOnBlack) { + texOnBlack->SetLastFwdTransactionId(mFwdTransactionId); + // Make sure that each texture was handled by the compositable + // because the recycling logic depends on it. + MOZ_ASSERT(texOnBlack->NumCompositableRefs() > 0); + } + + if (texOnWhite) { + texOnWhite->SetLastFwdTransactionId(mFwdTransactionId); + // Make sure that each texture was handled by the compositable + // because the recycling logic depends on it. + MOZ_ASSERT(texOnWhite->NumCompositableRefs() > 0); + } + + if (UsesImageBridge()) { + ScheduleComposition(aCompositable); + } + break; + } + case CompositableOperationDetail::TOpDeliverAcquireFence: { + const OpDeliverAcquireFence& op = aDetail.get_OpDeliverAcquireFence(); + RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent()); + MOZ_ASSERT(tex.get()); + MOZ_ASSERT(tex->AsAndroidHardwareBufferTextureHost()); + + auto fenceFd = op.fenceFd(); + tex->SetAcquireFence(std::move(fenceFd)); + break; + } + default: { + MOZ_ASSERT(false, "bad type"); + } + } + + return true; +} + +void CompositableParentManager::DestroyActor(const OpDestroy& aOp) { + switch (aOp.type()) { + case OpDestroy::TPTextureParent: { + auto actor = aOp.get_PTextureParent(); + 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, + bool aUseWebRender) { + 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, aUseWebRender); + if (!host) { + return nullptr; + } + + mCompositables[aHandle.Value()] = host; + return host; +} + +RefPtr<CompositableHost> CompositableParentManager::FindCompositable( + const CompositableHandle& aHandle, bool aAllowDisablingWebRender) { + auto iter = mCompositables.find(aHandle.Value()); + if (iter == mCompositables.end()) { + return nullptr; + } + + RefPtr<CompositableHost> host = iter->second; + if (!aAllowDisablingWebRender) { + return host; + } + + if (!host->AsWebRenderImageHost() || !host->GetAsyncRef()) { + return host; + } + + // Try to replace WebRenderImageHost of ImageBridge to ImageHost. + RefPtr<CompositableHost> newHost = CompositableHost::Create( + host->GetTextureInfo(), /* aUseWebRender */ false); + if (!newHost || !newHost->AsImageHost()) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return host; + } + + newHost->SetAsyncRef(host->GetAsyncRef()); + mCompositables[aHandle.Value()] = newHost; + + return newHost; +} + +void CompositableParentManager::ReleaseCompositable( + const CompositableHandle& aHandle) { + auto iter = mCompositables.find(aHandle.Value()); + if (iter == mCompositables.end()) { + return; + } + + RefPtr<CompositableHost> host = iter->second; + mCompositables.erase(iter); + + host->Detach(nullptr, CompositableHost::FORCE_DETACH); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositableTransactionParent.h b/gfx/layers/ipc/CompositableTransactionParent.h new file mode 100644 index 0000000000..8912f50bce --- /dev/null +++ b/gfx/layers/ipc/CompositableTransactionParent.h @@ -0,0 +1,65 @@ +/* -*- 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, + bool aUseWebRender); + RefPtr<CompositableHost> FindCompositable( + const CompositableHandle& aHandle, bool aAllowDisablingWebRender = false); + + protected: + /** + * Handle the IPDL messages that affect PCompositable actors. + */ + bool ReceiveCompositableUpdate(const CompositableOperation& aEdit); + bool ReceiveCompositableUpdate(const CompositableOperationDetail& aDetail, + NotNull<CompositableHost*> aCompositable); + + 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..442989a1af --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -0,0 +1,1310 @@ +/* -*- 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 "ClientLayerManager.h" // for ClientLayerManager +#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/LayerTransactionChild.h" +#include "mozilla/layers/PaintThread.h" +#include "mozilla/layers/PLayerTransactionChild.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/webgpu/WebGPUChild.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 "FrameLayerBuilder.h" +#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" + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/layers/AndroidHardwareBuffer.h" +#endif + +using mozilla::Unused; +using mozilla::gfx::GPUProcessManager; +using mozilla::layers::LayerTransactionChild; + +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), + mPaintLock("CompositorBridgeChild.mPaintLock"), + mTotalAsyncPaints(0), + mOutstandingAsyncPaints(0), + mOutstandingAsyncEndTransaction(false), + mIsDelayingForAsyncPaints(false), + mSlowFlushCount(0), + mTotalFlushCount(0) { + 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) { + 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; + } + + // Flush async paints before we destroy texture data. + FlushAsyncPaints(); + + 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<PLayerTransactionChild*, 16> transactions; + ManagedPLayerTransactionChild(transactions); + for (int i = transactions.Length() - 1; i >= 0; --i) { + RefPtr<LayerTransactionChild> layers = + static_cast<LayerTransactionChild*>(transactions[i]); + layers->Destroy(); + } + + 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(); + } + + AutoTArray<PWebGPUChild*, 16> webGPUChildren; + ManagedPWebGPUChild(webGPUChildren); + for (PWebGPUChild* child : webGPUChildren) { + Unused << child->SendShutdown(); + } + + const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild(); + for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) { + RefPtr<TextureClient> texture = + TextureClient::AsTextureClient(iter.Get()->GetKey()); + + 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([&]() { return !sCompositorBridge; }); + } +} + +bool CompositorBridgeChild::LookupCompositorFrameMetrics( + const ScrollableLayerGuid::ViewID aId, FrameMetrics& aFrame) { + SharedFrameMetricsData* data = mFrameMetricsTable.Get(aId); + if (data) { + data->CopyFrameMetrics(&aFrame); + return true; + } + return false; +} + +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, + LayerManager* 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::ChildProcessHasCompositorBridge() { + return sCompositorBridge != nullptr; +} + +/* 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(); +} + +PLayerTransactionChild* CompositorBridgeChild::AllocPLayerTransactionChild( + const nsTArray<LayersBackend>& aBackendHints, const LayersId& aId) { + LayerTransactionChild* c = new LayerTransactionChild(aId); + c->AddIPDLReference(); + + return c; +} + +bool CompositorBridgeChild::DeallocPLayerTransactionChild( + PLayerTransactionChild* actor) { + LayersId childId = static_cast<LayerTransactionChild*>(actor)->GetId(); + ClearSharedFrameMetricsData(childId); + static_cast<LayerTransactionChild*>(actor)->ReleaseIPDLReference(); + return true; +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvInvalidateLayers( + const LayersId& aLayersId) { + if (mLayerManager) { + MOZ_ASSERT(!aLayersId.IsValid()); + FrameLayerBuilder::InvalidateAllLayers(mLayerManager); + } else if (aLayersId.IsValid()) { + if (dom::BrowserChild* child = dom::BrowserChild::GetFrom(aLayersId)) { + child->InvalidateLayers(); + } + } + return IPC_OK(); +} + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +static void CalculatePluginClip( + const LayoutDeviceIntRect& aBounds, + const nsTArray<LayoutDeviceIntRect>& aPluginClipRects, + const LayoutDeviceIntPoint& aContentOffset, + const LayoutDeviceIntRegion& aParentLayerVisibleRegion, + nsTArray<LayoutDeviceIntRect>& aResult, LayoutDeviceIntRect& aVisibleBounds, + bool& aPluginIsVisible) { + aPluginIsVisible = true; + LayoutDeviceIntRegion contentVisibleRegion; + // aPluginClipRects (plugin widget origin) - contains *visible* rects + for (uint32_t idx = 0; idx < aPluginClipRects.Length(); idx++) { + LayoutDeviceIntRect rect = aPluginClipRects[idx]; + // shift to content origin + rect.MoveBy(aBounds.X(), aBounds.Y()); + // accumulate visible rects + contentVisibleRegion.OrWith(rect); + } + // apply layers clip (window origin) + LayoutDeviceIntRegion region = aParentLayerVisibleRegion; + region.MoveBy(-aContentOffset.x, -aContentOffset.y); + contentVisibleRegion.AndWith(region); + if (contentVisibleRegion.IsEmpty()) { + aPluginIsVisible = false; + return; + } + // shift to plugin widget origin + contentVisibleRegion.MoveBy(-aBounds.X(), -aBounds.Y()); + for (auto iter = contentVisibleRegion.RectIter(); !iter.Done(); iter.Next()) { + const LayoutDeviceIntRect& rect = iter.Get(); + aResult.AppendElement(rect); + aVisibleBounds.UnionRect(aVisibleBounds, rect); + } +} +#endif + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvUpdatePluginConfigurations( + const LayoutDeviceIntPoint& aContentOffset, + const LayoutDeviceIntRegion& aParentLayerVisibleRegion, + nsTArray<PluginWindowData>&& aPlugins) { +#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) + MOZ_ASSERT_UNREACHABLE( + "CompositorBridgeChild::RecvUpdatePluginConfigurations" + " calls unexpected on this platform."); + return IPC_FAIL_NO_REASON(this); +#else + // Now that we are on the main thread, update plugin widget config. + // This should happen a little before we paint to the screen assuming + // the main thread is running freely. + DebugOnly<nsresult> rv; + MOZ_ASSERT(NS_IsMainThread()); + + // Tracks visible plugins we update, so we can hide any plugins we don't. + nsTArray<uintptr_t> visiblePluginIds; + nsIWidget* parent = nullptr; + for (uint32_t pluginsIdx = 0; pluginsIdx < aPlugins.Length(); pluginsIdx++) { + nsIWidget* widget = nsIWidget::LookupRegisteredPluginWindow( + aPlugins[pluginsIdx].windowId()); + if (!widget) { + NS_WARNING("Unexpected, plugin id not found!"); + continue; + } + if (!parent) { + parent = widget->GetParent(); + } + bool isVisible = aPlugins[pluginsIdx].visible(); + if (widget && !widget->Destroyed()) { + LayoutDeviceIntRect bounds; + LayoutDeviceIntRect visibleBounds; + // If the plugin is visible update it's geometry. + if (isVisible) { + // Set bounds (content origin) + bounds = aPlugins[pluginsIdx].bounds(); + nsTArray<LayoutDeviceIntRect> rectsOut; + // This call may change the value of isVisible + CalculatePluginClip(bounds, aPlugins[pluginsIdx].clip(), aContentOffset, + aParentLayerVisibleRegion, rectsOut, visibleBounds, + isVisible); + // content clipping region (widget origin) + rv = widget->SetWindowClipRegion(rectsOut, false); + NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure"); + // This will trigger a browser window paint event for areas uncovered + // by a child window move, and will call invalidate on the plugin + // parent window which the browser owns. The latter gets picked up in + // our OnPaint handler and forwarded over to the plugin process async. + widget->Resize(aContentOffset.x + bounds.X(), + aContentOffset.y + bounds.Y(), bounds.Width(), + bounds.Height(), true); + } + + widget->Enable(isVisible); + + // visible state - updated after clipping, prior to invalidating + widget->Show(isVisible); + + // Handle invalidation, this can be costly, avoid if it is not needed. + if (isVisible) { + // invalidate region (widget origin) +# if defined(XP_WIN) + // Work around for flash's crummy sandbox. See bug 762948. This call + // digs down into the window hirearchy, invalidating regions on + // windows owned by other processes. + mozilla::widget::WinUtils::InvalidatePluginAsWorkaround(widget, + visibleBounds); +# else + widget->Invalidate(visibleBounds); +# endif + visiblePluginIds.AppendElement(aPlugins[pluginsIdx].windowId()); + } + } + } + // Any plugins we didn't update need to be hidden, as they are + // not associated with visible content. + nsIWidget::UpdateRegisteredPluginWindowVisibility((uintptr_t)parent, + visiblePluginIds); + if (!mCanSend) { + return IPC_OK(); + } + SendRemotePluginsReady(); + return IPC_OK(); +#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) +} + +#if defined(XP_WIN) +static void ScheduleSendAllPluginsCaptured(CompositorBridgeChild* aThis, + nsISerialEventTarget* aThread) { + aThread->Dispatch(NewNonOwningRunnableMethod( + "CompositorBridgeChild::SendAllPluginsCaptured", aThis, + &CompositorBridgeChild::SendAllPluginsCaptured)); +} +#endif + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvCaptureAllPlugins( + const uintptr_t& aParentWidget) { +#if defined(XP_WIN) + MOZ_ASSERT(NS_IsMainThread()); + nsIWidget::CaptureRegisteredPlugins(aParentWidget); + + // Bounce the call to SendAllPluginsCaptured off the ImageBridgeChild thread, + // to make sure that the image updates on that thread have been processed. + ImageBridgeChild::GetSingleton()->GetThread()->Dispatch(NewRunnableFunction( + "ScheduleSendAllPluginsCapturedRunnable", &ScheduleSendAllPluginsCaptured, + this, NS_GetCurrentThread())); + return IPC_OK(); +#else + MOZ_ASSERT_UNREACHABLE( + "CompositorBridgeChild::RecvCaptureAllPlugins calls unexpected."); + return IPC_FAIL_NO_REASON(this); +#endif +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvHideAllPlugins( + const uintptr_t& aParentWidget) { +#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) + MOZ_ASSERT_UNREACHABLE( + "CompositorBridgeChild::RecvHideAllPlugins calls " + "unexpected on this platform."); + return IPC_FAIL_NO_REASON(this); +#else + MOZ_ASSERT(NS_IsMainThread()); + nsTArray<uintptr_t> list; + nsIWidget::UpdateRegisteredPluginWindowVisibility(aParentWidget, list); + if (!mCanSend) { + return IPC_OK(); + } + SendRemotePluginsReady(); + return IPC_OK(); +#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvDidComposite( + const LayersId& aId, const TransactionId& aTransactionId, + const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd) { + // Hold a reference to keep texture pools alive. See bug 1387799 + const auto texturePools = mTexturePools.Clone(); + + if (mLayerManager) { + MOZ_ASSERT(!aId.IsValid()); + MOZ_ASSERT(mLayerManager->GetBackendType() == + LayersBackend::LAYERS_CLIENT || + mLayerManager->GetBackendType() == LayersBackend::LAYERS_WR); + // Hold a reference to keep LayerManager alive. See Bug 1242668. + RefPtr<LayerManager> m = mLayerManager; + m->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd); + } else if (aId.IsValid()) { + RefPtr<dom::BrowserChild> child = dom::BrowserChild::GetFrom(aId); + if (child) { + child->DidComposite(aTransactionId, 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 << "Receive IPC close with reason=AbnormalShutdown"; + } + + { + // We take the lock to update these fields, since they are read from the + // paint thread. We don't need the lock to init them, since that happens + // on the main thread before the paint thread can ever grab a reference + // to the CompositorBridge object. + // + // Note that it is useful to take this lock for one other reason: It also + // tells us whether GetIPCChannel is safe to call. If we access the IPC + // channel within this lock, when mCanSend is true, then we know it has not + // been zapped by IPDL. + MonitorAutoLock lock(mPaintLock); + mCanSend = false; + mActorDestroyed = true; + } + + if (mProcessToken && XRE_IsParentProcess()) { + GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); + } +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvSharedCompositorFrameMetrics( + const mozilla::ipc::SharedMemoryBasic::Handle& metrics, + const CrossProcessMutexHandle& handle, const LayersId& aLayersId, + const uint32_t& aAPZCId) { + SharedFrameMetricsData* data = + new SharedFrameMetricsData(metrics, handle, aLayersId, aAPZCId); + mFrameMetricsTable.Put(data->GetViewID(), data); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +CompositorBridgeChild::RecvReleaseSharedCompositorFrameMetrics( + const ViewID& aId, const uint32_t& aAPZCId) { + if (auto entry = mFrameMetricsTable.Lookup(aId)) { + // The SharedFrameMetricsData may have been removed previously if + // a SharedFrameMetricsData with the same ViewID but later APZCId had + // been store and over wrote it. + if (entry.Data()->GetAPZCId() == aAPZCId) { + entry.Remove(); + } + } + return IPC_OK(); +} + +CompositorBridgeChild::SharedFrameMetricsData::SharedFrameMetricsData( + const ipc::SharedMemoryBasic::Handle& metrics, + const CrossProcessMutexHandle& handle, const LayersId& aLayersId, + const uint32_t& aAPZCId) + : mMutex(nullptr), mLayersId(aLayersId), mAPZCId(aAPZCId) { + mBuffer = new ipc::SharedMemoryBasic; + mBuffer->SetHandle(metrics, ipc::SharedMemory::RightsReadOnly); + mBuffer->Map(sizeof(FrameMetrics)); + mMutex = new CrossProcessMutex(handle); + MOZ_COUNT_CTOR(SharedFrameMetricsData); +} + +CompositorBridgeChild::SharedFrameMetricsData::~SharedFrameMetricsData() { + // When the hash table deletes the class, delete + // the shared memory and mutex. + delete mMutex; + mBuffer = nullptr; + MOZ_COUNT_DTOR(SharedFrameMetricsData); +} + +void CompositorBridgeChild::SharedFrameMetricsData::CopyFrameMetrics( + FrameMetrics* aFrame) { + const FrameMetrics* frame = + static_cast<const FrameMetrics*>(mBuffer->memory()); + MOZ_ASSERT(frame); + mMutex->Lock(); + *aFrame = *frame; + mMutex->Unlock(); +} + +ScrollableLayerGuid::ViewID +CompositorBridgeChild::SharedFrameMetricsData::GetViewID() { + const FrameMetrics* frame = + static_cast<const FrameMetrics*>(mBuffer->memory()); + MOZ_ASSERT(frame); + // Not locking to read of mScrollId since it should not change after being + // initially set. + return frame->GetScrollId(); +} + +LayersId CompositorBridgeChild::SharedFrameMetricsData::GetLayersId() const { + return mLayersId; +} + +uint32_t CompositorBridgeChild::SharedFrameMetricsData::GetAPZCId() { + return mAPZCId; +} + +mozilla::ipc::IPCResult CompositorBridgeChild::RecvRemotePaintIsReady() { + // Used on the content thread, this bounces the message to the + // BrowserParent (via the BrowserChild) if the notification was previously + // requested. XPCOM gives a soup of compiler errors when trying to + // do_QueryReference so I'm using static_cast<> + MOZ_LAYERS_LOG( + ("[RemoteGfx] CompositorBridgeChild received RemotePaintIsReady")); + RefPtr<nsIBrowserChild> iBrowserChild(do_QueryReferent(mWeakBrowserChild)); + if (!iBrowserChild) { + MOZ_LAYERS_LOG( + ("[RemoteGfx] Note: BrowserChild was released before " + "RemotePaintIsReady. " + "MozAfterRemotePaint will not be sent to listener.")); + return IPC_OK(); + } + BrowserChild* browserChild = static_cast<BrowserChild*>(iBrowserChild.get()); + MOZ_ASSERT(browserChild); + Unused << browserChild->SendRemotePaintIsReady(); + mWeakBrowserChild = nullptr; + return IPC_OK(); +} + +void CompositorBridgeChild::RequestNotifyAfterRemotePaint( + BrowserChild* aBrowserChild) { + MOZ_ASSERT(aBrowserChild, + "NULL BrowserChild not allowed in " + "CompositorBridgeChild::RequestNotifyAfterRemotePaint"); + mWeakBrowserChild = + do_GetWeakReference(static_cast<dom::BrowserChild*>(aBrowserChild)); + if (!mCanSend) { + return; + } + Unused << SendRequestNotifyAfterRemotePaint(); +} + +void CompositorBridgeChild::CancelNotifyAfterRemotePaint( + BrowserChild* aBrowserChild) { + RefPtr<nsIBrowserChild> iBrowserChild(do_QueryReferent(mWeakBrowserChild)); + if (!iBrowserChild) { + return; + } + BrowserChild* browserChild = static_cast<BrowserChild*>(iBrowserChild.get()); + if (browserChild == aBrowserChild) { + mWeakBrowserChild = nullptr; + } +} + +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::SendNotifyChildCreated( + const LayersId& id, CompositorOptions* aOptions) { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendNotifyChildCreated(id, aOptions); +} + +bool CompositorBridgeChild::SendAdoptChild(const LayersId& id) { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendAdoptChild(id); +} + +bool CompositorBridgeChild::SendMakeSnapshot( + const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect) { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendMakeSnapshot(inSnapshot, dirtyRect); +} + +bool CompositorBridgeChild::SendFlushRendering() { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendFlushRendering(); +} + +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); +} + +bool CompositorBridgeChild::SendNotifyRegionInvalidated( + const nsIntRegion& region) { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendNotifyRegionInvalidated(region); +} + +bool CompositorBridgeChild::SendRequestNotifyAfterRemotePaint() { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendRequestNotifyAfterRemotePaint(); +} + +bool CompositorBridgeChild::SendAllPluginsCaptured() { + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendAllPluginsCaptured(); +} + +PTextureChild* CompositorBridgeChild::AllocPTextureChild( + const SurfaceDescriptor&, const 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; + } + case AsyncParentMessageData::TOpDeliverReleaseFence: { + // Release fences are delivered via ImageBridge. + // Since some TextureClients are recycled without recycle callback. + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + 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; + } + +#ifdef MOZ_WIDGET_ANDROID + auto bufferId = aClient->GetInternalData()->GetBufferId(); + if (bufferId.isSome()) { + MOZ_ASSERT(aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END); + AndroidHardwareBufferManager::Get()->HoldUntilNotifyNotUsed( + bufferId.ref(), GetFwdTransactionId(), /* aUsesImageBridge */ false); + } +#endif + + 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); +} + +TextureClientPool* CompositorBridgeChild::GetTexturePool( + KnowsCompositor* aAllocator, SurfaceFormat aFormat, TextureFlags aFlags) { + for (size_t i = 0; i < mTexturePools.Length(); i++) { + if (mTexturePools[i]->GetBackend() == + aAllocator->GetCompositorBackendType() && + mTexturePools[i]->GetMaxTextureSize() == + aAllocator->GetMaxTextureSize() && + mTexturePools[i]->GetFormat() == aFormat && + mTexturePools[i]->GetFlags() == aFlags) { + return mTexturePools[i]; + } + } + + mTexturePools.AppendElement(new TextureClientPool( + aAllocator, aFormat, gfx::gfxVars::TileSize(), aFlags, + StaticPrefs::layers_tile_pool_shrink_timeout_AtStartup(), + StaticPrefs::layers_tile_pool_clear_timeout_AtStartup(), + StaticPrefs::layers_tile_initial_pool_size_AtStartup(), + StaticPrefs::layers_tile_pool_unused_size_AtStartup(), this)); + + return mTexturePools.LastElement(); +} + +void CompositorBridgeChild::HandleMemoryPressure() { + for (size_t i = 0; i < mTexturePools.Length(); i++) { + mTexturePools[i]->Clear(); + } +} + +void CompositorBridgeChild::ClearTexturePool() { + for (size_t i = 0; i < mTexturePools.Length(); i++) { + mTexturePools[i]->Clear(); + } +} + +FixedSizeSmallShmemSectionAllocator* +CompositorBridgeChild::GetTileLockAllocator() { + if (!IPCOpen()) { + return nullptr; + } + + if (!mSectionAllocator) { + mSectionAllocator = new FixedSizeSmallShmemSectionAllocator(this); + } + return mSectionAllocator; +} + +PTextureChild* CompositorBridgeChild::CreateTexture( + const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) { + PTextureChild* textureChild = + AllocPTextureChild(aSharedData, aReadLock, aLayersBackend, aFlags, + LayersId{0} /* FIXME */, aSerial, aExternalImageId); + + // Do the DOM labeling. + if (aTarget) { + SetEventTargetForActor(textureChild, aTarget); + } + + return SendPTextureConstructor( + textureChild, aSharedData, 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; + } + } +} + +RefPtr<webgpu::WebGPUChild> CompositorBridgeChild::GetWebGPUChild() { + MOZ_ASSERT(gfx::gfxConfig::IsEnabled(gfx::Feature::WEBGPU)); + if (!mWebGPUChild) { + webgpu::PWebGPUChild* bridge = SendPWebGPUConstructor(); + mWebGPUChild = static_cast<webgpu::WebGPUChild*>(bridge); + } + + return mWebGPUChild; +} + +bool CompositorBridgeChild::AllocUnsafeShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + ShmemAllocated(this); + return PCompositorBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem); +} + +bool CompositorBridgeChild::AllocShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + ShmemAllocated(this); + return PCompositorBridgeChild::AllocShmem(aSize, aType, 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; +} + +// - + +void CompositorBridgeChild::WillEndTransaction() { ResetShmemCounter(); } + +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); + ClearSharedFrameMetricsData(wr::AsLayersId(child->GetPipeline())); + child->ReleaseIPDLReference(); + return true; +} + +webgpu::PWebGPUChild* CompositorBridgeChild::AllocPWebGPUChild() { + webgpu::WebGPUChild* child = new webgpu::WebGPUChild(); + child->AddIPDLReference(); + return child; +} + +bool CompositorBridgeChild::DeallocPWebGPUChild(webgpu::PWebGPUChild* aActor) { + webgpu::WebGPUChild* child = static_cast<webgpu::WebGPUChild*>(aActor); + child->ReleaseIPDLReference(); + return true; +} + +void CompositorBridgeChild::ClearSharedFrameMetricsData(LayersId aLayersId) { + for (auto iter = mFrameMetricsTable.Iter(); !iter.Done(); iter.Next()) { + auto data = iter.UserData(); + if (data->GetLayersId() == aLayersId) { + iter.Remove(); + } + } +} + +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()); +} + +void CompositorBridgeChild::FlushAsyncPaints() { + MOZ_ASSERT(NS_IsMainThread()); + + Maybe<TimeStamp> start; + if (XRE_IsContentProcess() && gfx::gfxVars::UseOMTP()) { + start = Some(TimeStamp::Now()); + } + + { + MonitorAutoLock lock(mPaintLock); + while (mOutstandingAsyncPaints > 0 || mOutstandingAsyncEndTransaction) { + lock.Wait(); + } + + // It's now safe to free any TextureClients that were used during painting. + mTextureClientsForAsyncPaint.Clear(); + } + + if (start) { + float ms = (TimeStamp::Now() - start.value()).ToMilliseconds(); + + // Anything above 200us gets recorded. + if (ms >= 0.2) { + mSlowFlushCount++; + Telemetry::Accumulate(Telemetry::GFX_OMTP_PAINT_WAIT_TIME, int32_t(ms)); + } + mTotalFlushCount++; + + double ratio = double(mSlowFlushCount) / double(mTotalFlushCount); + Telemetry::ScalarSet(Telemetry::ScalarID::GFX_OMTP_PAINT_WAIT_RATIO, + uint32_t(ratio * 100 * 100)); + } +} + +void CompositorBridgeChild::NotifyBeginAsyncPaint(PaintTask* aTask) { + MOZ_ASSERT(NS_IsMainThread()); + + MonitorAutoLock lock(mPaintLock); + + if (mTotalAsyncPaints == 0) { + mAsyncTransactionBegin = TimeStamp::Now(); + } + mTotalAsyncPaints += 1; + + // We must not be waiting for paints or buffer copying to complete yet. This + // would imply we started a new paint without waiting for a previous one, + // which could lead to incorrect rendering or IPDL deadlocks. + MOZ_ASSERT(!mIsDelayingForAsyncPaints); + + mOutstandingAsyncPaints++; + + // Mark texture clients that they are being used for async painting, and + // make sure we hold them alive on the main thread. + for (auto& client : aTask->mClients) { + client->AddPaintThreadRef(); + mTextureClientsForAsyncPaint.AppendElement(client); + }; +} + +// Must only be called from the paint thread. Notifies the CompositorBridge +// that the paint thread has finished an asynchronous paint request. +bool CompositorBridgeChild::NotifyFinishedAsyncWorkerPaint(PaintTask* aTask) { + MOZ_ASSERT(PaintThread::Get()->IsOnPaintWorkerThread()); + + MonitorAutoLock lock(mPaintLock); + mOutstandingAsyncPaints--; + + for (auto& client : aTask->mClients) { + client->DropPaintThreadRef(); + }; + aTask->DropTextureClients(); + + // If the main thread has completed queuing work and this was the + // last paint, then it is time to end the layer transaction and sync + return mOutstandingAsyncEndTransaction && mOutstandingAsyncPaints == 0; +} + +bool CompositorBridgeChild::NotifyBeginAsyncEndLayerTransaction( + SyncObjectClient* aSyncObject) { + MOZ_ASSERT(NS_IsMainThread()); + MonitorAutoLock lock(mPaintLock); + + MOZ_ASSERT(!mOutstandingAsyncEndTransaction); + mOutstandingAsyncEndTransaction = true; + mOutstandingAsyncSyncObject = aSyncObject; + return mOutstandingAsyncPaints == 0; +} + +void CompositorBridgeChild::NotifyFinishedAsyncEndLayerTransaction() { + MOZ_ASSERT(PaintThread::Get()->IsOnPaintWorkerThread()); + + if (mOutstandingAsyncSyncObject) { + mOutstandingAsyncSyncObject->Synchronize(); + mOutstandingAsyncSyncObject = nullptr; + } + + MonitorAutoLock lock(mPaintLock); + + if (mTotalAsyncPaints > 0) { + float tenthMs = + (TimeStamp::Now() - mAsyncTransactionBegin).ToMilliseconds() * 10; + Telemetry::Accumulate(Telemetry::GFX_OMTP_PAINT_TASK_COUNT, + int32_t(mTotalAsyncPaints)); + Telemetry::Accumulate(Telemetry::GFX_OMTP_PAINT_TIME, int32_t(tenthMs)); + mTotalAsyncPaints = 0; + } + + // Since this should happen after ALL paints are done and + // at the end of a transaction, this should always be true. + MOZ_RELEASE_ASSERT(mOutstandingAsyncPaints == 0); + MOZ_ASSERT(mOutstandingAsyncEndTransaction); + + mOutstandingAsyncEndTransaction = false; + + // It's possible that we painted so fast that the main thread never reached + // the code that starts delaying messages. If so, mIsDelayingForAsyncPaints + // will be false, and we can safely return. + if (mIsDelayingForAsyncPaints) { + ResumeIPCAfterAsyncPaint(); + } + + // Notify the main thread in case it's blocking. We do this unconditionally + // to avoid deadlocking. + lock.Notify(); +} + +void CompositorBridgeChild::ResumeIPCAfterAsyncPaint() { + // Note: the caller is responsible for holding the lock. + mPaintLock.AssertCurrentThreadOwns(); + MOZ_ASSERT(PaintThread::Get()->IsOnPaintWorkerThread()); + MOZ_ASSERT(mOutstandingAsyncPaints == 0); + MOZ_ASSERT(!mOutstandingAsyncEndTransaction); + MOZ_ASSERT(mIsDelayingForAsyncPaints); + + mIsDelayingForAsyncPaints = false; + + // It's also possible that the channel has shut down already. + if (!mCanSend || mActorDestroyed) { + return; + } + + GetIPCChannel()->StopPostponingSends(); +} + +void CompositorBridgeChild::PostponeMessagesIfAsyncPainting() { + MOZ_ASSERT(NS_IsMainThread()); + + MonitorAutoLock lock(mPaintLock); + + MOZ_ASSERT(!mIsDelayingForAsyncPaints); + + // We need to wait for async paints and the async end transaction as + // it will do texture synchronization + if (mOutstandingAsyncPaints > 0 || mOutstandingAsyncEndTransaction) { + mIsDelayingForAsyncPaints = true; + GetIPCChannel()->BeginPostponingSends(); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h new file mode 100644 index 0000000000..ebfd0f10c0 --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeChild.h @@ -0,0 +1,427 @@ +/* -*- 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/layers/PaintThread.h" // for PaintThread +#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 webgpu { +class PWebGPUChild; +class WebGPUChild; +} // namespace webgpu + +namespace widget { +class CompositorWidget; +} // namespace widget + +namespace layers { + +using mozilla::dom::BrowserChild; + +class IAPZCTreeManager; +class APZCTreeManagerChild; +class CanvasChild; +class ClientLayerManager; +class CompositorBridgeParent; +class CompositorManagerChild; +class CompositorOptions; +class LayerManager; +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, LayerManager* aLayerManager, + uint32_t aNamespace); + + void Destroy(); + + /** + * Lookup the FrameMetrics shared by the compositor process with the + * associated ScrollableLayerGuid::ViewID. The returned FrameMetrics is used + * in progressive paint calculations. + */ + bool LookupCompositorFrameMetrics(const ScrollableLayerGuid::ViewID aId, + FrameMetrics&); + + static CompositorBridgeChild* Get(); + + static bool ChildProcessHasCompositorBridge(); + + // 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 TransactionId& aTransactionId, + const TimeStamp& aCompositeStart, + const TimeStamp& aCompositeEnd); + + mozilla::ipc::IPCResult RecvNotifyFrameStats( + nsTArray<FrameStats>&& aFrameStats); + + mozilla::ipc::IPCResult RecvInvalidateLayers(const LayersId& aLayersId); + + mozilla::ipc::IPCResult RecvUpdatePluginConfigurations( + const LayoutDeviceIntPoint& aContentOffset, + const LayoutDeviceIntRegion& aVisibleRegion, + nsTArray<PluginWindowData>&& aPlugins); + + mozilla::ipc::IPCResult RecvCaptureAllPlugins(const uintptr_t& aParentWidget); + + mozilla::ipc::IPCResult RecvHideAllPlugins(const uintptr_t& aParentWidget); + + mozilla::ipc::IPCResult RecvNotifyJankedAnimations( + const LayersId& aLayersId, nsTArray<uint64_t>&& aJankedAnimations); + + PTextureChild* AllocPTextureChild( + const SurfaceDescriptor& aSharedData, const 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, + const ReadLockDescriptor& aReadLock, + LayersBackend aLayersBackend, + TextureFlags aFlags, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId, + nsISerialEventTarget* aTarget) override; + + already_AddRefed<CanvasChild> GetCanvasChild() final; + + void EndCanvasTransaction(); + + RefPtr<webgpu::WebGPUChild> GetWebGPUChild(); + + /** + * Request that the parent tell us when graphics are ready on GPU. + * When we get that message, we bounce it to the BrowserParent via + * the BrowserChild + * @param browserChild The object to bounce the note to. Non-NULL. + */ + void RequestNotifyAfterRemotePaint(BrowserChild* aBrowserChild); + + void CancelNotifyAfterRemotePaint(BrowserChild* aBrowserChild); + + // 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 SendNotifyChildCreated(const LayersId& id, CompositorOptions* aOptions); + bool SendAdoptChild(const LayersId& id); + bool SendMakeSnapshot(const SurfaceDescriptor& inSnapshot, + const gfx::IntRect& dirtyRect); + bool SendFlushRendering(); + bool SendGetTileSize(int32_t* tileWidth, int32_t* tileHeight); + bool SendStartFrameTimeRecording(const int32_t& bufferSize, + uint32_t* startIndex); + bool SendStopFrameTimeRecording(const uint32_t& startIndex, + nsTArray<float>* intervals); + bool SendNotifyRegionInvalidated(const nsIntRegion& region); + bool SendRequestNotifyAfterRemotePaint(); + bool SendAllPluginsCaptured(); + 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; + + TextureClientPool* GetTexturePool(KnowsCompositor* aAllocator, + gfx::SurfaceFormat aFormat, + TextureFlags aFlags); + void ClearTexturePool(); + + FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() override; + + void HandleMemoryPressure(); + + nsISerialEventTarget* GetThread() const override { return mThread; } + + base::ProcessId GetParentPid() const override { return OtherPid(); } + + bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) override; + bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + 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); + + void WillEndTransaction(); + + PWebRenderBridgeChild* AllocPWebRenderBridgeChild( + const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize&, + const WindowKind&); + bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor); + + webgpu::PWebGPUChild* AllocPWebGPUChild(); + bool DeallocPWebGPUChild(webgpu::PWebGPUChild* aActor); + + wr::MaybeExternalImageId GetNextExternalImageId() override; + + wr::PipelineId GetNextPipelineId(); + + // Must only be called from the main thread. Ensures that any paints from + // previous frames have been flushed. The main thread blocks until the + // operation completes. + void FlushAsyncPaints(); + + // Must only be called from the main thread. Notifies the CompositorBridge + // that the paint thread is going to begin painting asynchronously. + void NotifyBeginAsyncPaint(PaintTask* aTask); + + // Must only be called from the paint thread. Notifies the CompositorBridge + // that the paint thread has finished an asynchronous paint request. + bool NotifyFinishedAsyncWorkerPaint(PaintTask* aTask); + + // Must only be called from the main thread. Notifies the CompositorBridge + // that all work has been submitted to the paint thread or paint worker + // threads, and returns whether all paints are completed. If this returns + // true, then an AsyncEndLayerTransaction must be queued, otherwise once + // NotifyFinishedAsyncWorkerPaint returns true, an AsyncEndLayerTransaction + // must be executed. + bool NotifyBeginAsyncEndLayerTransaction(SyncObjectClient* aSyncObject); + + // Must only be called from the paint thread. Notifies the CompositorBridge + // that the paint thread has finished all async paints and and may do the + // requested texture sync and resume sending messages. + void NotifyFinishedAsyncEndLayerTransaction(); + + // Must only be called from the main thread. Notifies the CompoistorBridge + // that a transaction is about to be sent, and if the paint thread is + // currently painting, to begin delaying IPC messages. + void PostponeMessagesIfAsyncPainting(); + + 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(); + + PLayerTransactionChild* AllocPLayerTransactionChild( + const nsTArray<LayersBackend>& aBackendHints, const LayersId& aId); + + bool DeallocPLayerTransactionChild(PLayerTransactionChild* aChild); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + mozilla::ipc::IPCResult RecvSharedCompositorFrameMetrics( + const mozilla::ipc::SharedMemoryBasic::Handle& metrics, + const CrossProcessMutexHandle& handle, const LayersId& aLayersId, + const uint32_t& aAPZCId); + + mozilla::ipc::IPCResult RecvReleaseSharedCompositorFrameMetrics( + const ViewID& aId, const uint32_t& aAPZCId); + + mozilla::ipc::IPCResult RecvRemotePaintIsReady(); + + 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(); + + void ClearSharedFrameMetricsData(LayersId aLayersId); + + // Class used to store the shared FrameMetrics, mutex, and APZCId in a hash + // table + class SharedFrameMetricsData final { + public: + SharedFrameMetricsData( + const mozilla::ipc::SharedMemoryBasic::Handle& metrics, + const CrossProcessMutexHandle& handle, const LayersId& aLayersId, + const uint32_t& aAPZCId); + + ~SharedFrameMetricsData(); + + void CopyFrameMetrics(FrameMetrics* aFrame); + ScrollableLayerGuid::ViewID GetViewID(); + LayersId GetLayersId() const; + uint32_t GetAPZCId(); + + private: + // Pointer to the class that allows access to the shared memory that + // contains the shared FrameMetrics + RefPtr<mozilla::ipc::SharedMemoryBasic> mBuffer; + CrossProcessMutex* mMutex; + LayersId mLayersId; + // Unique ID of the APZC that is sharing the FrameMetrics + uint32_t mAPZCId; + }; + + RefPtr<CompositorManagerChild> mCompositorManager; + + RefPtr<LayerManager> 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; + + // The ViewID of the FrameMetrics is used as the key for this hash table. + // While this should be safe to use since the ViewID is unique + nsClassHashtable<nsUint64HashKey, SharedFrameMetricsData> mFrameMetricsTable; + + // Weakly hold the BrowserChild that made a request to be alerted when + // the transaction has been received. + nsWeakPtr mWeakBrowserChild; // type is BrowserChild + + 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 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; + + 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; + + // Off-Main-Thread Painting state. This covers access to the OMTP-related + // state below. + Monitor mPaintLock; + + // Contains the number of asynchronous paints that were queued since the + // beginning of the last async transaction, and the time stamp of when + // that was + size_t mTotalAsyncPaints; + TimeStamp mAsyncTransactionBegin; + + // Contains the number of outstanding asynchronous paints tied to a + // PLayerTransaction on this bridge. This is R/W on both the main and paint + // threads, and must be accessed within the paint lock. + size_t mOutstandingAsyncPaints; + + // Whether we are waiting for an async paint end transaction + bool mOutstandingAsyncEndTransaction; + RefPtr<SyncObjectClient> mOutstandingAsyncSyncObject; + + // True if this CompositorBridge is currently delaying its messages until the + // paint thread completes. This is R/W on both the main and paint threads, and + // must be accessed within the paint lock. + bool mIsDelayingForAsyncPaints; + + uintptr_t mSlowFlushCount; + uintptr_t mTotalFlushCount; + + RefPtr<CanvasChild> mCanvasChild; + + RefPtr<webgpu::WebGPUChild> mWebGPUChild; +}; + +} // 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..130cf28d7b --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -0,0 +1,2990 @@ +/* -*- 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 "LayerTransactionParent.h" // for LayerTransactionParent +#include "RenderTrace.h" // for RenderTraceLayers +#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/ipc/Transport.h" // for Transport +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUParent.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/AsyncCompositionManager.h" +#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor +#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/LayerManagerComposite.h" +#include "mozilla/layers/LayerManagerMLGPU.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/OMTASampler.h" +#include "mozilla/layers/PLayerTransactionParent.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/webgpu/WebGPUParent.h" +#include "mozilla/webrender/RenderThread.h" +#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService +#include "mozilla/mozalloc.h" // for operator new, etc +#include "mozilla/PerfStats.h" +#include "mozilla/PodOperations.h" +#include "mozilla/Telemetry.h" +#ifdef MOZ_WIDGET_GTK +# include "basic/X11BasicCompositor.h" // for X11BasicCompositor +#endif +#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 "GeckoProfiler.h" +#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 + +#include "LayerScope.h" + +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(); } + +void CompositorBridgeParentBase::NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) { + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); + if (!texture) { + return; + } + +#ifdef MOZ_WIDGET_ANDROID + if (texture->GetAndroidHardwareBuffer()) { + MOZ_ASSERT(texture->GetFlags() & TextureFlags::RECYCLE); + ImageBridgeParent::NotifyBufferNotUsedOfCompositorBridge( + GetChildProcessId(), texture, aTransactionId); + } +#endif + + 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::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem); +} + +bool CompositorBridgeParentBase::AllocUnsafeShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem); +} + +bool CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem) { + return PCompositorBridgeParent::DeallocShmem(aShmem); +} + +base::ProcessId CompositorBridgeParentBase::RemotePid() { return OtherPid(); } + +bool CompositorBridgeParentBase::StartSharingMetrics( + ipc::SharedMemoryBasic::Handle aHandle, + CrossProcessMutexHandle aMutexHandle, LayersId aLayersId, + uint32_t aApzcId) { + if (!CompositorThreadHolder::IsInCompositorThread()) { + MOZ_ASSERT(CompositorThread()); + CompositorThread()->Dispatch( + NewRunnableMethod<ipc::SharedMemoryBasic::Handle, + CrossProcessMutexHandle, LayersId, uint32_t>( + "layers::CompositorBridgeParent::StartSharingMetrics", this, + &CompositorBridgeParentBase::StartSharingMetrics, aHandle, + aMutexHandle, aLayersId, aApzcId)); + return true; + } + + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (!mCanSend) { + return false; + } + return PCompositorBridgeParent::SendSharedCompositorFrameMetrics( + aHandle, aMutexHandle, aLayersId, aApzcId); +} + +bool CompositorBridgeParentBase::StopSharingMetrics( + ScrollableLayerGuid::ViewID aScrollId, uint32_t aApzcId) { + if (!CompositorThreadHolder::IsInCompositorThread()) { + MOZ_ASSERT(CompositorThread()); + CompositorThread()->Dispatch( + NewRunnableMethod<ScrollableLayerGuid::ViewID, uint32_t>( + "layers::CompositorBridgeParent::StopSharingMetrics", this, + &CompositorBridgeParentBase::StopSharingMetrics, aScrollId, + aApzcId)); + return true; + } + + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (!mCanSend) { + return false; + } + return PCompositorBridgeParent::SendReleaseSharedCompositorFrameMetrics( + aScrollId, aApzcId); +} + +CompositorBridgeParent::LayerTreeState::LayerTreeState() + : mApzcTreeManagerParent(nullptr), + mParent(nullptr), + mLayerManager(nullptr), + mContentCompositorBridgeParent(nullptr), + mLayerTree(nullptr), + mUpdatedPluginDataAvailable(false) {} + +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... + sIndirectLayerTrees.clear(); +} + +#ifdef COMPOSITOR_PERFORMANCE_WARNING +static int32_t CalculateCompositionFrameRate() { + // Used when layout.frame_rate is -1. Needs to be kept in sync with + // DEFAULT_FRAME_RATE in nsRefreshDriver.cpp. + // TODO: This should actually return the vsync rate. + const int32_t defaultFrameRate = 60; + int32_t compositionFrameRatePref = + StaticPrefs::layers_offmainthreadcomposition_frame_rate(); + if (compositionFrameRatePref < 0) { + // Use the same frame rate for composition as for layout. + int32_t layoutFrameRatePref = StaticPrefs::layout_frame_rate(); + if (layoutFrameRatePref < 0) { + // TODO: The main thread frame scheduling code consults the actual + // monitor refresh rate in this case. We should do the same. + return defaultFrameRate; + } + return layoutFrameRatePref; + } + return compositionFrameRatePref; +} +#endif + +CompositorBridgeParent::CompositorBridgeParent( + CompositorManagerParent* aManager, CSSToLayoutDeviceScale aScale, + const TimeDuration& aVsyncRate, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) + : CompositorBridgeParentBase(aManager), + mWidget(nullptr), + mScale(aScale), + mVsyncRate(aVsyncRate), + mPendingTransaction{0}, + mPaused(false), + mHaveCompositionRecorder(false), + mIsForcedFirstPaint(false), + mUseExternalSurfaceSize(aUseExternalSurfaceSize), + mEGLSurfaceSize(aSurfaceSize), + mOptions(aOptions), + mPauseCompositionMonitor("PauseCompositionMonitor"), + mResumeCompositionMonitor("ResumeCompositionMonitor"), + mCompositorBridgeID(0), + mRootLayerTreeID{0}, + mOverrideComposeReadiness(false), + mForceCompositionTask(nullptr), + mCompositorScheduler(nullptr), + mAnimationStorage(nullptr), + mPaintTime(TimeDuration::Forever()) +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + , + mLastPluginUpdateLayerTreeId{0}, + mDeferPluginWindows(false), + mPluginWindowsHidden(false) +#endif +{ +} + +void CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget, + const LayersId& aLayerTreeId) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + mWidget = aWidget; + mRootLayerTreeID = aLayerTreeId; + + Initialize(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvInitialize( + const LayersId& aRootLayerTreeId) { + MOZ_ASSERT(XRE_IsGPUProcess()); + + mRootLayerTreeID = aRootLayerTreeId; +#ifdef XP_WIN + if (XRE_IsGPUProcess()) { + mWidget->AsWindows()->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, mOptions.UseWebRender()); + mApzSampler = new APZSampler(mApzcTreeManager, mOptions.UseWebRender()); + mApzUpdater = new APZUpdater(mApzcTreeManager, mOptions.UseWebRender()); + } + + if (mOptions.UseWebRender()) { + 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; + } + + LayerScope::SetPixelScale(mScale.scale); + + if (!mOptions.UseWebRender()) { + mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget); + } +} + +LayersId CompositorBridgeParent::RootLayerTreeId() { + MOZ_ASSERT(mRootLayerTreeID.IsValid()); + return mRootLayerTreeID; +} + +CompositorBridgeParent::~CompositorBridgeParent() { + 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(); + } +} + +void CompositorBridgeParent::ForceIsFirstPaint() { + if (mWrBridge) { + mIsForcedFirstPaint = true; + } else { + mCompositionManager->ForceIsFirstPaint(); + } +} + +void CompositorBridgeParent::StopAndClearResources() { + if (mForceCompositionTask) { + mForceCompositionTask->Cancel(); + mForceCompositionTask = nullptr; + } + + 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; + } + + // Ensure that the layer manager is destroyed before CompositorBridgeChild. + if (mLayerManager) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([this](LayerTreeState* lts, LayersId) -> void { + mLayerManager->ClearCachedResources(lts->mRoot); + lts->mLayerManager = nullptr; + lts->mParent = nullptr; + }); + mLayerManager->Destroy(); + mLayerManager = nullptr; + mCompositionManager = 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; + } + } + + if (mCompositor) { + mCompositor->Destroy(); + mCompositor = 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::RecvMakeSnapshot( + const SurfaceDescriptor& aInSnapshot, const gfx::IntRect& aRect) { + RefPtr<DrawTarget> target = + GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO); + MOZ_ASSERT(target); + if (!target) { + // We kill the content process rather than have it continue with an invalid + // snapshot, that may be too harsh and we could decide to return some sort + // of error to the child process and let it deal with it... + return IPC_FAIL_NO_REASON(this); + } + ForceComposeToTarget(target, &aRect); + return IPC_OK(); +} + +mozilla::ipc::IPCResult +CompositorBridgeParent::RecvWaitOnTransactionProcessed() { + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRendering() { + if (mWrBridge) { + mWrBridge->FlushRendering(); + return IPC_OK(); + } + + if (mCompositorScheduler->NeedsComposite()) { + CancelCurrentCompositeTask(); + ForceComposeToTarget(nullptr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRenderingAsync() { + if (mWrBridge) { + mWrBridge->FlushRendering(false); + return IPC_OK(); + } + + return RecvFlushRendering(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvForcePresent() { + if (mWrBridge) { + mWrBridge->ScheduleForcedGenerateFrame(); + } + // During the shutdown sequence mLayerManager may be null + if (mLayerManager) { + mLayerManager->ForcePresent(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyRegionInvalidated( + const nsIntRegion& aRegion) { + if (mLayerManager) { + mLayerManager->AddInvalidRegion(aRegion); + } + return IPC_OK(); +} + +void CompositorBridgeParent::Invalidate() { + if (mLayerManager) { + mLayerManager->InvalidateAll(); + } +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvStartFrameTimeRecording( + const int32_t& aBufferSize, uint32_t* aOutStartIndex) { + if (mLayerManager) { + *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize); + } else 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 (mLayerManager) { + mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals); + } else if (mWrBridge) { + mWrBridge->StopFrameTimeRecording(aStartIndex, *intervals); + } + return IPC_OK(); +} + +void CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) { + mCanSend = false; + + StopAndClearResources(); + + RemoveCompositor(mCompositorBridgeID); + + mCompositionManager = nullptr; + + { // 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() { + MOZ_ASSERT(CompositorThread()); + CompositorThread()->Dispatch( + NewRunnableMethod("layers::CompositorBridgeParent::ScheduleComposition", + this, &CompositorBridgeParent::ScheduleComposition)); +} + +void CompositorBridgeParent::PauseComposition() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "PauseComposition() can only be called on the compositor thread"); + + MonitorAutoLock lock(mPauseCompositionMonitor); + + if (!mPaused) { + mPaused = true; + + TimeStamp now = TimeStamp::Now(); + if (mCompositor) { + mCompositor->Pause(); + DidComposite(VsyncId(), now, now); + } else if (mWrBridge) { + mWrBridge->Pause(); + NotifyPipelineRendered(mWrBridge->PipelineId(), + mWrBridge->GetCurrentEpoch(), VsyncId(), now, now, + now); + } + } + + // if anyone's waiting to make sure that composition really got paused, tell + // them + lock.NotifyAll(); +} + +void CompositorBridgeParent::ResumeComposition() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "ResumeComposition() can only be called on the compositor thread"); + + MonitorAutoLock lock(mResumeCompositionMonitor); + + bool resumed = + mOptions.UseWebRender() ? mWrBridge->Resume() : mCompositor->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 + lock.NotifyAll(); + return; + } + + mPaused = false; + + Invalidate(); + mCompositorScheduler->ForceComposeToTarget(nullptr, nullptr); + + // if anyone's waiting to make sure that composition really got resumed, tell + // them + lock.NotifyAll(); +} + +void CompositorBridgeParent::ForceComposition() { + // Cancel the orientation changed state to force composition + mForceCompositionTask = nullptr; + ScheduleRenderOnCompositorThread(); +} + +void CompositorBridgeParent::CancelCurrentCompositeTask() { + mCompositorScheduler->CancelCurrentCompositeTask(); +} + +void CompositorBridgeParent::SetEGLSurfaceRect(int x, int y, int width, + int height) { + NS_ASSERTION(mUseExternalSurfaceSize, + "Compositor created without UseExternalSurfaceSize provided"); + mEGLSurfaceSize.SizeTo(width, height); + if (mCompositor) { + mCompositor->SetDestinationSurfaceSize( + gfx::IntSize(mEGLSurfaceSize.width, mEGLSurfaceSize.height)); + if (mCompositor->AsCompositorOGL()) { + mCompositor->AsCompositorOGL()->SetSurfaceOrigin(ScreenIntPoint(x, y)); + } + } +} + +void CompositorBridgeParent::ResumeCompositionAndResize(int x, int y, int width, + int height) { + SetEGLSurfaceRect(x, y, width, height); + ResumeComposition(); +} + +void CompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree, + const TimeDuration& aPaintTime) { + // We get a lot of paint timings for things with empty transactions. + if (!mLayerManager || aPaintTime.ToMilliseconds() < 1.0) { + return; + } + + mLayerManager->SetPaintTime(aPaintTime); +} + +void CompositorBridgeParent::RegisterPayloads( + LayerTransactionParent* aLayerTree, + const nsTArray<CompositionPayload>& aPayload) { + // We get a lot of paint timings for things with empty transactions. + if (!mLayerManager) { + return; + } + + mLayerManager->RegisterPayloads(aPayload); +} + +void CompositorBridgeParent::NotifyShadowTreeTransaction( + LayersId aId, bool aIsFirstPaint, const FocusTarget& aFocusTarget, + bool aScheduleComposite, uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, bool aHitTestUpdate) { + if (!aIsRepeatTransaction && mLayerManager && mLayerManager->GetRoot()) { + // Process plugin data here to give time for them to update before the next + // composition. + bool pluginsUpdatedFlag = true; + AutoResolveRefLayers resolve(mCompositionManager, this, nullptr, + &pluginsUpdatedFlag); + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + // If plugins haven't been updated, stop waiting. + if (!pluginsUpdatedFlag) { + mWaitForPluginsUntil = TimeStamp(); + mHaveBlockedForPlugins = false; + } +#endif + + if (mApzUpdater) { + mApzUpdater->UpdateFocusState(mRootLayerTreeID, aId, aFocusTarget); + if (aHitTestUpdate) { + mApzUpdater->UpdateHitTestingTree( + mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber); + } + } + + mLayerManager->NotifyShadowTreeTransaction(); + } + if (aScheduleComposite) { + ScheduleComposition(); + } +} + +void CompositorBridgeParent::ScheduleComposition() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mPaused) { + return; + } + + if (mWrBridge) { + mWrBridge->ScheduleGenerateFrame(); + } else { + mCompositorScheduler->ScheduleComposition(); + } +} + +// Go down the composite layer tree, setting properties to match their +// content-side counterparts. +/* static */ +void CompositorBridgeParent::SetShadowProperties(Layer* aLayer) { + ForEachNode<ForwardIterator>(aLayer, [](Layer* layer) { + if (Layer* maskLayer = layer->GetMaskLayer()) { + SetShadowProperties(maskLayer); + } + for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) { + SetShadowProperties(layer->GetAncestorMaskLayerAt(i)); + } + + // FIXME: Bug 717688 -- Do these updates in + // LayerTransactionParent::RecvUpdate. + HostLayer* layerCompositor = layer->AsHostLayer(); + // Set the layerComposite's base transform to the layer's base transform. + const auto& animations = layer->GetPropertyAnimationGroups(); + // If there is any animation, the animation value will override + // non-animated value later, so we don't need to set the non-animated + // value here. + if (animations.IsEmpty()) { + layerCompositor->SetShadowBaseTransform(layer->GetBaseTransform()); + layerCompositor->SetShadowTransformSetByAnimation(false); + layerCompositor->SetShadowOpacity(layer->GetOpacity()); + layerCompositor->SetShadowOpacitySetByAnimation(false); + } + layerCompositor->SetShadowVisibleRegion(layer->GetVisibleRegion()); + layerCompositor->SetShadowClipRect(layer->GetClipRect()); + }); +} + +void CompositorBridgeParent::CompositeToTarget(VsyncId aId, DrawTarget* aTarget, + const gfx::IntRect* aRect) { + AUTO_PROFILER_TRACING_MARKER("Paint", "Composite", GRAPHICS); + AUTO_PROFILER_LABEL("CompositorBridgeParent::CompositeToTarget", GRAPHICS); + PerfStats::AutoMetricRecording<PerfStats::Metric::Compositing> autoRecording; + + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "Composite can only be called on the compositor thread"); + TimeStamp start = TimeStamp::Now(); + + if (!CanComposite()) { + TimeStamp end = TimeStamp::Now(); + DidComposite(aId, start, end); + return; + } + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + if (!mWaitForPluginsUntil.IsNull() && mWaitForPluginsUntil > start) { + mHaveBlockedForPlugins = true; + ScheduleComposition(); + return; + } +#endif + + /* + * AutoResolveRefLayers handles two tasks related to Windows and Linux + * plugin window management: + * 1) calculating if we have remote content in the view. If we do not have + * remote content, all plugin windows for this CompositorBridgeParent (window) + * can be hidden since we do not support plugins in chrome when running + * under e10s. + * 2) Updating plugin position, size, and clip. We do this here while the + * remote layer tree is hooked up to to chrome layer tree. This is needed + * since plugin clipping can depend on chrome (for example, due to tab modal + * prompts). Updates in step 2 are applied via an async ipc message sent + * to the main thread. + */ + bool hasRemoteContent = false; + bool updatePluginsFlag = true; + AutoResolveRefLayers resolve(mCompositionManager, this, &hasRemoteContent, + &updatePluginsFlag); + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + // We do not support plugins in local content. When switching tabs + // to local pages, hide every plugin associated with the window. + if (!hasRemoteContent && gfxVars::BrowserTabsRemoteAutostart() && + mCachedPluginData.Length()) { + Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey()); + mCachedPluginData.Clear(); + } +#endif + + nsCString none; + if (aTarget) { + mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect); + } else { + mLayerManager->BeginTransaction(none); + } + + SetShadowProperties(mLayerManager->GetRoot()); + + if (mForceCompositionTask && !mOverrideComposeReadiness) { + if (mCompositionManager->ReadyForCompose()) { + mForceCompositionTask->Cancel(); + mForceCompositionTask = nullptr; + } else { + return; + } + } + + mCompositionManager->ComputeRotation(); + + SampleTime time = mTestTime ? SampleTime::FromTest(*mTestTime) + : mCompositorScheduler->GetLastComposeTime(); + bool requestNextFrame = + mCompositionManager->TransformShadowTree(time, mVsyncRate); + + if (requestNextFrame) { + ScheduleComposition(); +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + // If we have visible windowed plugins then we need to wait for content (and + // then the plugins) to have been updated by the active animation. + if (!mPluginWindowsHidden && mCachedPluginData.Length()) { + mWaitForPluginsUntil = + mCompositorScheduler->GetLastComposeTime().Time() + (mVsyncRate * 2); + } +#endif + } + + RenderTraceLayers(mLayerManager->GetRoot(), "0000"); + + if (StaticPrefs::layers_dump_host_layers() || StaticPrefs::layers_dump()) { + printf_stderr("Painting --- compositing layer tree:\n"); + mLayerManager->Dump(/* aSorted = */ true); + } + mLayerManager->SetDebugOverlayWantsNextFrame(false); + mLayerManager->EndTransaction(time.Time()); + + if (!aTarget) { + TimeStamp end = TimeStamp::Now(); + DidComposite(aId, start, end); + } + + // We're not really taking advantage of the stored composite-again-time here. + // We might be able to skip the next few composites altogether. However, + // that's a bit complex to implement and we'll get most of the advantage + // by skipping compositing when we detect there's nothing invalid. This is why + // we do "composite until" rather than "composite again at". + // + // TODO(bug 1328602) Figure out what we should do here with the render thread. + if (!mLayerManager->GetCompositeUntilTime().IsNull() || + mLayerManager->DebugOverlayWantsNextFrame()) { + ScheduleComposition(); + } + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + TimeDuration executionTime = + TimeStamp::Now() - mCompositorScheduler->GetLastComposeTime().Time(); + TimeDuration frameBudget = TimeDuration::FromMilliseconds(15); + int32_t frameRate = CalculateCompositionFrameRate(); + if (frameRate > 0) { + frameBudget = TimeDuration::FromSeconds(1.0 / frameRate); + } + if (executionTime > frameBudget) { + printf_stderr("Compositor: Composite execution took %4.1f ms\n", + executionTime.ToMilliseconds()); + } +#endif + + // 0 -> Full-tilt composite + if (StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 || + mLayerManager->AlwaysScheduleComposite()) { + // Special full-tilt composite mode for performance testing + ScheduleComposition(); + } + + // TODO(bug 1328602) Need an equivalent that works with the rende thread. + mLayerManager->SetCompositionTime(TimeStamp()); + + mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, + start); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvRemotePluginsReady() { +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + mWaitForPluginsUntil = TimeStamp(); + if (mHaveBlockedForPlugins) { + mHaveBlockedForPlugins = false; + ForceComposeToTarget(nullptr); + } else { + ScheduleComposition(); + } + return IPC_OK(); +#else + MOZ_ASSERT_UNREACHABLE( + "CompositorBridgeParent::RecvRemotePluginsReady calls " + "unexpected on this platform."); + return IPC_FAIL_NO_REASON(this); +#endif +} + +void CompositorBridgeParent::ForceComposeToTarget(DrawTarget* aTarget, + const gfx::IntRect* aRect) { + AUTO_PROFILER_LABEL("CompositorBridgeParent::ForceComposeToTarget", GRAPHICS); + + AutoRestore<bool> override(mOverrideComposeReadiness); + mOverrideComposeReadiness = true; + mCompositorScheduler->ForceComposeToTarget(aTarget, aRect); +} + +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::CanComposite() { + return mLayerManager && mLayerManager->GetRoot() && !mPaused; +} + +void CompositorBridgeParent::ScheduleRotationOnCompositorThread( + const TargetConfig& aTargetConfig, bool aIsFirstPaint) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + if (!aIsFirstPaint && !mCompositionManager->IsFirstPaint() && + mCompositionManager->RequiresReorientation(aTargetConfig.orientation())) { + if (mForceCompositionTask != nullptr) { + mForceCompositionTask->Cancel(); + } + RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod( + "layers::CompositorBridgeParent::ForceComposition", this, + &CompositorBridgeParent::ForceComposition); + mForceCompositionTask = task; + if (StaticPrefs::layers_orientation_sync_timeout() == 0) { + CompositorThread()->Dispatch(task.forget()); + } else { + CompositorThread()->DelayedDispatch( + task.forget(), StaticPrefs::layers_orientation_sync_timeout()); + } + } +} + +void CompositorBridgeParent::ShadowLayersUpdated( + LayerTransactionParent* aLayerTree, const TransactionInfo& aInfo, + bool aHitTestUpdate) { + const TargetConfig& targetConfig = aInfo.targetConfig(); + + ScheduleRotationOnCompositorThread(targetConfig, aInfo.isFirstPaint()); + + // Instruct the LayerManager to update its render bounds now. Since all the + // orientation change, dimension change would be done at the stage, update the + // size here is free of race condition. + mLayerManager->UpdateRenderBounds(targetConfig.naturalBounds()); + mLayerManager->SetRegionToClear(targetConfig.clearRegion()); + if (mLayerManager->GetCompositor()) { + mLayerManager->GetCompositor()->SetScreenRotation(targetConfig.rotation()); + } + + mCompositionManager->Updated(aInfo.isFirstPaint(), targetConfig); + Layer* root = aLayerTree->GetRoot(); + mLayerManager->SetRoot(root); + + if (mApzUpdater && !aInfo.isRepeatTransaction()) { + mApzUpdater->UpdateFocusState(mRootLayerTreeID, mRootLayerTreeID, + aInfo.focusTarget()); + + if (aHitTestUpdate) { + AutoResolveRefLayers resolve(mCompositionManager); + + mApzUpdater->UpdateHitTestingTree(root, aInfo.isFirstPaint(), + mRootLayerTreeID, + aInfo.paintSequenceNumber()); + } + } + + // The transaction ID might get reset to 1 if the page gets reloaded, see + // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41 + // Otherwise, it should be continually increasing. + MOZ_ASSERT(aInfo.id() == TransactionId{1} || + aInfo.id() > mPendingTransaction); + mPendingTransaction = aInfo.id(); + mRefreshStartTime = aInfo.refreshStart(); + mTxnStartTime = aInfo.transactionStart(); + mFwdTime = aInfo.fwdTime(); + RegisterPayloads(aLayerTree, aInfo.payload()); + + if (root) { + SetShadowProperties(root); + } + if (aInfo.scheduleComposite()) { + ScheduleComposition(); + if (mPaused) { + TimeStamp now = TimeStamp::Now(); + DidComposite(VsyncId(), now, now); + } + } + mLayerManager->NotifyShadowTreeTransaction(); +} + +void CompositorBridgeParent::ScheduleComposite( + LayerTransactionParent* aLayerTree) { + ScheduleComposition(); +} + +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(); + return true; + } + + bool testComposite = + mCompositionManager && mCompositorScheduler->NeedsComposite(); + + // Update but only if we were already scheduled to animate + if (testComposite) { + AutoResolveRefLayers resolve(mCompositionManager); + bool requestNextFrame = mCompositionManager->TransformShadowTree( + SampleTime::FromTest(aTime), mVsyncRate); + if (!requestNextFrame) { + CancelCurrentCompositeTask(); + // Pretend we composited in case someone is wating for this event. + TimeStamp now = TimeStamp::Now(); + DidComposite(VsyncId(), now, now); + } + } + + return true; +} + +void CompositorBridgeParent::LeaveTestMode(const LayersId& aId) { + mTestTime = Nothing(); + if (mApzcTreeManager) { + mApzcTreeManager->SetTestSampleTime(mTestTime); + } +} + +void CompositorBridgeParent::ApplyAsyncProperties( + LayerTransactionParent* aLayerTree, TransformsToSkip aSkip) { + // NOTE: This should only be used for testing. For example, when mTestTime is + // non-empty, or when called from test-only methods like + // LayerTransactionParent::RecvGetAnimationTransform. + + // Synchronously update the layer tree + if (aLayerTree->GetRoot()) { + AutoResolveRefLayers resolve(mCompositionManager); + SetShadowProperties(mLayerManager->GetRoot()); + + SampleTime time; + if (mTestTime) { + time = SampleTime::FromTest(*mTestTime); + } else { + time = mCompositorScheduler->GetLastComposeTime(); + } + bool requestNextFrame = + mCompositionManager->TransformShadowTree(time, mVsyncRate, aSkip); + if (!requestNextFrame) { + CancelCurrentCompositeTask(); + // Pretend we composited in case someone is waiting for this event. + TimeStamp now = TimeStamp::Now(); + DidComposite(VsyncId(), now, now); + } + } +} + +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 (mLayerManager) { + 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) { + if (mCompositionManager) { + mCompositionManager->GetFrameUniformity(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->RunOnControllerThread(aLayersId, task.forget()); +} + +void CompositorBridgeParent::SetFixedLayerMargins(ScreenIntCoord aTop, + ScreenIntCoord aBottom) { + if (AsyncCompositionManager* manager = GetCompositionManager(nullptr)) { + manager->SetFixedLayerMargins(aTop, aBottom); + } + + if (mApzcTreeManager) { + mApzcTreeManager->SetFixedLayerMargins(aTop, aBottom); + } + + Invalidate(); + ScheduleComposition(); +} + +void CompositorBridgeParent::InitializeLayerManager( + const nsTArray<LayersBackend>& aBackendHints) { + NS_ASSERTION(!mLayerManager, "Already initialised mLayerManager"); + NS_ASSERTION(!mCompositor, "Already initialised mCompositor"); + + if (!InitializeAdvancedLayers(aBackendHints, nullptr)) { + mCompositor = NewCompositor(aBackendHints); + if (!mCompositor) { + return; + } +#ifdef XP_WIN + if (mCompositor->AsBasicCompositor() && XRE_IsGPUProcess()) { + // BasicCompositor does not use CompositorWindow, + // then if CompositorWindow exists, it needs to be destroyed. + mWidget->AsWindows()->DestroyCompositorWindow(); + } +#endif + mLayerManager = new LayerManagerComposite(mCompositor); + } + mLayerManager->SetCompositorBridgeID(mCompositorBridgeID); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = mLayerManager; +} + +bool CompositorBridgeParent::InitializeAdvancedLayers( + const nsTArray<LayersBackend>& aBackendHints, + TextureFactoryIdentifier* aOutIdentifier) { +#ifdef XP_WIN + if (!mOptions.UseAdvancedLayers()) { + return false; + } + + // Currently LayerManagerMLGPU hardcodes a D3D11 device, so we reject using + // AL if LAYERS_D3D11 isn't in the backend hints. + if (!aBackendHints.Contains(LayersBackend::LAYERS_D3D11)) { + return false; + } + + RefPtr<LayerManagerMLGPU> manager = new LayerManagerMLGPU(mWidget); + if (!manager->Initialize()) { + return false; + } + + if (aOutIdentifier) { + *aOutIdentifier = manager->GetTextureFactoryIdentifier(); + } + mLayerManager = manager; + return true; +#else + return false; +#endif +} + +RefPtr<Compositor> CompositorBridgeParent::NewCompositor( + const nsTArray<LayersBackend>& aBackendHints) { + for (size_t i = 0; i < aBackendHints.Length(); ++i) { + RefPtr<Compositor> compositor; + if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) { + compositor = + new CompositorOGL(this, mWidget, mEGLSurfaceSize.width, + mEGLSurfaceSize.height, mUseExternalSurfaceSize); + } else if (aBackendHints[i] == LayersBackend::LAYERS_BASIC) { +#ifdef MOZ_WIDGET_GTK + if (gfxVars::UseXRender()) { + compositor = new X11BasicCompositor(this, mWidget); + } else +#endif + { + compositor = new BasicCompositor(this, mWidget); + } +#ifdef XP_WIN + } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) { + compositor = new CompositorD3D11(this, mWidget); +#endif + } + nsCString failureReason; + + // Some software GPU emulation implementations will happily try to create + // unreasonably big surfaces and then fail in awful ways. + // Let's at least limit this to the default max texture size we use for + // content, anything larger than that will fail to render on the content + // side anyway. We can revisit this value and make it even tighter if need + // be. + const int max_fb_size = 32767; + const LayoutDeviceIntSize size = mWidget->GetClientSize(); + if (size.width > max_fb_size || size.height > max_fb_size) { + failureReason = "FEATURE_FAILURE_MAX_FRAMEBUFFER_SIZE"; + return nullptr; + } + + MOZ_ASSERT(!gfxVars::UseWebRender() || + aBackendHints[i] == LayersBackend::LAYERS_BASIC); + if (compositor && compositor->Initialize(&failureReason)) { + if (failureReason.IsEmpty()) { + failureReason = "SUCCESS"; + } + + // should only report success here + if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) { + Telemetry::Accumulate(Telemetry::OPENGL_COMPOSITING_FAILURE_ID, + failureReason); + } +#ifdef XP_WIN + else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) { + Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID, + failureReason); + } +#endif + + return compositor; + } + + // report any failure reasons here + if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) { + gfxCriticalNote << "[OPENGL] Failed to init compositor with reason: " + << failureReason.get(); + Telemetry::Accumulate(Telemetry::OPENGL_COMPOSITING_FAILURE_ID, + failureReason); + } +#ifdef XP_WIN + else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) { + gfxCriticalNote << "[D3D11] Failed to init compositor with reason: " + << failureReason.get(); + Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID, + failureReason); + } +#endif + } + + return nullptr; +} + +PLayerTransactionParent* CompositorBridgeParent::AllocPLayerTransactionParent( + const nsTArray<LayersBackend>& aBackendHints, const LayersId& aId) { + MOZ_ASSERT(!aId.IsValid()); + +#ifdef XP_WIN + // This is needed to avoid freezing the window on a device crash on double + // buffering, see bug 1549674. + if (gfxVars::UseDoubleBufferingWithCompositor() && XRE_IsGPUProcess() && + aBackendHints.Contains(LayersBackend::LAYERS_D3D11)) { + mWidget->AsWindows()->EnsureCompositorWindow(); + } +#endif + + InitializeLayerManager(aBackendHints); + + if (!mLayerManager) { + NS_WARNING("Failed to initialise Compositor"); + LayerTransactionParent* p = new LayerTransactionParent( + /* aManager */ nullptr, this, /* aAnimStorage */ nullptr, + mRootLayerTreeID, mVsyncRate); + p->AddIPDLReference(); + return p; + } + + mCompositionManager = new AsyncCompositionManager(this, mLayerManager); + + LayerTransactionParent* p = new LayerTransactionParent( + mLayerManager, this, GetAnimationStorage(), mRootLayerTreeID, mVsyncRate); + p->AddIPDLReference(); + return p; +} + +bool CompositorBridgeParent::DeallocPLayerTransactionParent( + PLayerTransactionParent* actor) { + static_cast<LayerTransactionParent*>(actor)->ReleaseIPDLReference(); + return true; +} + +CompositorBridgeParent* CompositorBridgeParent::GetCompositorBridgeParent( + uint64_t id) { + AssertIsInCompositorThread(); + CompositorMap::iterator it = sCompositorMap->find(id); + return it != sCompositorMap->end() ? it->second : nullptr; +} + +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) { + 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(); + } else if (cbp->CanComposite()) { + cbp->mCompositorScheduler->ScheduleComposition(); + } +} + +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; + sIndirectLayerTrees[aChild].mLayerManager = mLayerManager; +} + +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.UseAdvancedLayers() == aNew.UseAdvancedLayers() && + aOld.UseWebRender() == aNew.UseWebRender() && + aOld.InitiallyPaused() == aNew.InitiallyPaused()) { + // Only APZ enablement changed. + return CompositorOptionsChangeKind::eBestEffort; + } + return CompositorOptionsChangeKind::eUnsupported; +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvAdoptChild( + const LayersId& child) { + RefPtr<APZUpdater> oldApzUpdater; + APZCTreeManagerParent* parent; + bool scheduleComposition = false; + bool apzEnablementChanged = false; + RefPtr<ContentCompositorBridgeParent> cpcp; + 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; + } + NotifyChildCreated(child); + if (sIndirectLayerTrees[child].mLayerTree) { + sIndirectLayerTrees[child].mLayerTree->SetLayerManager( + mLayerManager, GetAnimationStorage()); + // Trigger composition to handle a case that mLayerTree was not composited + // yet by previous CompositorBridgeParent, since nsRefreshDriver might + // wait composition complete. + scheduleComposition = true; + } + if (mWrBridge) { + childWrBridge = sIndirectLayerTrees[child].mWrBridge; + cpcp = sIndirectLayerTrees[child].mContentCompositorBridgeParent; + } + parent = sIndirectLayerTrees[child].mApzcTreeManagerParent; + } + + if (scheduleComposition) { + ScheduleComposition(); + } + + 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); + } + + 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(!mCompositor); + MOZ_ASSERT(!mCompositorScheduler); + MOZ_ASSERT(mWidget); + +#ifdef XP_WIN + if (mWidget && (DeviceManagerDx::Get()->CanUseDComp() || + gfxVars::UseWebRenderFlipSequentialWin())) { + mWidget->AsWindows()->EnsureCompositorWindow(); + } +#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; + } + + wr::TransactionBuilder txn; + 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; +} + +webgpu::PWebGPUParent* CompositorBridgeParent::AllocPWebGPUParent() { + MOZ_ASSERT(!mWebGPUBridge); + mWebGPUBridge = new webgpu::WebGPUParent(); + mWebGPUBridge.get()->AddRef(); // IPDL reference + return mWebGPUBridge; +} + +bool CompositorBridgeParent::DeallocPWebGPUParent( + webgpu::PWebGPUParent* aActor) { + webgpu::WebGPUParent* parent = static_cast<webgpu::WebGPUParent*>(aActor); + MOZ_ASSERT(mWebGPUBridge == parent); + parent->Release(); // IPDL reference + mWebGPUBridge = nullptr; + 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::SetUseWebRenderMultithreadingListener( + &UpdateWebRenderMultithreading); + gfxVars::SetWebRenderBatchingLookbackListener( + &UpdateWebRenderBatchingParameters); + 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 { + 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 { + wrBridge->UpdateDebugFlags(); + }); +} + +/*static*/ +void CompositorBridgeParent::UpdateWebRenderMultithreading() { + if (!CompositorThreadHolder::IsInCompositorThread()) { + if (CompositorThread()) { + CompositorThread()->Dispatch(NewRunnableFunction( + "CompositorBridgeParent::UpdateWebRenderMultithreading", + &CompositorBridgeParent::UpdateWebRenderMultithreading)); + } + + return; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void { + wrBridge->UpdateMultithreading(); + }); +} + +/*static*/ +void CompositorBridgeParent::UpdateWebRenderBatchingParameters() { + if (!CompositorThreadHolder::IsInCompositorThread()) { + if (CompositorThread()) { + CompositorThread()->Dispatch(NewRunnableFunction( + "CompositorBridgeParent::UpdateWebRenderBatchingParameters", + &CompositorBridgeParent::UpdateWebRenderBatchingParameters)); + } + + return; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void { + wrBridge->UpdateBatchingParameters(); + }); +} + +/*static*/ +void CompositorBridgeParent::UpdateWebRenderProfilerUI() { + if (!sIndirectLayerTreesLock) { + return; + } + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void { + wrBridge->UpdateProfilerUI(); + }); +} + +RefPtr<WebRenderBridgeParent> CompositorBridgeParent::GetWebRenderBridgeParent() + const { + return mWrBridge; +} + +Maybe<TimeStamp> CompositorBridgeParent::GetTestingTimeStamp() const { + return mTestTime; +} + +void EraseLayerState(LayersId aId) { + RefPtr<APZUpdater> apz; + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + auto iter = sIndirectLayerTrees.find(aId); + if (iter != sIndirectLayerTrees.end()) { + CompositorBridgeParent* parent = iter->second.mParent; + if (parent) { + apz = parent->GetAPZUpdater(); + } + sIndirectLayerTrees.erase(iter); + } + } + + if (apz) { + apz->NotifyLayerTreeRemoved(aId); + } +} + +/*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( + APZCTreeManager* aApzctm, LayersId aLayersId, Layer* aRoot, + GeckoContentController* aController) + : mLayersId(aLayersId) { + EnsureLayerTreeMapReady(); + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[aLayersId].mRoot = aRoot; + 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(); +} + +#if defined(MOZ_GECKO_PROFILER) +static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (profiler_thread_is_being_profiled()) { + // 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{}); + } +} +#endif + +/*static */ +void CompositorBridgeParent::PostInsertVsyncProfilerMarker( + TimeStamp aVsyncTimestamp) { +#if defined(MOZ_GECKO_PROFILER) + // Called in the vsync thread + if (profiler_is_active() && CompositorThreadHolder::IsActive()) { + CompositorThread()->Dispatch( + NewRunnableFunction("InsertVsyncProfilerMarkerRunnable", + InsertVsyncProfilerMarker, aVsyncTimestamp)); + } +#endif +} + +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 +} + +bool CompositorBridgeParent::IsPendingComposite() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (!mCompositor) { + return false; + } + return mCompositor->IsPendingComposite(); +} + +void CompositorBridgeParent::FinishPendingComposite() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (!mCompositor) { + return; + } + return mCompositor->FinishPendingComposite(); +} + +CompositorController* +CompositorBridgeParent::LayerTreeState::GetCompositorController() const { + return mParent; +} + +MetricsSharingController* +CompositorBridgeParent::LayerTreeState::CrossProcessSharingController() const { + return mContentCompositorBridgeParent; +} + +MetricsSharingController* +CompositorBridgeParent::LayerTreeState::InProcessSharingController() const { + return mParent; +} + +void CompositorBridgeParent::DidComposite(const VsyncId& aId, + TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd) { + if (mWrBridge) { + MOZ_ASSERT(false); // This should never get called for a WR compositor + } else { + NotifyDidComposite(mPendingTransaction, aId, aCompositeStart, + aCompositeEnd); +#if defined(ENABLE_FRAME_LATENCY_LOG) + if (mPendingTransaction.IsValid()) { + if (mRefreshStartTime) { + int32_t latencyMs = + lround((aCompositeEnd - mRefreshStartTime).ToMilliseconds()); + printf_stderr( + "From transaction start to end of generate frame latencyMs %d this " + "%p\n", + latencyMs, this); + } + if (mFwdTime) { + int32_t latencyMs = lround((aCompositeEnd - mFwdTime).ToMilliseconds()); + printf_stderr( + "From forwarding transaction to end of generate frame latencyMs %d " + "this %p\n", + latencyMs, this); + } + } + mRefreshStartTime = TimeStamp(); + mTxnStartTime = TimeStamp(); + mFwdTime = TimeStamp(); +#endif + mPendingTransaction = TransactionId{0}; + } +} + +void CompositorBridgeParent::NotifyDidSceneBuild( + RefPtr<const wr::WebRenderPipelineInfo> aInfo) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mPaused) { + return; + } + + if (mWrBridge) { + mWrBridge->NotifyDidSceneBuild(aInfo); + } else { + mCompositorScheduler->ScheduleComposition(); + } +} + +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); + } +} + +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; + + RefPtr<UiCompositorControllerParent> uiController = + UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeID); + + TransactionId transactionId = wrBridge->FlushTransactionIdsForEpoch( + aEpoch, aCompositeStartId, aCompositeStart, aRenderStart, aCompositeEnd, + uiController, aStats, &stats); + + LayersId layersId = isRoot ? LayersId{0} : wrBridge->GetLayersId(); + Unused << compBridge->SendDidComposite(layersId, transactionId, + aCompositeStart, aCompositeEnd); + + if (!stats.IsEmpty()) { + Unused << SendNotifyFrameStats(stats); + } +} + +RefPtr<AsyncImagePipelineManager> +CompositorBridgeParent::GetAsyncImagePipelineManager() const { + return mAsyncImageManager; +} + +void CompositorBridgeParent::NotifyDidComposite(TransactionId aTransactionId, + VsyncId aId, + TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd) { + MOZ_ASSERT(!mWrBridge, + "We should be going through NotifyDidRender and " + "NotifyPipelineRendered instead"); + + Unused << SendDidComposite(LayersId{0}, aTransactionId, aCompositeStart, + aCompositeEnd); + + if (mLayerManager) { + nsTArray<ImageCompositeNotificationInfo> notifications; + mLayerManager->ExtractImageCompositeNotifications(¬ifications); + if (!notifications.IsEmpty()) { + Unused << ImageBridgeParent::NotifyImageComposites(notifications); + } + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([&](LayerTreeState* lts, + const LayersId& aLayersId) -> void { + if (lts->mContentCompositorBridgeParent && lts->mParent == this) { + ContentCompositorBridgeParent* cpcp = lts->mContentCompositorBridgeParent; + cpcp->DidCompositeLocked(aLayersId, aId, aCompositeStart, aCompositeEnd); + } + }); +} + +void CompositorBridgeParent::InvalidateRemoteLayers() { + MOZ_ASSERT(CompositorThread()->IsOnCurrentThread()); + + Unused << PCompositorBridgeParent::SendInvalidateLayers(LayersId{0}); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([](LayerTreeState* lts, + const LayersId& aLayersId) -> void { + if (lts->mContentCompositorBridgeParent) { + ContentCompositorBridgeParent* cpcp = lts->mContentCompositorBridgeParent; + Unused << cpcp->SendInvalidateLayers(aLayersId); + } + }); +} + +void UpdateIndirectTree(LayersId aId, Layer* aRoot, + const TargetConfig& aTargetConfig) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[aId].mRoot = aRoot; + sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig; +} + +/* 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* state = nullptr; + LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aContentLayersId); + if (sIndirectLayerTrees.end() != itr) { + state = &itr->second; + } + + // |state| 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 (state && state->mParent) { + LayersId rootLayersId = state->mParent->RootLayerTreeId(); + itr = sIndirectLayerTrees.find(rootLayersId); + state = (sIndirectLayerTrees.end() != itr) ? &itr->second : nullptr; + } + + return state; +} + +/* 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, const ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const LayersId& aId, const uint64_t& aSerial, + const wr::MaybeExternalImageId& aExternalImageId) { + return TextureHost::CreateIPDLActor(this, aSharedData, aReadLock, + aLayersBackend, aFlags, 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(); + } +} + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +//#define PLUGINS_LOG(...) printf_stderr("CP [%s]: ", __FUNCTION__); +// printf_stderr(__VA_ARGS__); +// printf_stderr("\n"); +# define PLUGINS_LOG(...) + +bool CompositorBridgeParent::UpdatePluginWindowState(LayersId aId) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& lts = sIndirectLayerTrees[aId]; + if (!lts.mParent) { + PLUGINS_LOG("[%" PRIu64 "] layer tree compositor parent pointer is null", + aId); + return false; + } + + // Check if this layer tree has received any shadow layer updates + if (!lts.mUpdatedPluginDataAvailable) { + PLUGINS_LOG("[%" PRIu64 "] no plugin data", aId); + return false; + } + + // pluginMetricsChanged tracks whether we need to send plugin update + // data to the main thread. If we do we'll have to block composition, + // which we want to avoid if at all possible. + bool pluginMetricsChanged = false; + + // Same layer tree checks + if (mLastPluginUpdateLayerTreeId == aId) { + // no plugin data and nothing has changed, bail. + if (!mCachedPluginData.Length() && !lts.mPluginData.Length()) { + PLUGINS_LOG("[%" PRIu64 "] no data, no changes", aId); + return false; + } + + if (mCachedPluginData.Length() == lts.mPluginData.Length()) { + // check for plugin data changes + for (uint32_t idx = 0; idx < lts.mPluginData.Length(); idx++) { + if (!(mCachedPluginData[idx] == lts.mPluginData[idx])) { + pluginMetricsChanged = true; + break; + } + } + } else { + // array lengths don't match, need to update + pluginMetricsChanged = true; + } + } else { + // exchanging layer trees, we need to update + pluginMetricsChanged = true; + } + + // Check if plugin windows are currently hidden due to scrolling + if (mDeferPluginWindows) { + PLUGINS_LOG("[%" PRIu64 "] suppressing", aId); + return false; + } + + // If the plugin windows were hidden but now are not, we need to force + // update the metrics to make sure they are visible again. + if (mPluginWindowsHidden) { + PLUGINS_LOG("[%" PRIu64 "] re-showing", aId); + mPluginWindowsHidden = false; + pluginMetricsChanged = true; + } + + if (!lts.mPluginData.Length()) { + // Don't hide plugins if the previous remote layer tree didn't contain any. + if (!mCachedPluginData.Length()) { + PLUGINS_LOG("[%" PRIu64 "] nothing to hide", aId); + return false; + } + + uintptr_t parentWidget = GetWidget()->GetWidgetKey(); + + // We will pass through here in cases where the previous shadow layer + // tree contained visible plugins and the new tree does not. All we need + // to do here is hide the plugins for the old tree, so don't waste time + // calculating clipping. + mPluginsLayerOffset = nsIntPoint(0, 0); + mPluginsLayerVisibleRegion.SetEmpty(); + Unused << lts.mParent->SendHideAllPlugins(parentWidget); + lts.mUpdatedPluginDataAvailable = false; + PLUGINS_LOG("[%" PRIu64 "] hide all", aId); + } else { + // Retrieve the offset and visible region of the layer that hosts + // the plugins, CompositorBridgeChild needs these in calculating proper + // plugin clipping. + LayerTransactionParent* layerTree = lts.mLayerTree; + Layer* contentRoot = layerTree->GetRoot(); + if (contentRoot) { + nsIntPoint offset; + nsIntRegion visibleRegion; + if (contentRoot->GetVisibleRegionRelativeToRootLayer(visibleRegion, + &offset)) { + // Check to see if these values have changed, if so we need to + // update plugin window position within the window. + if (!pluginMetricsChanged && + mPluginsLayerVisibleRegion == visibleRegion && + mPluginsLayerOffset == offset) { + PLUGINS_LOG("[%" PRIu64 "] no change", aId); + return false; + } + mPluginsLayerOffset = offset; + mPluginsLayerVisibleRegion = visibleRegion; + Unused << lts.mParent->SendUpdatePluginConfigurations( + LayoutDeviceIntPoint::FromUnknownPoint(offset), + LayoutDeviceIntRegion::FromUnknownRegion(visibleRegion), + lts.mPluginData); + lts.mUpdatedPluginDataAvailable = false; + PLUGINS_LOG("[%" PRIu64 "] updated", aId); + } else { + PLUGINS_LOG("[%" PRIu64 "] no visibility data", aId); + return false; + } + } else { + PLUGINS_LOG("[%" PRIu64 "] no content root", aId); + return false; + } + } + + mLastPluginUpdateLayerTreeId = aId; + mCachedPluginData = lts.mPluginData.Clone(); + return true; +} + +void CompositorBridgeParent::ScheduleShowAllPluginWindows() { + MOZ_ASSERT(CompositorThread()); + CompositorThread()->Dispatch( + NewRunnableMethod("layers::CompositorBridgeParent::ShowAllPluginWindows", + this, &CompositorBridgeParent::ShowAllPluginWindows)); +} + +void CompositorBridgeParent::ShowAllPluginWindows() { + MOZ_ASSERT(!NS_IsMainThread()); + mDeferPluginWindows = false; + ScheduleComposition(); +} + +void CompositorBridgeParent::ScheduleHideAllPluginWindows() { + MOZ_ASSERT(CompositorThread()); + CompositorThread()->Dispatch( + NewRunnableMethod("layers::CompositorBridgeParent::HideAllPluginWindows", + this, &CompositorBridgeParent::HideAllPluginWindows)); +} + +void CompositorBridgeParent::HideAllPluginWindows() { + MOZ_ASSERT(!NS_IsMainThread()); + // No plugins in the cache implies no plugins to manage + // in this content. + if (!mCachedPluginData.Length() || mDeferPluginWindows) { + return; + } + + uintptr_t parentWidget = GetWidget()->GetWidgetKey(); + + mDeferPluginWindows = true; + mPluginWindowsHidden = true; + +# if defined(XP_WIN) + // We will get an async reply that this has happened and then send hide. + mWaitForPluginsUntil = TimeStamp::Now() + mVsyncRate; + Unused << SendCaptureAllPlugins(parentWidget); +# else + Unused << SendHideAllPlugins(parentWidget); + ScheduleComposition(); +# endif +} +#endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvAllPluginsCaptured() { +#if defined(XP_WIN) + mWaitForPluginsUntil = TimeStamp(); + mHaveBlockedForPlugins = false; + ForceComposeToTarget(nullptr); + Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey()); + return IPC_OK(); +#else + MOZ_ASSERT_UNREACHABLE( + "CompositorBridgeParent::RecvAllPluginsCaptured calls unexpected."); + return IPC_FAIL_NO_REASON(this); +#endif +} + +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); + +#ifdef MOZ_GECKO_PROFILER + if (profiler_can_accept_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{}); + } +#endif + + 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 (mLayerManager) { + mLayerManager->SetCompositionRecorder( + MakeUnique<CompositionRecorder>(aRecordingStart)); + } else if (mWrBridge) { + mWrBridge->BeginRecording(aRecordingStart); + } + + mHaveCompositionRecorder = true; + aResolve(true); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvEndRecordingToDisk( + EndRecordingToDiskResolver&& aResolve) { + if (!mHaveCompositionRecorder) { + aResolve(false); + return IPC_OK(); + } + + if (mLayerManager) { + mLayerManager->WriteCollectedFrames(); + aResolve(true); + } else if (mWrBridge) { + mWrBridge->WriteCollectedFrames()->Then( + NS_GetCurrentThread(), __func__, + [resolve{aResolve}](const bool success) { resolve(success); }, + [resolve{aResolve}]() { resolve(false); }); + } else { + aResolve(false); + } + + mHaveCompositionRecorder = false; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult CompositorBridgeParent::RecvEndRecordingToMemory( + EndRecordingToMemoryResolver&& aResolve) { + if (!mHaveCompositionRecorder) { + aResolve(Nothing()); + return IPC_OK(); + } + + if (mLayerManager) { + Maybe<CollectedFrames> frames = mLayerManager->GetCollectedFrames(); + if (frames) { + aResolve(WrapCollectedFrames(std::move(*frames))); + } else { + aResolve(Nothing()); + } + } else if (mWrBridge) { + RefPtr<CompositorBridgeParent> self = this; + mWrBridge->GetCollectedFrames()->Then( + NS_GetCurrentThread(), __func__, + [self, resolve{aResolve}](CollectedFrames&& frames) { + resolve(self->WrapCollectedFrames(std::move(frames))); + }, + [resolve{aResolve}]() { resolve(Nothing()); }); + } + + mHaveCompositionRecorder = false; + + return IPC_OK(); +} + +Maybe<CollectedFramesParams> CompositorBridgeParent::WrapCollectedFrames( + CollectedFrames&& aFrames) { + CollectedFramesParams ipcFrames; + ipcFrames.recordingStart() = aFrames.mRecordingStart; + + size_t totalLength = 0; + for (const CollectedFrame& frame : aFrames.mFrames) { + totalLength += frame.mDataUri.Length(); + } + + Shmem shmem; + if (!AllocShmem(totalLength, SharedMemory::TYPE_BASIC, &shmem)) { + return Nothing(); + } + + { + char* raw = shmem.get<char>(); + for (CollectedFrame& frame : aFrames.mFrames) { + size_t length = frame.mDataUri.Length(); + + PodCopy(raw, frame.mDataUri.get(), length); + raw += length; + + ipcFrames.frames().EmplaceBack(frame.mTimeOffset, length); + } + } + ipcFrames.buffer() = std::move(shmem); + + return Some(std::move(ipcFrames)); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h new file mode 100644 index 0000000000..9d518749a2 --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeParent.h @@ -0,0 +1,904 @@ +/* -*- 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 + +// Enable this pref to turn on compositor performance warning. +// This will print warnings if the compositor isn't meeting +// its responsiveness objectives: +// 1) Compose a frame within 15ms of receiving a ScheduleCompositeCall +// 2) Unless a frame was composited within the throttle threshold in +// which the deadline will be 15ms + throttle threshold +//#define COMPOSITOR_PERFORMANCE_WARNING + +#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/ISurfaceAllocator.h" // for IShmemAllocator +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/MetricsSharingController.h" +#include "mozilla/layers/PCompositorBridgeParent.h" +#include "mozilla/webrender/WebRenderTypes.h" + +struct DxgiAdapterDesc; + +namespace mozilla { + +class CancelableRunnable; + +namespace dom { +class WebGLParent; +} // namespace dom + +namespace gfx { +class DrawTarget; +class GPUProcessManager; +class GPUParent; +} // namespace gfx + +namespace ipc { +class Shmem; +#ifdef FUZZING +class ProtocolFuzzerHelper; +#endif +} // namespace ipc + +namespace webgpu { +class PWebGPUParent; +class WebGPUParent; +} // namespace webgpu + +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 AsyncCompositionManager; +class AsyncImagePipelineManager; +class Compositor; +class CompositorAnimationStorage; +class CompositorBridgeParent; +class CompositorManagerParent; +class CompositorVsyncScheduler; +class FrameUniformityData; +class GeckoContentController; +class HostLayerManager; +class IAPZCTreeManager; +class Layer; +class LayerTransactionParent; +class OMTASampler; +class ContentCompositorBridgeParent; +class CompositorThreadHolder; +class InProcessCompositorSession; +class UiCompositorControllerParent; +class WebRenderBridgeParent; +struct CollectedFrames; + +struct ScopedLayerTreeRegistration { + ScopedLayerTreeRegistration(APZCTreeManager* aApzctm, LayersId aLayersId, + Layer* aRoot, + GeckoContentController* aController); + ~ScopedLayerTreeRegistration(); + + private: + LayersId mLayersId; +}; + +class CompositorBridgeParentBase : public PCompositorBridgeParent, + public HostIPCAllocator, + public mozilla::ipc::IShmemAllocator, + public MetricsSharingController { + friend class PCompositorBridgeParent; + + public: + explicit CompositorBridgeParentBase(CompositorManagerParent* aManager); + + virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const TransactionInfo& aInfo, + bool aHitTestUpdate) = 0; + + virtual AsyncCompositionManager* GetCompositionManager( + LayerTransactionParent* aLayerTree) { + return nullptr; + } + + virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) {} + + virtual void ScheduleComposite(LayerTransactionParent* aLayerTree) {} + 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 ApplyAsyncProperties(LayerTransactionParent* aLayerTree, + TransformsToSkip aSkip) = 0; + 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; + virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, + const TimeDuration& aPaintTime) {} + virtual void RegisterPayloads(LayerTransactionParent* aLayerTree, + const nsTArray<CompositionPayload>& aPayload) {} + + 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; + void NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) override; + void SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) override; + + // IShmemAllocator + bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) override; + bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) override; + bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + + // MetricsSharingController + NS_IMETHOD_(MozExternalRefCountType) AddRef() override { + return HostIPCAllocator::AddRef(); + } + NS_IMETHOD_(MozExternalRefCountType) Release() override { + return HostIPCAllocator::Release(); + } + base::ProcessId RemotePid() override; + bool StartSharingMetrics(mozilla::ipc::SharedMemoryBasic::Handle aHandle, + CrossProcessMutexHandle aMutexHandle, + LayersId aLayersId, uint32_t aApzcId) override; + bool StopSharingMetrics(ScrollableLayerGuid::ViewID aScrollId, + uint32_t aApzcId) override; + + virtual bool IsRemote() const { return false; } + + virtual UniquePtr<SurfaceDescriptor> LookupSurfaceDescriptorForClientTexture( + const int64_t aTextureId) { + MOZ_CRASH("Should only be called on ContentCompositorBridgeParent."); + } + + virtual void ForceComposeToTarget(gfx::DrawTarget* aTarget, + const gfx::IntRect* aRect = nullptr) { + MOZ_CRASH(); + } + + 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 PLayerTransactionParent* AllocPLayerTransactionParent( + const nsTArray<LayersBackend>& layersBackendHints, + const LayersId& id) = 0; + virtual bool DeallocPLayerTransactionParent( + PLayerTransactionParent* aActor) = 0; + + virtual PTextureParent* AllocPTextureParent( + const SurfaceDescriptor& aSharedData, const 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 webgpu::PWebGPUParent* AllocPWebGPUParent() = 0; + virtual bool DeallocPWebGPUParent(webgpu::PWebGPUParent* aActor) = 0; + + virtual PCompositorWidgetParent* AllocPCompositorWidgetParent( + const CompositorWidgetInitData& aInitData) = 0; + virtual bool DeallocPCompositorWidgetParent( + PCompositorWidgetParent* aActor) = 0; + + virtual mozilla::ipc::IPCResult RecvRemotePluginsReady() = 0; + virtual mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& id) = 0; + virtual mozilla::ipc::IPCResult RecvFlushRenderingAsync() = 0; + virtual mozilla::ipc::IPCResult RecvForcePresent() = 0; + virtual mozilla::ipc::IPCResult RecvNotifyRegionInvalidated( + const nsIntRegion& region) = 0; + virtual mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() = 0; + virtual mozilla::ipc::IPCResult RecvAllPluginsCaptured() = 0; + virtual mozilla::ipc::IPCResult RecvBeginRecording( + const TimeStamp& aRecordingStart, BeginRecordingResolver&& aResolve) = 0; + virtual mozilla::ipc::IPCResult RecvEndRecordingToDisk( + EndRecordingToDiskResolver&& aResolve) = 0; + virtual mozilla::ipc::IPCResult RecvEndRecordingToMemory( + EndRecordingToMemoryResolver&& 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 RecvMakeSnapshot( + const SurfaceDescriptor& inSnapshot, const IntRect& dirtyRect) = 0; + virtual mozilla::ipc::IPCResult RecvFlushRendering() = 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; + + virtual mozilla::ipc::IPCResult RecvSupportsAsyncDXGISurface(bool* value) { + return IPC_FAIL_NO_REASON(this); + } + virtual mozilla::ipc::IPCResult RecvPreferredDXGIAdapter( + DxgiAdapterDesc* desc) { + return IPC_FAIL_NO_REASON(this); + } + + virtual already_AddRefed<PWebGLParent> AllocPWebGLParent() = 0; + + bool mCanSend; + + private: + RefPtr<CompositorManagerParent> mCompositorManager; +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS( + CompositorBridgeParentBase::TransformsToSkip) + +class CompositorBridgeParent final : public CompositorBridgeParentBase, + public CompositorController, + public CompositorVsyncSchedulerOwner { + friend class CompositorThreadHolder; + friend class InProcessCompositorSession; + friend class gfx::GPUProcessManager; + friend class gfx::GPUParent; + friend class PCompositorBridgeParent; +#ifdef FUZZING + friend class mozilla::ipc::ProtocolFuzzerHelper; +#endif + + 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); + + 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 RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, + const gfx::IntRect& aRect) override; + mozilla::ipc::IPCResult RecvFlushRendering() override; + mozilla::ipc::IPCResult RecvFlushRenderingAsync() override; + mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override; + mozilla::ipc::IPCResult RecvForcePresent() override; + + mozilla::ipc::IPCResult RecvNotifyRegionInvalidated( + const nsIntRegion& aRegion) 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(); + } + + // Unused for chrome <-> compositor communication (which this class does). + // @see ContentCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint + mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() override { + return IPC_OK(); + }; + + mozilla::ipc::IPCResult RecvAllPluginsCaptured() override; + mozilla::ipc::IPCResult RecvBeginRecording( + const TimeStamp& aRecordingStart, + BeginRecordingResolver&& aResolve) override; + mozilla::ipc::IPCResult RecvEndRecordingToDisk( + EndRecordingToDiskResolver&& aResolve) override; + mozilla::ipc::IPCResult RecvEndRecordingToMemory( + EndRecordingToMemoryResolver&& aResolve) override; + + void NotifyMemoryPressure() override; + void AccumulateMemoryReport(wr::MemoryReport*) override; + + void ActorDestroy(ActorDestroyReason why) override; + + void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const TransactionInfo& aInfo, + bool aHitTestUpdate) override; + void ScheduleComposite(LayerTransactionParent* aLayerTree) override; + bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override; + void LeaveTestMode(const LayersId& aId) override; + void ApplyAsyncProperties(LayerTransactionParent* aLayerTree, + TransformsToSkip aSkip) 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; + AsyncCompositionManager* GetCompositionManager( + LayerTransactionParent* aLayerTree) override { + return mCompositionManager; + } + void SetFixedLayerMargins(ScreenIntCoord aTop, ScreenIntCoord aBottom); + + PTextureParent* AllocPTextureParent( + const SurfaceDescriptor& aSharedData, const 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(); + + static void SetShadowProperties(Layer* aLayer); + + void NotifyChildCreated(LayersId aChild); + + void AsyncRender(); + + // Can be called from any thread + void ScheduleRenderOnCompositorThread() override; + + void ScheduleComposition(); + + void NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstPaint, + const FocusTarget& aFocusTarget, + bool aScheduleComposite, + uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + bool aHitTestUpdate); + + void UpdatePaintTime(LayerTransactionParent* aLayerTree, + const TimeDuration& aPaintTime) override; + void RegisterPayloads(LayerTransactionParent* aLayerTree, + const nsTArray<CompositionPayload>& aPayload) override; + + /** + * Check rotation info and schedule a rendering task if needed. + * Only can be called from compositor thread. + */ + void ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig, + bool aIsFirstPaint); + + static void ScheduleForcedComposition(const LayersId& aLayersId); + + /** + * Returns the unique layer tree identifier that corresponds to the root + * tree of this compositor. + */ + LayersId RootLayerTreeId(); + + /** + * Notify local and remote layer trees connected to this compositor that + * the compositor's local device is being reset. All layers must be + * invalidated to clear any cached TextureSources. + * + * This must be called on the compositor thread. + */ + void InvalidateRemoteLayers(); + + /** + * Initialize statics. + */ + static void InitializeStatics(); + + /** + * Returns a pointer to the CompositorBridgeParent corresponding to the given + * ID. + */ + static CompositorBridgeParent* GetCompositorBridgeParent(uint64_t id); + + /** + * 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<Layer> mRoot; + RefPtr<GeckoContentController> mController; + APZCTreeManagerParent* mApzcTreeManagerParent; + RefPtr<CompositorBridgeParent> mParent; + HostLayerManager* mLayerManager; + 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; + TargetConfig mTargetConfig; + LayerTransactionParent* mLayerTree; + nsTArray<PluginWindowData> mPluginData; + bool mUpdatedPluginDataAvailable; + + CompositorController* GetCompositorController() const; + MetricsSharingController* CrossProcessSharingController() const; + MetricsSharingController* InProcessSharingController() 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); + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + /** + * Calculates and requests the main thread update plugin positioning, clip, + * and visibility via ipc. + */ + bool UpdatePluginWindowState(LayersId aId); + + /** + * Plugin visibility helpers for the apz (main thread) and compositor + * thread. + */ + void ScheduleShowAllPluginWindows() override; + void ScheduleHideAllPluginWindows() override; + void ShowAllPluginWindows(); + void HideAllPluginWindows(); +#else + void ScheduleShowAllPluginWindows() override {} + void ScheduleHideAllPluginWindows() override {} +#endif + + /** + * Main thread response for a plugin visibility request made by the + * compositor thread. + */ + mozilla::ipc::IPCResult RecvRemotePluginsReady() override; + + /** + * Used by the profiler to denote when a vsync occured + */ + static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp); + + widget::CompositorWidget* GetWidget() { return mWidget; } + + virtual void ForceComposeToTarget( + gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) override; + + 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; + + CompositorOptions GetOptions() const { return mOptions; } + + TimeDuration GetVsyncInterval() const override { + // 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; + + webgpu::PWebGPUParent* AllocPWebGPUParent() override; + bool DeallocPWebGPUParent(webgpu::PWebGPUParent* aActor) override; + + 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; } + webgpu::WebGPUParent* GetWebGPUBridge() { return mWebGPUBridge; } + + already_AddRefed<PWebGLParent> AllocPWebGLParent() override { + MOZ_ASSERT_UNREACHABLE( + "This message is CrossProcessCompositorBridgeParent only"); + return nullptr; + } + + 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 the debug flags have been updated. + */ + static void UpdateWebRenderMultithreading(); + + /** + * Notify the compositor webrender batching parameters have been updated. + */ + static void UpdateWebRenderBatchingParameters(); + + /** + * Notify the compositor webrender profiler UI string has been updated. + */ + static void UpdateWebRenderProfilerUI(); + + /** + * Wrap the data structure to be sent over IPC. + */ + Maybe<CollectedFramesParams> WrapCollectedFrames(CollectedFrames&& aFrames); + + protected: + // Protected destructor, to discourage deletion outside of Release(): + virtual ~CompositorBridgeParent(); + + void DeferredDestroy(); + + PLayerTransactionParent* AllocPLayerTransactionParent( + const nsTArray<LayersBackend>& aBackendHints, + const LayersId& aId) override; + bool DeallocPLayerTransactionParent( + PLayerTransactionParent* aLayers) override; + + void SetEGLSurfaceRect(int x, int y, int width, int height); + + void InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints); + + public: + void PauseComposition(); + void ResumeComposition(); + void ResumeCompositionAndResize(int x, int y, int width, int height); + void Invalidate(); + bool IsPaused() { return mPaused; } + + protected: + void ForceComposition(); + void CancelCurrentCompositeTask(); + + // CompositorVsyncSchedulerOwner + bool IsPendingComposite() override; + void FinishPendingComposite() override; + void CompositeToTarget(VsyncId aId, gfx::DrawTarget* aTarget, + const gfx::IntRect* aRect = nullptr) override; + + bool InitializeAdvancedLayers(const nsTArray<LayersBackend>& aBackendHints, + TextureFactoryIdentifier* aOutIdentifier); + RefPtr<Compositor> NewCompositor( + const nsTArray<LayersBackend>& aBackendHints); + + /** + * 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(); + + /** + * Return true if current state allows compositing, that is + * finishing a layers transaction. + */ + bool CanComposite(); + + void DidComposite(const VsyncId& aId, TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd); + + void NotifyDidComposite(TransactionId aTransactionId, VsyncId aId, + TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd); + + // 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); + + RefPtr<HostLayerManager> mLayerManager; + RefPtr<Compositor> mCompositor; + RefPtr<AsyncCompositionManager> mCompositionManager; + RefPtr<AsyncImagePipelineManager> mAsyncImageManager; + RefPtr<WebRenderBridgeParent> mWrBridge; + RefPtr<webgpu::WebGPUParent> mWebGPUBridge; + widget::CompositorWidget* mWidget; + Maybe<TimeStamp> mTestTime; + CSSToLayoutDeviceScale mScale; + TimeDuration mVsyncRate; + + TransactionId mPendingTransaction; + TimeStamp mRefreshStartTime; + TimeStamp mTxnStartTime; + TimeStamp mFwdTime; + + bool mPaused; + bool mHaveCompositionRecorder; + bool mIsForcedFirstPaint; + + bool mUseExternalSurfaceSize; + gfx::IntSize mEGLSurfaceSize; + + CompositorOptions mOptions; + + mozilla::Monitor mPauseCompositionMonitor; + mozilla::Monitor mResumeCompositionMonitor; + + uint64_t mCompositorBridgeID; + LayersId mRootLayerTreeID; + + bool mOverrideComposeReadiness; + RefPtr<CancelableRunnable> mForceCompositionTask; + + RefPtr<APZCTreeManager> mApzcTreeManager; + RefPtr<APZSampler> mApzSampler; + RefPtr<APZUpdater> mApzUpdater; + RefPtr<OMTASampler> mOMTASampler; + + 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; + + TimeDuration mPaintTime; + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + // cached plugin data used to reduce the number of updates we request. + LayersId mLastPluginUpdateLayerTreeId; + nsIntPoint mPluginsLayerOffset; + nsIntRegion mPluginsLayerVisibleRegion; + nsTArray<PluginWindowData> mCachedPluginData; + // Time until which we will block composition to wait for plugin updates. + TimeStamp mWaitForPluginsUntil; + // Indicates that we have actually blocked a composition waiting for plugins. + bool mHaveBlockedForPlugins = false; + // indicates if plugin window visibility and metric updates are currently + // being defered due to a scroll operation. + bool mDeferPluginWindows; + // indicates if the plugin windows were hidden, and need to be made + // visible again even if their geometry has not changed. + bool mPluginWindowsHidden; +#endif + + 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); + +} // 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..b629d28229 --- /dev/null +++ b/gfx/layers/ipc/CompositorManagerChild.cpp @@ -0,0 +1,256 @@ +/* -*- 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; + +/* 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); +} + +/* 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); + return sInstance->CanSend(); +} + +/* static */ +void CompositorManagerChild::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + CompositorBridgeChild::ShutDown(); + + if (!sInstance) { + return; + } + + sInstance->Close(); + sInstance = nullptr; +} + +/* 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; + } +} + +/* 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, LayerManager* aLayerManager, uint32_t aNamespace, + CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) { + return nullptr; + } + + TimeDuration vsyncRate = gfxPlatform::GetPlatform() + ->GetHardwareVsync() + ->GetGlobalDisplay() + .GetVsyncRate(); + + CompositorBridgeOptions options = WidgetCompositorOptions( + aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, aSurfaceSize); + + 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( + LayerManager* 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) { + MOZ_ASSERT(aParent); + + SetOtherProcessId(base::GetCurrentProcId()); + ipc::MessageChannel* channel = aParent->GetIPCChannel(); + if (NS_WARN_IF(!Open(channel, CompositorThread(), ipc::ChildSide))) { + return; + } + + mCanSend = true; + AddRef(); + SetReplyTimeout(); +} + +CompositorManagerChild::CompositorManagerChild( + Endpoint<PCompositorManagerChild>&& aEndpoint, uint64_t aProcessToken, + uint32_t aNamespace) + : mProcessToken(aProcessToken), + mNamespace(aNamespace), + mResourceId(0), + mCanSend(false) { + if (NS_WARN_IF(!aEndpoint.Bind(this))) { + return; + } + + mCanSend = true; + AddRef(); + SetReplyTimeout(); +} + +void CompositorManagerChild::ActorDealloc() { + MOZ_ASSERT(!mCanSend); + Release(); +} + +void CompositorManagerChild::ActorDestroy(ActorDestroyReason aReason) { + mCanSend = false; + if (sInstance == this) { + sInstance = nullptr; + } +} + +void CompositorManagerChild::HandleFatalError(const char* aMsg) const { + 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..50adedbb63 --- /dev/null +++ b/gfx/layers/ipc/CompositorManagerChild.h @@ -0,0 +1,109 @@ +/* -*- 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/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 LayerManager; + +class CompositorManagerChild : public PCompositorManagerChild { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerChild) + + 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, LayerManager* aLayerManager, uint32_t aNamespace, + CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize); + + static already_AddRefed<CompositorBridgeChild> + CreateSameProcessWidgetCompositorBridge(LayerManager* aLayerManager, + uint32_t aNamespace); + + static CompositorManagerChild* GetInstance() { + MOZ_ASSERT(NS_IsMainThread()); + return sInstance; + } + + bool CanSend() const { + MOZ_ASSERT(NS_IsMainThread()); + return mCanSend; + } + + 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) const override; + + void ProcessingError(Result aCode, const char* aReason) override; + + bool ShouldContinueFromReplyTimeout() override; + + mozilla::ipc::IPCResult RecvNotifyWebRenderError( + const WebRenderError&& aError); + + private: + static StaticRefPtr<CompositorManagerChild> sInstance; + + CompositorManagerChild(CompositorManagerParent* aParent, + uint64_t aProcessToken, uint32_t aNamespace); + + CompositorManagerChild(Endpoint<PCompositorManagerChild>&& aEndpoint, + uint64_t aProcessToken, uint32_t aNamespace); + + virtual ~CompositorManagerChild() = default; + + void ActorDealloc() override; + + void SetReplyTimeout(); + + uint64_t mProcessToken; + uint32_t mNamespace; + uint32_t mResourceId; + bool mCanSend; +}; + +} // 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..8b6ee34080 --- /dev/null +++ b/gfx/layers/ipc/CompositorManagerParent.cpp @@ -0,0 +1,335 @@ +/* -*- 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/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(); + parent->SetOtherProcessId(base::GetCurrentProcId()); + return parent.forget(); +} + +/* static */ +bool CompositorManagerParent::Create( + Endpoint<PCompositorManagerParent>&& aEndpoint, 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(); + + 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) { + 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() + ->GetHardwareVsync() + ->GetGlobalDisplay() + .GetVsyncRate(); + + RefPtr<CompositorBridgeParent> bridge = + new CompositorBridgeParent(sInstance, aScale, vsyncRate, aOptions, + aUseExternalSurfaceSize, aSurfaceSize); + + sInstance->mPendingCompositorBridges.AppendElement(bridge); + return bridge.forget(); +} + +CompositorManagerParent::CompositorManagerParent() + : 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()); + + // Add the IPDL reference to ourself, so we can't get freed until IPDL is + // done with us. + AddRef(); + + 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()); + + StaticMutexAutoLock lock(sMutex); + if (sInstance == this) { + sInstance = nullptr; + } +} + +void CompositorManagerParent::ActorDealloc() { + GetCurrentSerialEventTarget()->Dispatch( + NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy", + this, &CompositorManagerParent::DeferredDestroy)); + +#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN + StaticMutexAutoLock lock(sMutex); + if (sActiveActors) { + sActiveActors->RemoveElement(this); + } +#endif + Release(); +} + +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()); + 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, const SurfaceDescriptorShared& aDesc) { + SharedSurfacesParent::Add(aId, 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(); +} + +/* 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..f16a85aca4 --- /dev/null +++ b/gfx/layers/ipc/CompositorManagerParent.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_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/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) + + public: + static already_AddRefed<CompositorManagerParent> CreateSameProcess(); + static bool Create(Endpoint<PCompositorManagerParent>&& aEndpoint, + bool aIsRoot); + static void Shutdown(); + + static already_AddRefed<CompositorBridgeParent> + CreateSameProcessWidgetCompositorBridge(CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize); + + mozilla::ipc::IPCResult RecvAddSharedSurface( + const wr::ExternalImageId& aId, const SurfaceDescriptorShared& aDesc); + mozilla::ipc::IPCResult RecvRemoveSharedSurface( + const wr::ExternalImageId& aId); + mozilla::ipc::IPCResult RecvReportSharedSurfacesMemory( + ReportSharedSurfacesMemoryResolver&&); + + mozilla::ipc::IPCResult RecvNotifyMemoryPressure(); + + mozilla::ipc::IPCResult RecvReportMemory(ReportMemoryResolver&&); + + void BindComplete(bool aIsRoot); + void ActorDestroy(ActorDestroyReason aReason) override; + + already_AddRefed<PCompositorBridgeParent> AllocPCompositorBridgeParent( + const CompositorBridgeOptions& aOpt); + + static void NotifyWebRenderError(wr::WebRenderError aError); + + private: + static StaticRefPtr<CompositorManagerParent> sInstance; + static StaticMutex sMutex; + +#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN + static StaticAutoPtr<nsTArray<CompositorManagerParent*>> sActiveActors; + static void ShutdownInternal(); +#endif + + CompositorManagerParent(); + virtual ~CompositorManagerParent(); + + void Bind(Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot); + + void ActorDealloc() override; + + void DeferredDestroy(); + + 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..53a23cbc6f --- /dev/null +++ b/gfx/layers/ipc/CompositorThread.cpp @@ -0,0 +1,159 @@ +/* -*- 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 "MainThreadUtils.h" +#include "VRManagerParent.h" +#include "mozilla/BackgroundHangMonitor.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "mozilla/layers/CanvasTranslator.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/ImageBridgeParent.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; + +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!"); + + nsCOMPtr<nsIThread> compositorThread; + nsresult rv = NS_NewNamedThread( + "Compositor", getter_AddRefs(compositorThread), + NS_NewRunnableFunction( + "CompositorThreadHolder::CompositorThreadHolderSetup", []() { + 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); + })); + + 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. + 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(); + + // 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)]() { + nsCOMPtr<nsIThread> thread = NS_GetCurrentThread(); + static_cast<nsThread*>(thread.get())->SetUseHangMonitor(false); + })); + + sCompositorThreadHolder = nullptr; + sBackgroundHangMonitor = nullptr; + + SpinEventLoopUntil([&]() { + 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; +} + +} // 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..2dea93e9d0 --- /dev/null +++ b/gfx/layers/ipc/CompositorThread.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_CompositorThread_h +#define mozilla_layers_CompositorThread_h + +#include "nsISupportsImpl.h" +#include "nsIThread.h" + +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(); + + 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..4662a1e3ae --- /dev/null +++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp @@ -0,0 +1,365 @@ +/* -*- 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); } + +bool CompositorVsyncScheduler::Observer::NotifyVsync(const VsyncEvent& aVsync) { + MutexAutoLock lock(mMutex); + if (!mOwner) { + return false; + } + 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), + mVsyncNotificationsSkipped(0), + mWidget(aWidget), + mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor"), + mCurrentCompositeTask(nullptr), + 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) { + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + if (mCurrentCompositeTask == nullptr && CompositorThread()) { + RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<VsyncEvent>( + "layers::CompositorVsyncScheduler::Composite", this, + &CompositorVsyncScheduler::Composite, aVsyncEvent); + 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() { + 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); + } 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); + } + } +} + +bool 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); + } +#else + PostCompositeTask(aVsync); +#endif // defined(MOZ_WIDGET_ANDROID) + + PostVRTask(aVsync.mTime); + return true; +} + +void CompositorVsyncScheduler::CancelCurrentVRTask() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || + NS_IsMainThread()); + MonitorAutoLock lock(mCurrentVRTaskMonitor); + if (mCurrentVRTask) { + mCurrentVRTask->Cancel(); + mCurrentVRTask = nullptr; + } +} + +void CompositorVsyncScheduler::CancelCurrentCompositeTask() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || + NS_IsMainThread()); + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + if (mCurrentCompositeTask) { + mCurrentCompositeTask->Cancel(); + mCurrentCompositeTask = nullptr; + } +} + +void CompositorVsyncScheduler::Composite(const VsyncEvent& aVsyncEvent) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(mVsyncSchedulerOwner); + + { // scope lock + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + 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, 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(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(), aTarget, aRect); +} + +bool CompositorVsyncScheduler::NeedsComposite() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return (bool)mCompositeRequestedAt; +} + +bool CompositorVsyncScheduler::FlushPendingComposite() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mCompositeRequestedAt) { + CancelCurrentCompositeTask(); + ForceComposeToTarget(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..67e3d73b03 --- /dev/null +++ b/gfx/layers/ipc/CompositorVsyncScheduler.h @@ -0,0 +1,173 @@ +/* -*- 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/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. + */ + bool 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(); + + /** + * Cancel any composite task that has been scheduled but hasn't run yet. + */ + void 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(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); + + // 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); + + void ObserveVsync(); + void UnobserveVsync(); + + void DispatchVREvents(TimeStamp aVsyncTimestamp); + + class Observer final : public VsyncObserver { + public: + explicit Observer(CompositorVsyncScheduler* aOwner); + bool NotifyVsync(const VsyncEvent& aVsync) override; + void Destroy(); + + private: + virtual ~Observer(); + + Mutex mMutex; + // Hold raw pointer to avoid mutual reference. + CompositorVsyncScheduler* mOwner; + }; + + CompositorVsyncSchedulerOwner* mVsyncSchedulerOwner; + SampleTime mLastComposeTime; + TimeStamp mLastVsyncTime; + TimeStamp mLastVsyncOutputTime; + VsyncId mLastVsyncId; + + bool mAsapScheduling; + bool mIsObservingVsync; + TimeStamp mCompositeRequestedAt; + int32_t mVsyncNotificationsSkipped; + widget::CompositorWidget* mWidget; + RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver; + + mozilla::Monitor mCurrentCompositeTaskMonitor; + RefPtr<CancelableRunnable> mCurrentCompositeTask; + + mozilla::Monitor mCurrentVRTaskMonitor; + 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..1691f6535a --- /dev/null +++ b/gfx/layers/ipc/CompositorVsyncSchedulerOwner.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 mozilla_layers_CompositorVsyncSchedulerOwner_h +#define mozilla_layers_CompositorVsyncSchedulerOwner_h + +#include "mozilla/VsyncDispatcher.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, 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..793919e7f3 --- /dev/null +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp @@ -0,0 +1,753 @@ +/* -*- 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 "LayerTransactionParent.h" // for LayerTransactionParent +#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/D3DMessageUtils.h" // for DxgiAdapterDesc +#include "mozilla/dom/WebGLParent.h" +#include "mozilla/ipc/Transport.h" // for Transport +#include "mozilla/layers/AnimationHelper.h" // for CompositorAnimationStorage +#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent +#include "mozilla/layers/APZUpdater.h" // for APZUpdater +#include "mozilla/layers/AsyncCompositionManager.h" +#include "mozilla/layers/CompositorOptions.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/PLayerTransactionParent.h" +#include "mozilla/layers/RemoteContentController.h" +#include "mozilla/layers/WebRenderBridgeParent.h" +#include "mozilla/layers/AsyncImagePipelineManager.h" +#include "mozilla/webgpu/WebGPUParent.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/StaticPtr.h" +#include "mozilla/Telemetry.h" +#ifdef MOZ_GECKO_PROFILER +# include "mozilla/BaseProfilerMarkerTypes.h" +#endif + +namespace mozilla { + +namespace layers { + +// defined in CompositorBridgeParent.cpp +typedef std::map<LayersId, CompositorBridgeParent::LayerTreeState> LayerTreeMap; +extern LayerTreeMap sIndirectLayerTrees; +extern StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock; +void UpdateIndirectTree(LayersId aId, Layer* aRoot, + const TargetConfig& aTargetConfig); +void EraseLayerState(LayersId aId); + +mozilla::ipc::IPCResult +ContentCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint() { + mNotifyAfterRemotePaint = true; + return IPC_OK(); +} + +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)); +} + +PLayerTransactionParent* +ContentCompositorBridgeParent::AllocPLayerTransactionParent( + const nsTArray<LayersBackend>&, const LayersId& aId) { + MOZ_ASSERT(aId.IsValid()); + + // Check to see if this child process has access to this layer tree. + if (!LayerTreeOwnerTracker::Get()->IsMapped(aId, OtherPid())) { + NS_ERROR( + "Unexpected layers id in AllocPLayerTransactionParent; dropping " + "message..."); + return nullptr; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + + CompositorBridgeParent::LayerTreeState* state = nullptr; + LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId); + if (sIndirectLayerTrees.end() != itr) { + state = &itr->second; + } + + if (state && state->mLayerManager) { + state->mContentCompositorBridgeParent = this; + HostLayerManager* lm = state->mLayerManager; + CompositorAnimationStorage* animStorage = + state->mParent ? state->mParent->GetAnimationStorage() : nullptr; + TimeDuration vsyncRate = + state->mParent ? state->mParent->GetVsyncInterval() : TimeDuration(); + LayerTransactionParent* p = + new LayerTransactionParent(lm, this, animStorage, aId, vsyncRate); + p->AddIPDLReference(); + sIndirectLayerTrees[aId].mLayerTree = p; + return p; + } + + NS_WARNING("Created child without a matching parent?"); + LayerTransactionParent* p = new LayerTransactionParent( + /* aManager */ nullptr, this, /* aAnimStorage */ nullptr, aId, + TimeDuration()); + p->AddIPDLReference(); + return p; +} + +bool ContentCompositorBridgeParent::DeallocPLayerTransactionParent( + PLayerTransactionParent* aLayers) { + LayerTransactionParent* slp = static_cast<LayerTransactionParent*>(aLayers); + EraseLayerState(slp->GetId()); + static_cast<LayerTransactionParent*>(aLayers)->ReleaseIPDLReference(); + return true; +} + +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 useWebRender = false; + RefPtr<APZCTreeManager> temp = new APZCTreeManager(dummyId, useWebRender); + RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp, useWebRender); + tempUpdater->ClearTree(dummyId); + return new APZCTreeManagerParent(aLayersId, temp, tempUpdater); + } + + 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; +} + +webgpu::PWebGPUParent* ContentCompositorBridgeParent::AllocPWebGPUParent() { + webgpu::WebGPUParent* parent = new webgpu::WebGPUParent(); + parent->AddRef(); // IPDL reference + return parent; +} + +bool ContentCompositorBridgeParent::DeallocPWebGPUParent( + webgpu::PWebGPUParent* aActor) { + webgpu::WebGPUParent* parent = static_cast<webgpu::WebGPUParent*>(aActor); + parent->Release(); // IPDL reference + return true; +} + +mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvNotifyChildCreated( + const LayersId& child, CompositorOptions* aOptions) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin(); + it != sIndirectLayerTrees.end(); it++) { + CompositorBridgeParent::LayerTreeState* lts = &it->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::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 == status.sequenceNumber() && !dm->HasDeviceReset()) { + *isContentOnlyTDR = true; + } + +#endif + return IPC_OK(); +}; + +void ContentCompositorBridgeParent::ShadowLayersUpdated( + LayerTransactionParent* aLayerTree, const TransactionInfo& aInfo, + bool aHitTestUpdate) { + LayersId id = aLayerTree->GetId(); + + MOZ_ASSERT(id.IsValid()); + + CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state) { + return; + } + MOZ_ASSERT(state->mParent); + state->mParent->ScheduleRotationOnCompositorThread(aInfo.targetConfig(), + aInfo.isFirstPaint()); + + Layer* shadowRoot = aLayerTree->GetRoot(); + if (shadowRoot) { + CompositorBridgeParent::SetShadowProperties(shadowRoot); + } + UpdateIndirectTree(id, shadowRoot, aInfo.targetConfig()); + + // Cache the plugin data for this remote layer tree + state->mPluginData = aInfo.plugins().Clone(); + state->mUpdatedPluginDataAvailable = true; + + state->mParent->NotifyShadowTreeTransaction( + id, aInfo.isFirstPaint(), aInfo.focusTarget(), aInfo.scheduleComposite(), + aInfo.paintSequenceNumber(), aInfo.isRepeatTransaction(), aHitTestUpdate); + + // Send the 'remote paint ready' message to the content thread if it has + // already asked. + if (mNotifyAfterRemotePaint) { + Unused << SendRemotePaintIsReady(); + mNotifyAfterRemotePaint = false; + } + + if (aLayerTree->ShouldParentObserveEpoch()) { + // Note that we send this through the window compositor, since this needs + // to reach the widget owning the tab. + Unused << state->mParent->SendObserveLayersUpdate( + id, aLayerTree->GetChildEpoch(), true); + } + + auto endTime = TimeStamp::Now(); +#ifdef MOZ_GECKO_PROFILER + if (profiler_can_accept_markers()) { + profiler_add_marker( + "CONTENT_FULL_PAINT_TIME", geckoprofiler::category::GRAPHICS, + MarkerTiming::Interval(aInfo.transactionStart(), endTime), + baseprofiler::markers::ContentBuildMarker{}); + } +#endif + Telemetry::Accumulate( + Telemetry::CONTENT_FULL_PAINT_TIME, + static_cast<uint32_t>( + (endTime - aInfo.transactionStart()).ToMilliseconds())); + + RegisterPayloads(aLayerTree, aInfo.payload()); + + aLayerTree->SetPendingTransactionId( + aInfo.id(), aInfo.vsyncId(), aInfo.vsyncStart(), aInfo.refreshStart(), + aInfo.transactionStart(), endTime, aInfo.containsSVG(), aInfo.url(), + aInfo.fwdTime()); +} + +void ContentCompositorBridgeParent::DidCompositeLocked( + LayersId aId, const VsyncId& aVsyncId, TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd) { + sIndirectLayerTreesLock->AssertCurrentThreadOwns(); + if (LayerTransactionParent* layerTree = sIndirectLayerTrees[aId].mLayerTree) { + TransactionId transactionId = + layerTree->FlushTransactionId(aVsyncId, aCompositeEnd); + if (transactionId.IsValid()) { + Unused << SendDidComposite(aId, transactionId, aCompositeStart, + aCompositeEnd); + } + } else if (sIndirectLayerTrees[aId].mWrBridge) { + MOZ_ASSERT(false); // this should never get called for a WR compositor + } +} + +void ContentCompositorBridgeParent::ScheduleComposite( + LayerTransactionParent* aLayerTree) { + LayersId id = aLayerTree->GetId(); + MOZ_ASSERT(id.IsValid()); + CompositorBridgeParent* parent; + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + parent = sIndirectLayerTrees[id].mParent; + } + if (parent) { + parent->ScheduleComposite(aLayerTree); + } +} + +void ContentCompositorBridgeParent::NotifyClearCachedResources( + LayerTransactionParent* aLayerTree) { + LayersId id = aLayerTree->GetId(); + MOZ_ASSERT(id.IsValid()); + + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (state && state->mParent) { + // Note that we send this through the window compositor, since this needs + // to reach the widget owning the tab. + Unused << state->mParent->SendObserveLayersUpdate( + id, aLayerTree->GetChildEpoch(), false); + } +} + +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::ApplyAsyncProperties( + LayerTransactionParent* aLayerTree, TransformsToSkip aSkip) { + LayersId id = aLayerTree->GetId(); + MOZ_ASSERT(id.IsValid()); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state) { + return; + } + + MOZ_ASSERT(state->mParent); + state->mParent->ApplyAsyncProperties(aLayerTree, aSkip); +} + +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)); +} + +AsyncCompositionManager* ContentCompositorBridgeParent::GetCompositionManager( + LayerTransactionParent* aLayerTree) { + LayersId id = aLayerTree->GetId(); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state) { + return nullptr; + } + + MOZ_ASSERT(state->mParent); + return state->mParent->GetCompositionManager(aLayerTree); +} + +void ContentCompositorBridgeParent::DeferredDestroy() { mSelfRef = nullptr; } + +ContentCompositorBridgeParent::~ContentCompositorBridgeParent() { + MOZ_ASSERT(XRE_GetIOMessageLoop()); +} + +PTextureParent* ContentCompositorBridgeParent::AllocPTextureParent( + const SurfaceDescriptor& aSharedData, const 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 && state->mLayerManager) { + actualBackend = state->mLayerManager->GetBackendType(); + } + + 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, aReadLock, + aLayersBackend, aFlags, 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) { + return mCanvasTranslator->WaitForSurfaceDescriptor(aTextureId); +} + +bool ContentCompositorBridgeParent::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void ContentCompositorBridgeParent::UpdatePaintTime( + LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) { + LayersId id = aLayerTree->GetId(); + MOZ_ASSERT(id.IsValid()); + + CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state || !state->mParent) { + return; + } + + state->mParent->UpdatePaintTime(aLayerTree, aPaintTime); +} + +void ContentCompositorBridgeParent::RegisterPayloads( + LayerTransactionParent* aLayerTree, + const nsTArray<CompositionPayload>& aPayload) { + LayersId id = aLayerTree->GetId(); + MOZ_ASSERT(id.IsValid()); + + CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state || !state->mParent) { + return; + } + + state->mParent->RegisterPayloads(aLayerTree, aPayload); +} + +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); +} + +static inline bool AllowDirectDXGISurfaceDrawing() { + if (!StaticPrefs::dom_ipc_plugins_asyncdrawing_enabled()) { + return false; + } +#if defined(XP_WIN) + gfx::DeviceManagerDx* dm = gfx::DeviceManagerDx::Get(); + MOZ_ASSERT(dm); + if (!dm || !dm->GetCompositorDevice() || !dm->TextureSharingWorks()) { + return false; + } + return true; +#else + return false; +#endif +} + +mozilla::ipc::IPCResult +ContentCompositorBridgeParent::RecvSupportsAsyncDXGISurface(bool* value) { + *value = AllowDirectDXGISurfaceDrawing(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvPreferredDXGIAdapter( + DxgiAdapterDesc* aOutDesc) { + PodZero(aOutDesc); +#ifdef XP_WIN + if (!AllowDirectDXGISurfaceDrawing()) { + return IPC_FAIL_NO_REASON(this); + } + + RefPtr<ID3D11Device> device = + gfx::DeviceManagerDx::Get()->GetCompositorDevice(); + if (!device) { + return IPC_FAIL_NO_REASON(this); + } + + RefPtr<IDXGIDevice> dxgi; + if (FAILED(device->QueryInterface(__uuidof(IDXGIDevice), + getter_AddRefs(dxgi))) || + !dxgi) { + return IPC_FAIL_NO_REASON(this); + } + RefPtr<IDXGIAdapter> adapter; + if (FAILED(dxgi->GetAdapter(getter_AddRefs(adapter))) || !adapter) { + return IPC_FAIL_NO_REASON(this); + } + + DXGI_ADAPTER_DESC desc; + if (FAILED(adapter->GetDesc(&desc))) { + return IPC_FAIL_NO_REASON(this); + } + + *aOutDesc = DxgiAdapterDesc::From(desc); +#endif + return IPC_OK(); +} + +already_AddRefed<dom::PWebGLParent> +ContentCompositorBridgeParent::AllocPWebGLParent() { + RefPtr<dom::PWebGLParent> parent = new dom::WebGLParent(); + return parent.forget(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.h b/gfx/layers/ipc/ContentCompositorBridgeParent.h new file mode 100644 index 0000000000..2d50894eb3 --- /dev/null +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.h @@ -0,0 +1,246 @@ +/* -*- 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 { +namespace webgpu { +class PWebGPUParent; +} // namespace webgpu + +namespace 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), + mNotifyAfterRemotePaint(false), + 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 RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, + const gfx::IntRect& aRect) override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvFlushRendering() override { return IPC_OK(); } + mozilla::ipc::IPCResult RecvFlushRenderingAsync() override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvForcePresent() override { return IPC_OK(); } + mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override { + return IPC_OK(); + } + mozilla::ipc::IPCResult RecvNotifyRegionInvalidated( + const nsIntRegion& aRegion) 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 RecvCheckContentOnlyTDR( + const uint32_t& sequenceNum, bool* isContentOnlyTDR) override; + + mozilla::ipc::IPCResult RecvAllPluginsCaptured() override { return IPC_OK(); } + + mozilla::ipc::IPCResult RecvBeginRecording( + const TimeStamp& aRecordingStart, + BeginRecordingResolver&& aResolve) override { + aResolve(false); + return IPC_OK(); + } + + mozilla::ipc::IPCResult RecvEndRecordingToDisk( + EndRecordingToDiskResolver&& aResolve) override { + aResolve(false); + return IPC_OK(); + } + + mozilla::ipc::IPCResult RecvEndRecordingToMemory( + EndRecordingToMemoryResolver&& aResolve) override { + aResolve(Nothing()); + return IPC_OK(); + } + + /** + * Tells this CompositorBridgeParent to send a message when the compositor has + * received the transaction. + */ + mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() override; + + PLayerTransactionParent* AllocPLayerTransactionParent( + const nsTArray<LayersBackend>& aBackendHints, + const LayersId& aId) override; + + bool DeallocPLayerTransactionParent( + PLayerTransactionParent* aLayers) override; + + void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const TransactionInfo& aInfo, + bool aHitTestUpdate) override; + void ScheduleComposite(LayerTransactionParent* aLayerTree) override; + void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) override; + bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override; + void LeaveTestMode(const LayersId& aId) override; + void ApplyAsyncProperties(LayerTransactionParent* aLayerTree, + TransformsToSkip aSkip) 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; + + AsyncCompositionManager* GetCompositionManager( + LayerTransactionParent* aParent) override; + mozilla::ipc::IPCResult RecvRemotePluginsReady() override { + return IPC_FAIL_NO_REASON(this); + } + + already_AddRefed<dom::PWebGLParent> AllocPWebGLParent() 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, const 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; + + void UpdatePaintTime(LayerTransactionParent* aLayerTree, + const TimeDuration& aPaintTime) override; + void RegisterPayloads(LayerTransactionParent* aLayerTree, + const nsTArray<CompositionPayload>& aPayload) override; + + PWebRenderBridgeParent* AllocPWebRenderBridgeParent( + const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize, + const WindowKind& aWindowKind) override; + bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override; + + webgpu::PWebGPUParent* AllocPWebGPUParent() override; + bool DeallocPWebGPUParent(webgpu::PWebGPUParent* 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; + + mozilla::ipc::IPCResult RecvSupportsAsyncDXGISurface(bool* value) override; + mozilla::ipc::IPCResult RecvPreferredDXGIAdapter( + DxgiAdapterDesc* desc) override; + + 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; + + // If true, we should send a RemotePaintIsReady message when the layer + // transaction is received + bool mNotifyAfterRemotePaint; + bool mDestroyCalled; + + RefPtr<CanvasTranslator> mCanvasTranslator; +}; + +} // namespace layers +} // namespace mozilla + +#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..bf38b22226 --- /dev/null +++ b/gfx/layers/ipc/ISurfaceAllocator.cpp @@ -0,0 +1,239 @@ +/* -*- 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); + +/* static */ +uint32_t CompositableForwarder::GetMaxFileDescriptorsPerMessage() { +#if defined(OS_POSIX) + static const uint32_t kMaxFileDescriptors = + FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE; +#else + // default number that works everywhere else + static const uint32_t kMaxFileDescriptors = 250; +#endif + return kMaxFileDescriptors; +} + +mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType() { + return ipc::SharedMemory::SharedMemoryType::TYPE_BASIC; +} + +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). +#if defined(OS_POSIX) + static const uint32_t kMaxMessageNumber = + FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE; +#else + // default number that works everywhere else + static const uint32_t kMaxMessageNumber = 250; +#endif + + 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, OptimalShmemType(), + &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..740c3b2b3e --- /dev/null +++ b/gfx/layers/ipc/ISurfaceAllocator.h @@ -0,0 +1,287 @@ +/* -*- 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/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; + +mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType(); + +/** + * 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; } + + 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::DrawTarget> GetDrawTargetForDescriptor( + const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend); + +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..0c3024426f --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -0,0 +1,995 @@ +/* -*- 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 "Layers.h" // for Layer, etc +#include "ShadowLayers.h" // for ShadowLayerForwarder +#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/ipc/Transport.h" // for Transport +#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 + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/layers/AndroidHardwareBuffer.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(); + + auto fenceFd = t.mTextureClient->GetInternalData()->GetAcquireFence(); + if (fenceFd.IsValid()) { + mTxn->AddNoSwapEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpDeliverAcquireFence(nullptr, t.mTextureClient->GetIPDLActor(), + fenceFd))); + } + + textures.AppendElement( + TimedTexture(nullptr, 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::UseComponentAlphaTextures( + CompositableClient* aCompositable, TextureClient* aTextureOnBlack, + TextureClient* aTextureOnWhite) { + MOZ_CRASH("should not be called"); +} + +void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary( + TextureClient* aClient) { + if (!aClient) { + return; + } + +#ifdef MOZ_WIDGET_ANDROID + auto bufferId = aClient->GetInternalData()->GetBufferId(); + if (bufferId.isSome()) { + MOZ_ASSERT(aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END); + AndroidHardwareBufferManager::Get()->HoldUntilNotifyNotUsed( + bufferId.ref(), GetFwdTransactionId(), /* aUsesImageBridge */ true); + } +#endif + + // 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; +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::ActorDealloc() { this->Release(); } + +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()); + + // Note: this is static, rather than per-IBC, so IDs are not re-used across + // ImageBridgeChild instances. This is relevant for the GPU process, where + // we don't want old IDs to potentially leak into a recreated ImageBridge. + static uint64_t sNextID = 1; + uint64_t id = sNextID++; + + // ImageClient of ImageContainer provides aImageContainer. + // But offscreen canvas does not provide it. + if (aImageContainer) { + MutexAutoLock lock(mContainerMapLock); + MOZ_ASSERT(mImageContainerListeners.find(id) == + mImageContainerListeners.end()); + mImageContainerListeners.emplace( + id, aImageContainer->GetImageContainerListener()); + } + + CompositableHandle handle(id); + aCompositable->InitIPDL(handle); + SendNewCompositable(handle, aCompositable->GetTextureInfo(), + GetCompositorBackendType()); +} + +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, Layer::CONTENT_OPAQUE); + 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 (!IsSameProcess()) { + ShadowLayerForwarder::PlatformSyncBeforeUpdate(); + } + + 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; + } + + // This reference is dropped in DeallocPImageBridgeChild. + this->AddRef(); + + mCanSend = true; +} + +void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) { + ipc::MessageChannel* parentChannel = aParent->GetIPCChannel(); + Open(parentChannel, aParent->GetThread(), mozilla::ipc::ChildSide); + + // This reference is dropped in DeallocPImageBridgeChild. + this->AddRef(); + + 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) { + // ImageHost is incompatible between WebRender enabled and WebRender disabled. + // Then drop all ImageContainers' ImageClients during disabling WebRender. + bool disablingWebRender = + GetCompositorBackendType() == LayersBackend::LAYERS_WR && + aIdentifier.mParentBackend != LayersBackend::LAYERS_WR; + + // Do not update TextureFactoryIdentifier if aIdentifier is going to disable + // WebRender, but gecko is still using WebRender. Since gecko uses different + // incompatible ImageHost and TextureHost between WebRender and non-WebRender. + // + // Even when WebRender is still in use, if non-accelerated widget is opened, + // aIdentifier disables WebRender at ImageBridgeChild. + if (disablingWebRender && gfxVars::UseWebRender()) { + return; + } + + // D3DTexture might become obsolte. To prevent to use obsoleted D3DTexture, + // drop all ImageContainers' ImageClients. + + // During re-creating GPU process, there was a period that ImageBridgeChild + // was re-created, but ImageBridgeChild::UpdateTextureFactoryIdentifier() was + // not called yet. In the period, if ImageBridgeChild::CreateImageClient() is + // called, ImageBridgeParent creates incompatible ImageHost than + // WebRenderImageHost. + bool initializingWebRender = + GetCompositorBackendType() != LayersBackend::LAYERS_WR && + aIdentifier.mParentBackend == LayersBackend::LAYERS_WR; + + bool needsDrop = disablingWebRender || initializingWebRender; + +#if defined(XP_WIN) + RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice(); + needsDrop |= !!mImageDevice && mImageDevice != device && + GetCompositorBackendType() == LayersBackend::LAYERS_D3D11; + mImageDevice = device; +#endif + + IdentifyTextureHost(aIdentifier); + if (needsDrop) { + nsTArray<RefPtr<ImageContainerListener> > listeners; + { + MutexAutoLock lock(mContainerMapLock); + for (const auto& entry : mImageContainerListeners) { + listeners.AppendElement(entry.second); + } + } + // Drop ImageContainer's ImageClient whithout holding mContainerMapLock to + // avoid deadlock. + for (auto container : listeners) { + container->DropImageClient(); + } + } +} + +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::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (!InImageBridgeChildThread()) { + return DispatchAllocShmemInternal(aSize, aType, aShmem, + true); // true: unsafe + } + + if (!CanSend()) { + return false; + } + return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem); +} + +bool ImageBridgeChild::AllocShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (!InImageBridgeChildThread()) { + return DispatchAllocShmemInternal(aSize, aType, aShmem, + false); // false: unsafe + } + + if (!CanSend()) { + return false; + } + return PImageBridgeChild::AllocShmem(aSize, aType, aShmem); +} + +void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize, + SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem, bool aUnsafe, + bool* aSuccess) { + AutoCompleteTask complete(aTask); + + if (!CanSend()) { + return; + } + + bool ok = false; + if (aUnsafe) { + ok = AllocUnsafeShmem(aSize, aType, aShmem); + } else { + ok = AllocShmem(aSize, aType, aShmem); + } + *aSuccess = ok; +} + +bool ImageBridgeChild::DispatchAllocShmemInternal( + size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem, + bool aUnsafe) { + SynchronousTask task("AllocatorProxy alloc"); + + bool success = false; + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyAllocShmemNow, + &task, aSize, aType, 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&, const 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; + } + case AsyncParentMessageData::TOpDeliverReleaseFence: { +#ifdef MOZ_WIDGET_ANDROID + const OpDeliverReleaseFence& op = message.get_OpDeliverReleaseFence(); + ipc::FileDescriptor fenceFd; + if (op.fenceFd().isSome()) { + fenceFd = *op.fenceFd(); + } + AndroidHardwareBufferManager::Get()->NotifyNotUsed( + std::move(fenceFd), op.bufferId(), op.fwdTransactionId(), + op.usesImageBridge()); +#else + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); +#endif + 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, const ReadLockDescriptor& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) { + MOZ_ASSERT(CanSend()); + return SendPTextureConstructor(aSharedData, 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(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(nullptr, 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) const { + 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..76663f7ffd --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeChild.h @@ -0,0 +1,390 @@ +/* -*- 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, const 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); + + /** + * 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, + SharedMemory::SharedMemoryType aType, + 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 UseComponentAlphaTextures(CompositableClient* aCompositable, + TextureClient* aClientOnBlack, + TextureClient* aClientOnWhite) 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; + + void UseTiledLayerBuffer( + CompositableClient* aCompositable, + const SurfaceDescriptorTiles& aTileLayerDescriptor) override { + MOZ_CRASH("should not be called"); + } + + void UpdateTextureRegion(CompositableClient* aCompositable, + const ThebesBufferData& aThebesBufferData, + const nsIntRegion& aUpdatedRegion) override { + MOZ_CRASH("should not be called"); + } + + // 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::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) override; + bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + 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, const ReadLockDescriptor& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId, + nsISerialEventTarget* aTarget = nullptr) 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) const override; + + wr::MaybeExternalImageId GetNextExternalImageId() override; + + protected: + explicit ImageBridgeChild(uint32_t aNamespace); + bool DispatchAllocShmemInternal(size_t aSize, + SharedMemory::SharedMemoryType aType, + 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; + void ActorDealloc() 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; + std::unordered_map<uint64_t, RefPtr<ImageContainerListener>> + mImageContainerListeners; + RefPtr<ImageContainerListener> FindListener( + const CompositableHandle& aHandle); + +#if defined(XP_WIN) + /** + * Used for checking if D3D11Device is updated. + */ + RefPtr<ID3D11Device> mImageDevice; +#endif +}; + +} // 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..8022b76687 --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -0,0 +1,796 @@ +/* -*- 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 "GeckoProfiler.h" +#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/ipc/Transport.h" // for Transport +#include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent +#include "mozilla/layers/BufferTexture.h" +#include "mozilla/layers/CompositableTransactionParent.h" +#include "mozilla/layers/LayerManagerComposite.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/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 + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/layers/AndroidHardwareBuffer.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) + : mThread(aThread), + 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); + parent->mSelfRef = parent; + + { + 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()); + + 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; + 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 + // mSelfRef. If mSelfRef 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))) { + return IPC_FAIL_NO_REASON(this); + } + uint32_t dropped = compositable->GetDroppedFrames(); + if (dropped) { + Unused << SendReportFramesDropped(edit.compositable(), dropped); + } + } + + if (!IsSameProcess()) { + // Ensure that any pending operations involving back and front + // buffers have completed, so that neither process stomps on the + // other's buffer contents. + LayerManagerComposite::PlatformSyncBeforeReplyUpdate(); + } + + return IPC_OK(); +} + +/* static */ +bool ImageBridgeParent::CreateForContent( + Endpoint<PImageBridgeParent>&& aEndpoint) { + nsCOMPtr<nsISerialEventTarget> compositorThread = CompositorThread(); + if (!compositorThread) { + return false; + } + + RefPtr<ImageBridgeParent> bridge = + new ImageBridgeParent(compositorThread, aEndpoint.OtherPid()); + 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; + mSelfRef = this; + + // 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, + const LayersBackend& aLayersBackend) { + bool useWebRender = aLayersBackend == LayersBackend::LAYERS_WR; + RefPtr<CompositableHost> host = AddCompositable(aHandle, aInfo, useWebRender); + 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, const ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const uint64_t& aSerial, const wr::MaybeExternalImageId& aExternalImageId) { + return TextureHost::CreateIPDLActor(this, aSharedData, aReadLock, + aLayersBackend, aFlags, 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; + mSelfRef = nullptr; // "this" ImageBridge may get deleted here. +} + +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::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (mClosed) { + return false; + } + return PImageBridgeParent::AllocShmem(aSize, aType, aShmem); +} + +bool ImageBridgeParent::AllocUnsafeShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (mClosed) { + return false; + } + return PImageBridgeParent::AllocUnsafeShmem(aSize, aType, 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; + } + +#ifdef MOZ_WIDGET_ANDROID + if (auto hardwareBuffer = texture->GetAndroidHardwareBuffer()) { + MOZ_ASSERT(texture->GetFlags() & TextureFlags::RECYCLE); + + Maybe<FileDescriptor> fenceFd = Some(FileDescriptor()); + auto* compositor = texture->GetProvider() + ? texture->GetProvider()->AsCompositorOGL() + : nullptr; + if (compositor) { + fenceFd = Some(compositor->GetReleaseFence()); + } + + auto* wrTexture = texture->AsWebRenderTextureHost(); + if (wrTexture) { + MOZ_ASSERT(!fenceFd->IsValid()); + fenceFd = Some(texture->GetAndResetReleaseFence()); + } + + // Invalid file descriptor could not be sent via IPC, but + // OpDeliverReleaseFence message needs to be sent to child side. + if (!fenceFd->IsValid()) { + fenceFd = Nothing(); + } + mPendingAsyncMessage.push_back(OpDeliverReleaseFence( + std::move(fenceFd), hardwareBuffer->mId, aTransactionId, + /* usesImageBridge */ true)); + } +#endif + + 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(); + } +} + +/* static */ +void ImageBridgeParent::NotifyBufferNotUsedOfCompositorBridge( + base::ProcessId aChildProcessId, TextureHost* aTexture, + uint64_t aTransactionId) { + RefPtr<ImageBridgeParent> bridge = GetInstance(aChildProcessId); + if (!bridge || bridge->mClosed) { + return; + } + bridge->NotifyBufferNotUsedOfCompositorBridge(aTexture, aTransactionId); +} + +void ImageBridgeParent::NotifyBufferNotUsedOfCompositorBridge( + TextureHost* aTexture, uint64_t aTransactionId) { + MOZ_ASSERT(aTexture); + MOZ_ASSERT(aTexture->GetAndroidHardwareBuffer()); + +#ifdef MOZ_WIDGET_ANDROID + auto* compositor = aTexture->GetProvider() + ? aTexture->GetProvider()->AsCompositorOGL() + : nullptr; + Maybe<FileDescriptor> fenceFd = Some(FileDescriptor()); + if (compositor) { + fenceFd = Some(compositor->GetReleaseFence()); + } + + auto* wrTexture = aTexture->AsWebRenderTextureHost(); + if (wrTexture) { + MOZ_ASSERT(!fenceFd->IsValid()); + fenceFd = Some(aTexture->GetAndResetReleaseFence()); + } + + // Invalid file descriptor could not be sent via IPC, but + // OpDeliverReleaseFence message needs to be sent to child side. + if (!fenceFd->IsValid()) { + fenceFd = Nothing(); + } + mPendingAsyncMessage.push_back( + OpDeliverReleaseFence(fenceFd, aTexture->GetAndroidHardwareBuffer()->mId, + aTransactionId, /* usesImageBridge */ false)); + SendPendingAsyncMessages(); +#else + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); +#endif +} + +#if defined(OS_WIN) + +ImageBridgeParent::PluginTextureDatas::PluginTextureDatas( + UniquePtr<D3D11TextureData>&& aPluginTextureData, + UniquePtr<D3D11TextureData>&& aDisplayTextureData) + : mPluginTextureData(std::move(aPluginTextureData)), + mDisplayTextureData(std::move(aDisplayTextureData)) {} + +ImageBridgeParent::PluginTextureDatas::~PluginTextureDatas() {} + +#endif // defined(OS_WIN) + +mozilla::ipc::IPCResult ImageBridgeParent::RecvMakeAsyncPluginSurfaces( + SurfaceFormat aFormat, IntSize aSize, SurfaceDescriptorPlugin* aSD) { +#if defined(OS_WIN) + *aSD = SurfaceDescriptorPlugin(); + + RefPtr<ID3D11Device> d3dDevice = + DeviceManagerDx::Get()->GetCompositorDevice(); + if (!d3dDevice) { + NS_WARNING("Failed to get D3D11 device for plugin display"); + return IPC_OK(); + } + + auto pluginSurf = WrapUnique(D3D11TextureData::Create( + aSize, aFormat, ALLOC_FOR_OUT_OF_BAND_CONTENT, d3dDevice)); + if (!pluginSurf) { + NS_ERROR("Failed to create plugin surface"); + return IPC_OK(); + } + + auto dispSurf = WrapUnique(D3D11TextureData::Create( + aSize, aFormat, ALLOC_FOR_OUT_OF_BAND_CONTENT, d3dDevice)); + if (!dispSurf) { + NS_ERROR("Failed to create plugin display surface"); + return IPC_OK(); + } + + // Identify plugin surfaces with a simple non-zero 64-bit ID. + static uint64_t sPluginSurfaceId = 1; + + SurfaceDescriptor pluginSD, dispSD; + if ((!pluginSurf->Serialize(pluginSD)) || (!dispSurf->Serialize(dispSD))) { + NS_ERROR("Failed to make surface descriptors for plugin"); + return IPC_OK(); + } + + if (!mPluginTextureDatas.put( + sPluginSurfaceId, MakeUnique<PluginTextureDatas>( + std::move(pluginSurf), std::move(dispSurf)))) { + NS_ERROR("Failed to add plugin surfaces to map"); + return IPC_OK(); + } + + SurfaceDescriptorPlugin sd(sPluginSurfaceId, pluginSD, dispSD); + RefPtr<TextureHost> displayHost = CreateTextureHostD3D11( + dispSD, this, LayersBackend::LAYERS_NONE, TextureFlags::RECYCLE); + if (!displayHost) { + NS_ERROR("Failed to create plugin display texture host"); + return IPC_OK(); + } + + if (!mGPUVideoTextureHosts.put(sPluginSurfaceId, displayHost)) { + NS_ERROR("Failed to add plugin display texture host to map"); + return IPC_OK(); + } + + *aSD = sd; + ++sPluginSurfaceId; +#endif // defined(OS_WIN) + + return IPC_OK(); +} + +mozilla::ipc::IPCResult ImageBridgeParent::RecvUpdateAsyncPluginSurface( + const SurfaceDescriptorPlugin& aSD) { +#if defined(OS_WIN) + uint64_t surfaceId = aSD.id(); + auto itTextures = mPluginTextureDatas.lookup(surfaceId); + if (!itTextures) { + return IPC_OK(); + } + + auto& textures = itTextures->value(); + if (!textures->IsValid()) { + // The display texture may be gone. The plugin texture should never be gone + // here. + MOZ_ASSERT(textures->mPluginTextureData); + return IPC_OK(); + } + + RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetCompositorDevice(); + if (!device) { + NS_WARNING("Failed to get D3D11 device for plugin display"); + return IPC_OK(); + } + + RefPtr<ID3D11DeviceContext> context; + device->GetImmediateContext(getter_AddRefs(context)); + if (!context) { + NS_WARNING("Could not get an immediate D3D11 context"); + return IPC_OK(); + } + + RefPtr<IDXGIKeyedMutex> dispMutex; + HRESULT hr = textures->mDisplayTextureData->GetD3D11Texture()->QueryInterface( + __uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(dispMutex)); + if (FAILED(hr) || !dispMutex) { + NS_WARNING("Could not acquire plugin display IDXGIKeyedMutex"); + return IPC_OK(); + } + + RefPtr<IDXGIKeyedMutex> pluginMutex; + hr = textures->mPluginTextureData->GetD3D11Texture()->QueryInterface( + __uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(pluginMutex)); + if (FAILED(hr) || !pluginMutex) { + NS_WARNING("Could not acquire plugin offscreen IDXGIKeyedMutex"); + return IPC_OK(); + } + + { + AutoTextureLock lock1(dispMutex, hr); + if (hr == WAIT_ABANDONED || hr == WAIT_TIMEOUT || FAILED(hr)) { + NS_WARNING( + "Could not acquire DXGI surface lock - display forgot to release?"); + return IPC_OK(); + } + + AutoTextureLock lock2(pluginMutex, hr); + if (hr == WAIT_ABANDONED || hr == WAIT_TIMEOUT || FAILED(hr)) { + NS_WARNING( + "Could not acquire DXGI surface lock - plugin forgot to release?"); + return IPC_OK(); + } + + context->CopyResource(textures->mDisplayTextureData->GetD3D11Texture(), + textures->mPluginTextureData->GetD3D11Texture()); + } +#endif // defined(OS_WIN) + return IPC_OK(); +} + +mozilla::ipc::IPCResult ImageBridgeParent::RecvReadbackAsyncPluginSurface( + const SurfaceDescriptorPlugin& aSD, SurfaceDescriptor* aResult) { +#if defined(OS_WIN) + *aResult = null_t(); + + auto itTextures = mPluginTextureDatas.lookup(aSD.id()); + if (!itTextures) { + return IPC_OK(); + } + + auto& textures = itTextures->value(); + D3D11TextureData* displayTexData = textures->mDisplayTextureData.get(); + MOZ_RELEASE_ASSERT(displayTexData); + if ((!displayTexData) || (!displayTexData->GetD3D11Texture())) { + NS_WARNING("Error in plugin display texture"); + return IPC_OK(); + } + MOZ_ASSERT(displayTexData->GetSurfaceFormat() == SurfaceFormat::B8G8R8A8 || + displayTexData->GetSurfaceFormat() == SurfaceFormat::B8G8R8X8); + + RefPtr<ID3D11Device> device; + displayTexData->GetD3D11Texture()->GetDevice(getter_AddRefs(device)); + if (!device) { + NS_WARNING("Failed to get D3D11 device for plugin display"); + return IPC_OK(); + } + + UniquePtr<BufferTextureData> shmemTexData(BufferTextureData::Create( + displayTexData->GetSize(), displayTexData->GetSurfaceFormat(), + gfx::BackendType::SKIA, LayersBackend::LAYERS_NONE, + displayTexData->GetTextureFlags(), TextureAllocationFlags::ALLOC_DEFAULT, + this)); + if (!shmemTexData) { + NS_WARNING("Could not create BufferTextureData"); + return IPC_OK(); + } + + if (!gfx::Factory::ReadbackTexture(shmemTexData.get(), + displayTexData->GetD3D11Texture())) { + NS_WARNING("Failed to read plugin texture into Shmem"); + return IPC_OK(); + } + + // Take the Shmem from the TextureData. + shmemTexData->Serialize(*aResult); +#endif // defined(OS_WIN) + return IPC_OK(); +} + +mozilla::ipc::IPCResult ImageBridgeParent::RecvRemoveAsyncPluginSurface( + const SurfaceDescriptorPlugin& aSD, bool aIsFrontSurface) { +#if defined(OS_WIN) + auto itTextures = mPluginTextureDatas.lookup(aSD.id()); + if (!itTextures) { + return IPC_OK(); + } + + auto& textures = itTextures->value(); + if (aIsFrontSurface) { + textures->mDisplayTextureData = nullptr; + } else { + textures->mPluginTextureData = nullptr; + } + if ((!textures->mDisplayTextureData) && (!textures->mPluginTextureData)) { + mPluginTextureDatas.remove(aSD.id()); + } +#endif // defined(OS_WIN) + return IPC_OK(); +} + +#if defined(OS_WIN) +RefPtr<TextureHost> GetNullPluginTextureHost() { + class NullPluginTextureHost : public TextureHost { + public: + NullPluginTextureHost() : TextureHost(TextureFlags::NO_FLAGS) {} + + ~NullPluginTextureHost() {} + + gfx::SurfaceFormat GetFormat() const override { + return gfx::SurfaceFormat::UNKNOWN; + } + + already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override { + return nullptr; + } + + gfx::IntSize GetSize() const override { return gfx::IntSize(); } + + bool BindTextureSource(CompositableTextureSourceRef& aTexture) override { + return false; + } + + const char* Name() override { return "NullPluginTextureHost"; } + + virtual bool Lock() { return false; } + + void CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) override {} + + uint32_t NumSubTextures() override { return 0; } + + void PushResourceUpdates(wr::TransactionBuilder& aResources, + ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, + const wr::ExternalImageId& aExtID) override {} + + void PushDisplayItems(wr::DisplayListBuilder& aBuilder, + const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, + wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aImageKeys, + PushDisplayItemFlagSet aFlags) override {} + }; + + static StaticRefPtr<TextureHost> sNullPluginTextureHost; + if (!sNullPluginTextureHost) { + sNullPluginTextureHost = new NullPluginTextureHost(); + ClearOnShutdown(&sNullPluginTextureHost); + }; + + MOZ_ASSERT(sNullPluginTextureHost); + return sNullPluginTextureHost.get(); +} +#endif // defined(OS_WIN) + +RefPtr<TextureHost> ImageBridgeParent::LookupTextureHost( + const SurfaceDescriptorPlugin& aDescriptor) { +#if defined(OS_WIN) + auto it = mGPUVideoTextureHosts.lookup(aDescriptor.id()); + RefPtr<TextureHost> ret = it ? it->value() : nullptr; + return ret ? ret : GetNullPluginTextureHost(); +#else + MOZ_ASSERT_UNREACHABLE("Unsupported architecture."); + return nullptr; +#endif // defined(OS_WIN) +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h new file mode 100644 index 0000000000..08680cab39 --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeParent.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 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/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); + + public: + virtual ~ImageBridgeParent(); + + /** + * Creates the globals of ImageBridgeParent. + */ + static void Setup(); + + static ImageBridgeParent* CreateSameProcess(); + static bool CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint); + static bool CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint); + 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; + + static void NotifyBufferNotUsedOfCompositorBridge( + base::ProcessId aChildProcessId, TextureHost* aTexture, + uint64_t aTransactionId); + + void NotifyBufferNotUsedOfCompositorBridge(TextureHost* aTexture, + uint64_t aTransactionId); + + base::ProcessId GetChildProcessId() override { return OtherPid(); } + + // PImageBridge + mozilla::ipc::IPCResult RecvUpdate(EditArray&& aEdits, + OpDestroyArray&& aToDestroy, + const uint64_t& aFwdTransactionId); + + PTextureParent* AllocPTextureParent( + const SurfaceDescriptor& aSharedData, const 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, + const LayersBackend& aLayersBackend); + 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::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) override; + + bool AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + 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; } + + // See PluginInstanceParent for details on the Windows async plugin + // rendering protocol. + mozilla::ipc::IPCResult RecvMakeAsyncPluginSurfaces( + SurfaceFormat aFormat, IntSize aSize, SurfaceDescriptorPlugin* aSD); + mozilla::ipc::IPCResult RecvUpdateAsyncPluginSurface( + const SurfaceDescriptorPlugin& aSD); + mozilla::ipc::IPCResult RecvReadbackAsyncPluginSurface( + const SurfaceDescriptorPlugin& aSD, SurfaceDescriptor* aResult); + mozilla::ipc::IPCResult RecvRemoveAsyncPluginSurface( + const SurfaceDescriptorPlugin& aSD, bool aIsFrontSurface); + + RefPtr<TextureHost> LookupTextureHost( + const SurfaceDescriptorPlugin& aDescriptor); + + protected: + void Bind(Endpoint<PImageBridgeParent>&& aEndpoint); + + private: + static void ShutdownInternal(); + + void DeferredDestroy(); + nsCOMPtr<nsISerialEventTarget> mThread; + // This keeps us alive until ActorDestroy(), at which point we do a + // deferred destruction of ourselves. + RefPtr<ImageBridgeParent> mSelfRef; + + bool mClosed; + + /** + * Map of all living ImageBridgeParent instances + */ + typedef std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeMap; + static ImageBridgeMap sImageBridges; + + RefPtr<CompositorThreadHolder> mCompositorThreadHolder; + +#if defined(OS_WIN) + // Owns a pair of textures used to double-buffer a plugin async rendering + // instance. + struct PluginTextureDatas { + UniquePtr<D3D11TextureData> mPluginTextureData; + UniquePtr<D3D11TextureData> mDisplayTextureData; + + PluginTextureDatas(UniquePtr<D3D11TextureData>&& aPluginTextureData, + UniquePtr<D3D11TextureData>&& aDisplayTextureData); + + ~PluginTextureDatas(); + + PluginTextureDatas(const PluginTextureDatas& o) = delete; + PluginTextureDatas& operator=(const PluginTextureDatas& o) = delete; + + bool IsValid() { return mPluginTextureData && mDisplayTextureData; } + }; + + HashMap<WindowsHandle, RefPtr<TextureHost>> mGPUVideoTextureHosts; + HashMap<WindowsHandle, UniquePtr<PluginTextureDatas>> mPluginTextureDatas; +#endif // defined(OS_WIN) +}; + +} // namespace layers +} // namespace mozilla + +#endif // gfx_layers_ipc_ImageBridgeParent_h_ diff --git a/gfx/layers/ipc/KnowsCompositor.h b/gfx/layers/ipc/KnowsCompositor.h new file mode 100644 index 0000000000..68fd77eda1 --- /dev/null +++ b/gfx/layers/ipc/KnowsCompositor.h @@ -0,0 +1,237 @@ +/* -*- 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; + } + + 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 SupportsTextureDirectMapping() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mSupportsTextureDirectMapping; + } + + bool SupportsD3D11() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier.mParentBackend == + layers::LayersBackend::LAYERS_D3D11 || + (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; + } + + 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; + } + + TextureFactoryIdentifier GetTextureFactoryIdentifier() const { + auto lock = mData.Lock(); + return lock.ref().mTextureFactoryIdentifier; + } + + bool DeviceCanReset() const { + return GetCompositorBackendType() != LayersBackend::LAYERS_BASIC; + } + + 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/LayerAnimationUtils.cpp b/gfx/layers/ipc/LayerAnimationUtils.cpp new file mode 100644 index 0000000000..2902122ad3 --- /dev/null +++ b/gfx/layers/ipc/LayerAnimationUtils.cpp @@ -0,0 +1,40 @@ +/* -*- 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 "LayerAnimationUtils.h" +#include "mozilla/ComputedTimingFunction.h" // For ComputedTimingFunction +#include "mozilla/layers/LayersMessages.h" // For TimingFunction etc. +#include "nsTimingFunction.h" + +namespace mozilla { +namespace layers { + +/* static */ +Maybe<ComputedTimingFunction> +AnimationUtils::TimingFunctionToComputedTimingFunction( + const TimingFunction& aTimingFunction) { + switch (aTimingFunction.type()) { + case TimingFunction::Tnull_t: + return Nothing(); + case TimingFunction::TCubicBezierFunction: { + CubicBezierFunction cbf = aTimingFunction.get_CubicBezierFunction(); + return Some(ComputedTimingFunction::CubicBezier(cbf.x1(), cbf.y1(), + cbf.x2(), cbf.y2())); + } + case TimingFunction::TStepFunction: { + StepFunction sf = aTimingFunction.get_StepFunction(); + StyleStepPosition pos = static_cast<StyleStepPosition>(sf.type()); + return Some(ComputedTimingFunction::Steps(sf.steps(), pos)); + } + default: + MOZ_ASSERT_UNREACHABLE("Function must be null, bezier, step or frames"); + break; + } + return Nothing(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/LayerAnimationUtils.h b/gfx/layers/ipc/LayerAnimationUtils.h new file mode 100644 index 0000000000..0a17e51b70 --- /dev/null +++ b/gfx/layers/ipc/LayerAnimationUtils.h @@ -0,0 +1,29 @@ +/* -*- 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_LayerAnimationUtils_h +#define mozilla_layers_LayerAnimationUtils_h + +#include "mozilla/Maybe.h" + +namespace mozilla { + +class ComputedTimingFunction; + +namespace layers { + +class TimingFunction; + +class AnimationUtils { + public: + static Maybe<ComputedTimingFunction> TimingFunctionToComputedTimingFunction( + const TimingFunction& aTimingFunction); +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_LayerAnimationUtils_h diff --git a/gfx/layers/ipc/LayerTransactionChild.cpp b/gfx/layers/ipc/LayerTransactionChild.cpp new file mode 100644 index 0000000000..4dbd40e089 --- /dev/null +++ b/gfx/layers/ipc/LayerTransactionChild.cpp @@ -0,0 +1,37 @@ +/* -*- 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 "LayerTransactionChild.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "nsTArray.h" // for nsTArray +#include "mozilla/layers/TextureClient.h" + +namespace mozilla { +namespace layers { + +void LayerTransactionChild::Destroy() { + if (!IPCOpen()) { + return; + } + // mDestroyed is used to prevent calling Send__delete__() twice. + // When this function is called from CompositorBridgeChild::Destroy(), + // under Send__delete__() call, this function is called from + // ShadowLayerForwarder's destructor. + // When it happens, IPCOpen() is still true. + // See bug 1004191. + mDestroyed = true; + + SendShutdown(); +} + +void LayerTransactionChild::ActorDestroy(ActorDestroyReason why) { + mDestroyed = true; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/LayerTransactionChild.h b/gfx/layers/ipc/LayerTransactionChild.h new file mode 100644 index 0000000000..83ce36b022 --- /dev/null +++ b/gfx/layers/ipc/LayerTransactionChild.h @@ -0,0 +1,74 @@ +/* -*- 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_LAYERTRANSACTIONCHILD_H +#define MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H + +#include <stdint.h> // for uint32_t +#include "mozilla/Attributes.h" // for override +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/layers/PLayerTransactionChild.h" +#include "mozilla/RefPtr.h" + +namespace mozilla { + +namespace layers { + +class ShadowLayerForwarder; + +class LayerTransactionChild : public PLayerTransactionChild { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LayerTransactionChild) + /** + * Clean this up, finishing with SendShutDown() which will cause __delete__ + * to be sent from the parent side. + * + * It is expected (checked with an assert) that all shadow layers + * created by this have already been destroyed and + * Send__delete__()d by the time this method is called. + */ + void Destroy(); + + bool IPCOpen() const { return mIPCOpen && !mDestroyed; } + bool IsDestroyed() const { return mDestroyed; } + + void SetForwarder(ShadowLayerForwarder* aForwarder) { + mForwarder = aForwarder; + } + + LayersId GetId() const { return mId; } + + void MarkDestroyed() { mDestroyed = true; } + + protected: + explicit LayerTransactionChild(const LayersId& aId) + : mForwarder(nullptr), mIPCOpen(false), mDestroyed(false), mId(aId) {} + virtual ~LayerTransactionChild() = default; + + void ActorDestroy(ActorDestroyReason why) override; + + void AddIPDLReference() { + MOZ_ASSERT(mIPCOpen == false); + mIPCOpen = true; + AddRef(); + } + void ReleaseIPDLReference() { + MOZ_ASSERT(mIPCOpen == true); + mIPCOpen = false; + Release(); + } + friend class CompositorBridgeChild; + + ShadowLayerForwarder* mForwarder; + bool mIPCOpen; + bool mDestroyed; + LayersId mId; +}; + +} // namespace layers +} // namespace mozilla + +#endif // MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp new file mode 100644 index 0000000000..e7dc07f1d9 --- /dev/null +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -0,0 +1,1020 @@ +/* -*- 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 "LayerTransactionParent.h" +#include <vector> // for vector +#include "CompositableHost.h" // for CompositableParent, Get, etc +#include "ImageLayers.h" // for ImageLayer +#include "Layers.h" // for Layer, ContainerLayer, etc +#include "CompositableTransactionParent.h" // for EditReplyVector +#include "CompositorBridgeParent.h" +#include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D +#include "mozilla/layers/AnimationHelper.h" // for GetAnimatedPropValue +#include "mozilla/layers/CanvasLayerComposite.h" +#include "mozilla/layers/ColorLayerComposite.h" +#include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorAnimationStorage.h" // for CompositorAnimationStorage +#include "mozilla/layers/ContainerLayerComposite.h" +#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent +#include "mozilla/layers/ImageLayerComposite.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayersMessages.h" // for EditReply, etc +#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG +#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL +#include "mozilla/layers/PaintedLayerComposite.h" +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "mozilla/PerfStats.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/Telemetry.h" +#include "mozilla/Unused.h" +#include "nsCoord.h" // for NSAppUnitsToFloatPixels +#include "nsISupportsImpl.h" // for Layer::Release, etc +#include "nsLayoutUtils.h" // for nsLayoutUtils +#include "nsMathUtils.h" // for NS_round +#include "nsPoint.h" // for nsPoint +#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc +#include "TreeTraversal.h" // for ForEachNode +#include "GeckoProfiler.h" +#include "mozilla/layers/TextureHost.h" +#include "mozilla/layers/AsyncCompositionManager.h" + +using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON; + +namespace mozilla { +namespace layers { + +//-------------------------------------------------- +// LayerTransactionParent +LayerTransactionParent::LayerTransactionParent( + HostLayerManager* aManager, CompositorBridgeParentBase* aBridge, + CompositorAnimationStorage* aAnimStorage, LayersId aId, + TimeDuration aVsyncRate) + : mLayerManager(aManager), + mCompositorBridge(aBridge), + mAnimStorage(aAnimStorage), + mId(aId), + mChildEpoch{0}, + mParentEpoch{0}, + mVsyncRate(aVsyncRate), + mDestroyed(false), + mIPCOpen(false), + mUpdateHitTestingTree(false) { + MOZ_ASSERT(mId.IsValid()); +} + +LayerTransactionParent::~LayerTransactionParent() = default; + +void LayerTransactionParent::SetLayerManager( + HostLayerManager* aLayerManager, CompositorAnimationStorage* aAnimStorage) { + if (mDestroyed) { + return; + } + mLayerManager = aLayerManager; + for (auto iter = mLayerMap.Iter(); !iter.Done(); iter.Next()) { + auto layer = iter.Data(); + if (mAnimStorage && layer->GetCompositorAnimationsId()) { + mAnimStorage->ClearById(layer->GetCompositorAnimationsId()); + } + layer->AsHostLayer()->SetLayerManager(aLayerManager); + } + mAnimStorage = aAnimStorage; +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvShutdown() { + Destroy(); + IProtocol* mgr = Manager(); + if (!Send__delete__(this)) { + return IPC_FAIL_NO_REASON(mgr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvShutdownSync() { + return RecvShutdown(); +} + +void LayerTransactionParent::Destroy() { + if (mDestroyed) { + return; + } + mDestroyed = true; + if (mAnimStorage) { + for (auto iter = mLayerMap.Iter(); !iter.Done(); iter.Next()) { + auto layer = iter.Data(); + if (layer->GetCompositorAnimationsId()) { + mAnimStorage->ClearById(layer->GetCompositorAnimationsId()); + } + layer->Disconnect(); + } + } + mCompositables.clear(); + mAnimStorage = nullptr; +} + +class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender final { + public: + explicit AutoLayerTransactionParentAsyncMessageSender( + LayerTransactionParent* aLayerTransaction, + const nsTArray<OpDestroy>* aDestroyActors = nullptr) + : mLayerTransaction(aLayerTransaction), mActorsToDestroy(aDestroyActors) { + mLayerTransaction->SetAboutToSendAsyncMessages(); + } + + ~AutoLayerTransactionParentAsyncMessageSender() { + mLayerTransaction->SendPendingAsyncMessages(); + if (mActorsToDestroy) { + // Destroy the actors after sending the async messages because the latter + // may contain references to some actors. + for (const auto& op : *mActorsToDestroy) { + mLayerTransaction->DestroyActor(op); + } + } + } + + private: + LayerTransactionParent* mLayerTransaction; + const nsTArray<OpDestroy>* mActorsToDestroy; +}; + +mozilla::ipc::IPCResult LayerTransactionParent::RecvPaintTime( + const TransactionId& aTransactionId, const TimeDuration& aPaintTime) { + mCompositorBridge->UpdatePaintTime(this, aPaintTime); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvUpdate( + const TransactionInfo& aInfo) { + AUTO_PROFILER_TRACING_MARKER("Paint", "LayerTransaction", GRAPHICS); + AUTO_PROFILER_LABEL("LayerTransactionParent::RecvUpdate", GRAPHICS); + PerfStats::AutoMetricRecording<PerfStats::Metric::LayerTransactions> + autoRecording; + + TimeStamp updateStart = TimeStamp::Now(); + + MOZ_LAYERS_LOG( + ("[ParentSide] received txn with %zu edits", aInfo.cset().Length())); + + UpdateFwdTransactionId(aInfo.fwdTransactionId()); + + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + for (const auto& op : aInfo.toDestroy()) { + DestroyActor(op); + } + return IPC_OK(); + } + + // This ensures that destroy operations are always processed. It is not safe + // to early-return from RecvUpdate without doing so. + AutoLayerTransactionParentAsyncMessageSender autoAsyncMessageSender( + this, &aInfo.toDestroy()); + + { + AutoResolveRefLayers resolve( + mCompositorBridge->GetCompositionManager(this)); + nsCString none; + mLayerManager->BeginTransaction(none); + } + + // Not all edits require an update to the hit testing tree. + mUpdateHitTestingTree = false; + + for (EditArray::index_type i = 0; i < aInfo.cset().Length(); ++i) { + const Edit& edit = const_cast<Edit&>(aInfo.cset()[i]); + + switch (edit.type()) { + // Create* ops + case Edit::TOpCreatePaintedLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreatePaintedLayer")); + + RefPtr<PaintedLayer> layer = mLayerManager->CreatePaintedLayer(); + if (!BindLayer(layer, edit.get_OpCreatePaintedLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreatePaintedLayer"); + break; + } + case Edit::TOpCreateContainerLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer")); + + RefPtr<ContainerLayer> layer = mLayerManager->CreateContainerLayer(); + if (!BindLayer(layer, edit.get_OpCreateContainerLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateContainerLayer"); + break; + } + case Edit::TOpCreateImageLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer")); + + RefPtr<ImageLayer> layer = mLayerManager->CreateImageLayer(); + if (!BindLayer(layer, edit.get_OpCreateImageLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateImageLayer"); + break; + } + case Edit::TOpCreateColorLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer")); + + RefPtr<ColorLayer> layer = mLayerManager->CreateColorLayer(); + if (!BindLayer(layer, edit.get_OpCreateColorLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateColorLayer"); + break; + } + case Edit::TOpCreateCanvasLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer")); + + RefPtr<CanvasLayer> layer = mLayerManager->CreateCanvasLayer(); + if (!BindLayer(layer, edit.get_OpCreateCanvasLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateCanvasLayer"); + break; + } + case Edit::TOpCreateRefLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer")); + + RefPtr<RefLayer> layer = mLayerManager->CreateRefLayer(); + if (!BindLayer(layer, edit.get_OpCreateRefLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateRefLayer"); + break; + } + case Edit::TOpSetDiagnosticTypes: { + mLayerManager->SetDiagnosticTypes( + edit.get_OpSetDiagnosticTypes().diagnostics()); + break; + } + // Tree ops + case Edit::TOpSetRoot: { + MOZ_LAYERS_LOG(("[ParentSide] SetRoot")); + + Layer* newRoot = AsLayer(edit.get_OpSetRoot().root()); + if (!newRoot) { + return IPC_FAIL_NO_REASON(this); + } + if (newRoot->GetParent()) { + // newRoot is not a root! + return IPC_FAIL_NO_REASON(this); + } + mRoot = newRoot; + + UpdateHitTestingTree(mRoot, "SetRoot"); + break; + } + case Edit::TOpInsertAfter: { + MOZ_LAYERS_LOG(("[ParentSide] InsertAfter")); + + const OpInsertAfter& oia = edit.get_OpInsertAfter(); + Layer* child = AsLayer(oia.childLayer()); + Layer* layer = AsLayer(oia.container()); + Layer* after = AsLayer(oia.after()); + if (!child || !layer || !after) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->InsertAfter(child, after)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "InsertAfter"); + break; + } + case Edit::TOpPrependChild: { + MOZ_LAYERS_LOG(("[ParentSide] PrependChild")); + + const OpPrependChild& oac = edit.get_OpPrependChild(); + Layer* child = AsLayer(oac.childLayer()); + Layer* layer = AsLayer(oac.container()); + if (!child || !layer) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->InsertAfter(child, nullptr)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "PrependChild"); + break; + } + case Edit::TOpRemoveChild: { + MOZ_LAYERS_LOG(("[ParentSide] RemoveChild")); + + const OpRemoveChild& orc = edit.get_OpRemoveChild(); + Layer* childLayer = AsLayer(orc.childLayer()); + Layer* layer = AsLayer(orc.container()); + if (!childLayer || !layer) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->RemoveChild(childLayer)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "RemoveChild"); + break; + } + case Edit::TOpRepositionChild: { + MOZ_LAYERS_LOG(("[ParentSide] RepositionChild")); + + const OpRepositionChild& orc = edit.get_OpRepositionChild(); + Layer* child = AsLayer(orc.childLayer()); + Layer* after = AsLayer(orc.after()); + Layer* layer = AsLayer(orc.container()); + if (!child || !layer || !after) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->RepositionChild(child, after)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "RepositionChild"); + break; + } + case Edit::TOpRaiseToTopChild: { + MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild")); + + const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild(); + Layer* child = AsLayer(rtc.childLayer()); + if (!child) { + return IPC_FAIL_NO_REASON(this); + } + Layer* layer = AsLayer(rtc.container()); + if (!layer) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->RepositionChild(child, nullptr)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "RaiseToTopChild"); + break; + } + case Edit::TCompositableOperation: { + if (!ReceiveCompositableUpdate(edit.get_CompositableOperation())) { + return IPC_FAIL_NO_REASON(this); + } + break; + } + case Edit::TOpAttachCompositable: { + const OpAttachCompositable& op = edit.get_OpAttachCompositable(); + RefPtr<CompositableHost> host = FindCompositable(op.compositable()); + if (!Attach(AsLayer(op.layer()), host, false)) { + return IPC_FAIL_NO_REASON(this); + } + host->SetCompositorBridgeID(mLayerManager->GetCompositorBridgeID()); + break; + } + case Edit::TOpAttachAsyncCompositable: { + const OpAttachAsyncCompositable& op = + edit.get_OpAttachAsyncCompositable(); + RefPtr<ImageBridgeParent> imageBridge = + ImageBridgeParent::GetInstance(OtherPid()); + if (!imageBridge) { + return IPC_FAIL_NO_REASON(this); + } + RefPtr<CompositableHost> host = imageBridge->FindCompositable( + op.compositable(), /* aAllowDisablingWebRender */ true); + if (!host) { + // This normally should not happen, but can after a GPU process crash. + // Media may not have had time to update the ImageContainer associated + // with a video frame, and we may try to attach a stale + // CompositableHandle. Rather than break the whole transaction, we + // just continue. + gfxCriticalNote << "CompositableHost " << op.compositable().Value() + << " not found"; + continue; + } + if (!Attach(AsLayer(op.layer()), host, true)) { + return IPC_FAIL_NO_REASON(this); + } + host->SetCompositorBridgeID(mLayerManager->GetCompositorBridgeID()); + break; + } + default: + MOZ_CRASH("not reached"); + } + } + + // Process simple attribute updates. + for (const auto& op : aInfo.setSimpleAttrs()) { + MOZ_LAYERS_LOG(("[ParentSide] SetSimpleLayerAttributes")); + Layer* layer = AsLayer(op.layer()); + if (!layer) { + return IPC_FAIL_NO_REASON(this); + } + const SimpleLayerAttributes& attrs = op.attrs(); + const SimpleLayerAttributes& orig = layer->GetSimpleAttributes(); + if (!attrs.HitTestingInfoIsEqual(orig)) { + UpdateHitTestingTree(layer, "scrolling info changed"); + } + layer->SetSimpleAttributes(op.attrs()); + } + + // Process attribute updates. + for (const auto& op : aInfo.setAttrs()) { + MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes")); + if (!SetLayerAttributes(op)) { + return IPC_FAIL_NO_REASON(this); + } + } + + // Process paints separately, after all normal edits. + for (const auto& op : aInfo.paints()) { + if (!ReceiveCompositableUpdate(op)) { + return IPC_FAIL_NO_REASON(this); + } + } + + mCompositorBridge->ShadowLayersUpdated(this, aInfo, mUpdateHitTestingTree); + + { + AutoResolveRefLayers resolve( + mCompositorBridge->GetCompositionManager(this)); + mLayerManager->EndTransaction(TimeStamp(), + LayerManager::END_NO_IMMEDIATE_REDRAW); + } + + if (!IsSameProcess()) { + // Ensure that any pending operations involving back and front + // buffers have completed, so that neither process stomps on the + // other's buffer contents. + LayerManagerComposite::PlatformSyncBeforeReplyUpdate(); + } + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + int compositeTime = + (int)(mozilla::TimeStamp::Now() - updateStart).ToMilliseconds(); + if (compositeTime > 15) { + printf_stderr("Compositor: Layers update took %i ms (blocking gecko).\n", + compositeTime); + } +#endif + + // Enable visual warning for long transaction when draw FPS option is enabled + bool drawFps = StaticPrefs::layers_acceleration_draw_fps(); + if (drawFps) { + uint32_t visualWarningTrigger = + StaticPrefs::layers_transaction_warning_ms(); + // The default theshold is 200ms to trigger, hit red when it take 4 times + // longer + TimeDuration latency = TimeStamp::Now() - aInfo.transactionStart(); + if (latency > TimeDuration::FromMilliseconds(visualWarningTrigger)) { + float severity = + (latency - TimeDuration::FromMilliseconds(visualWarningTrigger)) + .ToMilliseconds() / + (4 * visualWarningTrigger); + if (severity > 1.f) { + severity = 1.f; + } + mLayerManager->VisualFrameWarning(severity); + printf_stderr( + "LayerTransactionParent::RecvUpdate transaction from process %d took " + "%f ms", + OtherPid(), latency.ToMilliseconds()); + } + + mLayerManager->RecordUpdateTime( + (TimeStamp::Now() - updateStart).ToMilliseconds()); + } + + return IPC_OK(); +} + +bool LayerTransactionParent::SetLayerAttributes( + const OpSetLayerAttributes& aOp) { + Layer* layer = AsLayer(aOp.layer()); + if (!layer) { + return false; + } + + const LayerAttributes& attrs = aOp.attrs(); + const CommonLayerAttributes& common = attrs.common(); + if (common.visibleRegion() != layer->GetVisibleRegion()) { + UpdateHitTestingTree(layer, "visible region changed"); + layer->SetVisibleRegion(common.visibleRegion()); + } + if (common.eventRegions() != layer->GetEventRegions()) { + UpdateHitTestingTree(layer, "event regions changed"); + layer->SetEventRegions(common.eventRegions()); + } + Maybe<ParentLayerIntRect> clipRect = + common.useClipRect() ? Some(common.clipRect()) : Nothing(); + if (clipRect != layer->GetClipRect()) { + UpdateHitTestingTree(layer, "clip rect changed"); + layer->SetClipRect(clipRect); + } + if (LayerHandle maskLayer = common.maskLayer()) { + layer->SetMaskLayer(AsLayer(maskLayer)); + } else { + layer->SetMaskLayer(nullptr); + } + layer->SetCompositorAnimations(mId, common.compositorAnimations()); + // Clean up the Animations by id in the CompositorAnimationStorage + // if there are no active animations on the layer + if (mAnimStorage && layer->GetCompositorAnimationsId() && + layer->GetPropertyAnimationGroups().IsEmpty()) { + mAnimStorage->ClearById(layer->GetCompositorAnimationsId()); + } + if (common.scrollMetadata() != layer->GetAllScrollMetadata()) { + UpdateHitTestingTree(layer, "scroll metadata changed"); + layer->SetScrollMetadata(common.scrollMetadata()); + } + layer->SetDisplayListLog(common.displayListLog().get()); + + // The updated invalid region is added to the existing one, since we can + // update multiple times before the next composite. + layer->AddInvalidRegion(common.invalidRegion()); + + nsTArray<RefPtr<Layer>> maskLayers; + for (size_t i = 0; i < common.ancestorMaskLayers().Length(); i++) { + Layer* maskLayer = AsLayer(common.ancestorMaskLayers().ElementAt(i)); + if (!maskLayer) { + return false; + } + maskLayers.AppendElement(maskLayer); + } + layer->SetAncestorMaskLayers(maskLayers); + + typedef SpecificLayerAttributes Specific; + const SpecificLayerAttributes& specific = attrs.specific(); + switch (specific.type()) { + case Specific::Tnull_t: + break; + + case Specific::TPaintedLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] painted layer")); + + PaintedLayer* paintedLayer = layer->AsPaintedLayer(); + if (!paintedLayer) { + return false; + } + const PaintedLayerAttributes& attrs = + specific.get_PaintedLayerAttributes(); + + paintedLayer->SetValidRegion(attrs.validRegion()); + break; + } + case Specific::TContainerLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] container layer")); + + ContainerLayer* containerLayer = layer->AsContainerLayer(); + if (!containerLayer) { + return false; + } + const ContainerLayerAttributes& attrs = + specific.get_ContainerLayerAttributes(); + containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale()); + containerLayer->SetInheritedScale(attrs.inheritedXScale(), + attrs.inheritedYScale()); + containerLayer->SetScaleToResolution(attrs.presShellResolution()); + break; + } + case Specific::TColorLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] color layer")); + + ColorLayer* colorLayer = layer->AsColorLayer(); + if (!colorLayer) { + return false; + } + colorLayer->SetColor(specific.get_ColorLayerAttributes().color().value()); + colorLayer->SetBounds(specific.get_ColorLayerAttributes().bounds()); + break; + } + case Specific::TCanvasLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] canvas layer")); + + CanvasLayer* canvasLayer = layer->AsCanvasLayer(); + if (!canvasLayer) { + return false; + } + canvasLayer->SetSamplingFilter( + specific.get_CanvasLayerAttributes().samplingFilter()); + canvasLayer->SetBounds(specific.get_CanvasLayerAttributes().bounds()); + break; + } + case Specific::TRefLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] ref layer")); + + RefLayer* refLayer = layer->AsRefLayer(); + if (!refLayer) { + return false; + } + refLayer->SetReferentId(specific.get_RefLayerAttributes().id()); + refLayer->SetEventRegionsOverride( + specific.get_RefLayerAttributes().eventRegionsOverride()); + refLayer->SetRemoteDocumentSize( + specific.get_RefLayerAttributes().remoteDocumentSize()); + UpdateHitTestingTree(layer, "ref layer attributes changed"); + break; + } + case Specific::TImageLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] image layer")); + + ImageLayer* imageLayer = layer->AsImageLayer(); + if (!imageLayer) { + return false; + } + const ImageLayerAttributes& attrs = specific.get_ImageLayerAttributes(); + imageLayer->SetSamplingFilter(attrs.samplingFilter()); + imageLayer->SetScaleToSize(attrs.scaleToSize(), attrs.scaleMode()); + break; + } + default: + MOZ_CRASH("not reached"); + } + + return true; +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetLayersObserverEpoch( + const LayersObserverEpoch& aChildEpoch) { + mChildEpoch = aChildEpoch; + return IPC_OK(); +} + +bool LayerTransactionParent::ShouldParentObserveEpoch() { + if (mParentEpoch == mChildEpoch) { + return false; + } + + mParentEpoch = mChildEpoch; + return true; +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetTestSampleTime( + const TimeStamp& aTime) { + if (!mCompositorBridge->SetTestSampleTime(GetId(), aTime)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvLeaveTestMode() { + if (mDestroyed) { + return IPC_FAIL_NO_REASON(this); + } + + mCompositorBridge->LeaveTestMode(GetId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetAnimationValue( + const uint64_t& aCompositorAnimationsId, OMTAValue* aValue) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + return IPC_FAIL_NO_REASON(this); + } + + // Make sure we apply the latest animation style or else we can end up with + // a race between when we temporarily clear the animation transform (in + // CompositorBridgeParent::SetShadowProperties) and when animation + // recalculates the value. + mCompositorBridge->ApplyAsyncProperties( + this, CompositorBridgeParentBase::TransformsToSkip::APZ); + + if (!mAnimStorage) { + return IPC_FAIL_NO_REASON(this); + } + + *aValue = mAnimStorage->GetOMTAValue(aCompositorAnimationsId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetTransform( + const LayerHandle& aLayerHandle, Maybe<Matrix4x4>* aTransform) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + return IPC_FAIL_NO_REASON(this); + } + + Layer* layer = AsLayer(aLayerHandle); + if (!layer) { + return IPC_FAIL_NO_REASON(this); + } + + mCompositorBridge->ApplyAsyncProperties( + this, CompositorBridgeParentBase::TransformsToSkip::NoneOfThem); + + Matrix4x4 transform = layer->AsHostLayer()->GetShadowBaseTransform(); + // Undo the scale transform applied by FrameTransformToTransformInDevice in + // AsyncCompositionManager.cpp. + if (ContainerLayer* c = layer->AsContainerLayer()) { + transform.PostScale(1.0f / c->GetInheritedXScale(), + 1.0f / c->GetInheritedYScale(), 1.0f); + } + float scale = 1; + Point3D scaledOrigin; + if (layer->GetTransformData()) { + const TransformData& data = *layer->GetTransformData(); + scale = data.appUnitsPerDevPixel(); + scaledOrigin = Point3D( + NS_round(NSAppUnitsToFloatPixels(data.origin().x, scale)), + NS_round(NSAppUnitsToFloatPixels(data.origin().y, scale)), 0.0f); + } + + // If our parent isn't a perspective layer, then the offset into reference + // frame coordinates will have been applied to us. Add an inverse translation + // to cancel it out. + if (!layer->GetParent() || !layer->GetParent()->GetTransformIsPerspective()) { + transform.PostTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z); + } + + *aTransform = Some(transform); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetAsyncScrollOffset( + const ScrollableLayerGuid::ViewID& aScrollID, const float& aX, + const float& aY) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + return IPC_FAIL_NO_REASON(this); + } + + mCompositorBridge->SetTestAsyncScrollOffset(GetId(), aScrollID, + CSSPoint(aX, aY)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetAsyncZoom( + const ScrollableLayerGuid::ViewID& aScrollID, const float& aValue) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + return IPC_FAIL_NO_REASON(this); + } + + mCompositorBridge->SetTestAsyncZoom(GetId(), aScrollID, + LayerToParentLayerScale(aValue)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvFlushApzRepaints() { + mCompositorBridge->FlushApzRepaints(GetId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetAPZTestData( + APZTestData* aOutData) { + mCompositorBridge->GetAPZTestData(GetId(), aOutData); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetFrameUniformity( + FrameUniformityData* aOutData) { + mCompositorBridge->GetFrameUniformity(GetId(), aOutData); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvRequestProperty( + const nsString& aProperty, float* aValue) { + *aValue = -1; + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetConfirmedTargetAPZC( + const uint64_t& aBlockId, nsTArray<ScrollableLayerGuid>&& aTargets) { + for (size_t i = 0; i < aTargets.Length(); i++) { + // Guard against bad data from hijacked child processes + if (aTargets[i].mLayersId != GetId()) { + NS_ERROR( + "Unexpected layers id in RecvSetConfirmedTargetAPZC; dropping " + "message..."); + return IPC_FAIL(this, "Bad layers id"); + } + } + mCompositorBridge->SetConfirmedTargetAPZC(GetId(), aBlockId, + std::move(aTargets)); + return IPC_OK(); +} + +bool LayerTransactionParent::Attach(Layer* aLayer, + CompositableHost* aCompositable, + bool aIsAsync) { + if (!aCompositable || !aLayer) { + return false; + } + + HostLayer* layer = aLayer->AsHostLayer(); + if (!layer) { + return false; + } + + TextureSourceProvider* provider = + static_cast<HostLayerManager*>(aLayer->Manager()) + ->GetTextureSourceProvider(); + + MOZ_ASSERT(!aCompositable->AsWebRenderImageHost()); + if (aCompositable->AsWebRenderImageHost()) { + gfxCriticalNote << "Use WebRenderImageHost at LayerTransactionParent."; + } + if (!layer->SetCompositableHost(aCompositable)) { + // not all layer types accept a compositable, see bug 967824 + return false; + } + aCompositable->Attach(aLayer, provider, + aIsAsync ? CompositableHost::ALLOW_REATTACH | + CompositableHost::KEEP_ATTACHED + : CompositableHost::NO_FLAGS); + return true; +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvClearCachedResources() { + if (mRoot) { + // NB: |mRoot| here is the *child* context's root. In this parent + // context, it's just a subtree root. We need to scope the clear + // of resources to exactly that subtree, so we specify it here. + mLayerManager->ClearCachedResources(mRoot); + } + mCompositorBridge->NotifyClearCachedResources(this); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvScheduleComposite() { + mCompositorBridge->ScheduleComposite(this); + return IPC_OK(); +} + +void LayerTransactionParent::ActorDestroy(ActorDestroyReason why) { Destroy(); } + +bool LayerTransactionParent::AllocShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (!mIPCOpen || mDestroyed) { + return false; + } + return PLayerTransactionParent::AllocShmem(aSize, aType, aShmem); +} + +bool LayerTransactionParent::AllocUnsafeShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (!mIPCOpen || mDestroyed) { + return false; + } + + return PLayerTransactionParent::AllocUnsafeShmem(aSize, aType, aShmem); +} + +bool LayerTransactionParent::DeallocShmem(ipc::Shmem& aShmem) { + if (!mIPCOpen || mDestroyed) { + return false; + } + return PLayerTransactionParent::DeallocShmem(aShmem); +} + +bool LayerTransactionParent::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void LayerTransactionParent::SetPendingTransactionId( + TransactionId aId, const VsyncId& aVsyncId, + const TimeStamp& aVsyncStartTime, const TimeStamp& aRefreshStartTime, + const TimeStamp& aTxnStartTime, const TimeStamp& aTxnEndTime, + bool aContainsSVG, const nsCString& aURL, const TimeStamp& aFwdTime) { + mPendingTransactions.AppendElement(PendingTransaction{ + aId, aVsyncId, aVsyncStartTime, aRefreshStartTime, aTxnStartTime, + aTxnEndTime, aFwdTime, aURL, aContainsSVG}); +} + +TransactionId LayerTransactionParent::FlushTransactionId( + const VsyncId& aCompositeId, TimeStamp& aCompositeEnd) { + TransactionId id; + for (auto& transaction : mPendingTransactions) { + id = transaction.mId; + if (mId.IsValid() && transaction.mId.IsValid() && !mVsyncRate.IsZero()) { + RecordContentFrameTime( + transaction.mTxnVsyncId, transaction.mVsyncStartTime, + transaction.mTxnStartTime, aCompositeId, aCompositeEnd, + transaction.mTxnEndTime - transaction.mTxnStartTime, mVsyncRate, + transaction.mContainsSVG, false); + } + +#if defined(ENABLE_FRAME_LATENCY_LOG) + if (transaction.mId.IsValid()) { + if (transaction.mRefreshStartTime) { + int32_t latencyMs = lround( + (aCompositeEnd - transaction.mRefreshStartTime).ToMilliseconds()); + printf_stderr( + "From transaction start to end of generate frame latencyMs %d this " + "%p\n", + latencyMs, this); + } + if (transaction.mFwdTime) { + int32_t latencyMs = + lround((aCompositeEnd - transaction.mFwdTime).ToMilliseconds()); + printf_stderr( + "From forwarding transaction to end of generate frame latencyMs %d " + "this %p\n", + latencyMs, this); + } + } +#endif + } + + mPendingTransactions.Clear(); + return id; +} + +void LayerTransactionParent::SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); +} + +void LayerTransactionParent::SendPendingAsyncMessages() { + mCompositorBridge->SendPendingAsyncMessages(); +} + +void LayerTransactionParent::SetAboutToSendAsyncMessages() { + mCompositorBridge->SetAboutToSendAsyncMessages(); +} + +void LayerTransactionParent::NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); +} + +bool LayerTransactionParent::BindLayerToHandle(RefPtr<Layer> aLayer, + const LayerHandle& aHandle) { + if (!aHandle || !aLayer) { + return false; + } + auto entry = mLayerMap.LookupForAdd(aHandle.Value()); + if (entry) { + return false; + } + entry.OrInsert([&aLayer]() { return aLayer; }); + return true; +} + +Layer* LayerTransactionParent::AsLayer(const LayerHandle& aHandle) { + if (!aHandle) { + return nullptr; + } + return mLayerMap.GetWeak(aHandle.Value()); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvNewCompositable( + const CompositableHandle& aHandle, const TextureInfo& aInfo) { + if (!AddCompositable(aHandle, aInfo, /* aUseWebRender */ false)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvReleaseLayer( + const LayerHandle& aHandle) { + RefPtr<Layer> layer; + if (!aHandle || !mLayerMap.Remove(aHandle.Value(), getter_AddRefs(layer))) { + return IPC_FAIL_NO_REASON(this); + } + if (mAnimStorage && layer->GetCompositorAnimationsId()) { + mAnimStorage->ClearById(layer->GetCompositorAnimationsId()); + layer->ClearCompositorAnimations(); + } + layer->Disconnect(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvReleaseCompositable( + const CompositableHandle& aHandle) { + ReleaseCompositable(aHandle); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvRecordPaintTimes( + const PaintTiming& aTiming) { + // Currently we only add paint timings for remote layers. In the future + // we could be smarter and use paint timings from the UI process, either + // as a separate overlay or if no remote layers are attached. + if (mLayerManager && mCompositorBridge->IsRemote()) { + mLayerManager->RecordPaintTimes(aTiming); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetTextureFactoryIdentifier( + TextureFactoryIdentifier* aIdentifier) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + // Default constructor sets mParentBackend to LAYERS_NONE. + return IPC_OK(); + } + + *aIdentifier = mLayerManager->GetTextureFactoryIdentifier(); + return IPC_OK(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/LayerTransactionParent.h b/gfx/layers/ipc/LayerTransactionParent.h new file mode 100644 index 0000000000..73850a5e6b --- /dev/null +++ b/gfx/layers/ipc/LayerTransactionParent.h @@ -0,0 +1,236 @@ +/* -*- 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_LAYERTRANSACTIONPARENT_H +#define MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H + +#include <stddef.h> // for size_t +#include <stdint.h> // for uint64_t, uint32_t +#include "CompositableTransactionParent.h" +#include "mozilla/Attributes.h" // for override +#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc +#include "mozilla/layers/PLayerTransactionParent.h" +#include "nsRefPtrHashtable.h" +#include "nsTArrayForwardDeclare.h" // for nsTArray + +namespace mozilla { + +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layers { + +class Layer; +class HostLayerManager; +class ShadowLayerParent; +class CompositableParent; +class CompositorAnimationStorage; +class CompositorBridgeParentBase; + +class LayerTransactionParent final : public PLayerTransactionParent, + public CompositableParentManager, + public mozilla::ipc::IShmemAllocator { + typedef nsTArray<Edit> EditArray; + typedef nsTArray<OpDestroy> OpDestroyArray; + typedef nsTArray<PluginWindowData> PluginsArray; + + friend class PLayerTransactionParent; + + public: + LayerTransactionParent(HostLayerManager* aManager, + CompositorBridgeParentBase* aBridge, + CompositorAnimationStorage* aAnimStorage, LayersId aId, + TimeDuration aVsyncRate); + + protected: + virtual ~LayerTransactionParent(); + + public: + void Destroy(); + + void SetLayerManager(HostLayerManager* aLayerManager, + CompositorAnimationStorage* aAnimStorage); + + LayersId GetId() const { return mId; } + Layer* GetRoot() const { return mRoot; } + + LayersObserverEpoch GetChildEpoch() const { return mChildEpoch; } + bool ShouldParentObserveEpoch(); + + IShmemAllocator* AsShmemAllocator() override { return this; } + + bool AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) override; + + bool AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) override; + + bool DeallocShmem(ipc::Shmem& aShmem) override; + + bool IsSameProcess() const override; + + void SetPendingTransactionId(TransactionId aId, const VsyncId& aVsyncId, + const TimeStamp& aVsyncStartTime, + const TimeStamp& aRefreshStartTime, + const TimeStamp& aTxnStartTime, + const TimeStamp& aTxnEndTime, bool aContainsSVG, + const nsCString& aURL, + const TimeStamp& aFwdTime); + TransactionId FlushTransactionId(const VsyncId& aId, + TimeStamp& aCompositeEnd); + + // CompositableParentManager + void SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) override; + + void SendPendingAsyncMessages() override; + + void SetAboutToSendAsyncMessages() override; + + void NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) override; + + base::ProcessId GetChildProcessId() override { return OtherPid(); } + + protected: + mozilla::ipc::IPCResult RecvShutdown(); + mozilla::ipc::IPCResult RecvShutdownSync(); + + mozilla::ipc::IPCResult RecvPaintTime(const TransactionId& aTransactionId, + const TimeDuration& aPaintTime); + + mozilla::ipc::IPCResult RecvUpdate(const TransactionInfo& aInfo); + + mozilla::ipc::IPCResult RecvSetLayersObserverEpoch( + const LayersObserverEpoch& aChildEpoch); + mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle, + const TextureInfo& aInfo); + mozilla::ipc::IPCResult RecvReleaseLayer(const LayerHandle& aHandle); + mozilla::ipc::IPCResult RecvReleaseCompositable( + const CompositableHandle& aHandle); + + mozilla::ipc::IPCResult RecvClearCachedResources(); + mozilla::ipc::IPCResult RecvScheduleComposite(); + mozilla::ipc::IPCResult RecvSetTestSampleTime(const TimeStamp& aTime); + mozilla::ipc::IPCResult RecvLeaveTestMode(); + mozilla::ipc::IPCResult RecvGetAnimationValue( + const uint64_t& aCompositorAnimationsId, OMTAValue* aValue); + mozilla::ipc::IPCResult RecvGetTransform(const LayerHandle& aHandle, + Maybe<Matrix4x4>* aTransform); + mozilla::ipc::IPCResult RecvSetAsyncScrollOffset( + const ScrollableLayerGuid::ViewID& aId, const float& aX, const float& aY); + mozilla::ipc::IPCResult RecvSetAsyncZoom( + const ScrollableLayerGuid::ViewID& aId, const float& aValue); + mozilla::ipc::IPCResult RecvFlushApzRepaints(); + mozilla::ipc::IPCResult RecvGetAPZTestData(APZTestData* aOutData); + mozilla::ipc::IPCResult RecvGetFrameUniformity(FrameUniformityData* aOutData); + mozilla::ipc::IPCResult RecvRequestProperty(const nsString& aProperty, + float* aValue); + mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC( + const uint64_t& aBlockId, nsTArray<ScrollableLayerGuid>&& aTargets); + mozilla::ipc::IPCResult RecvRecordPaintTimes(const PaintTiming& aTiming); + mozilla::ipc::IPCResult RecvGetTextureFactoryIdentifier( + TextureFactoryIdentifier* aIdentifier); + + bool SetLayerAttributes(const OpSetLayerAttributes& aOp); + + void ActorDestroy(ActorDestroyReason why) override; + + template <typename T> + bool BindLayer(const RefPtr<Layer>& aLayer, const T& aCreateOp) { + return BindLayerToHandle(aLayer, aCreateOp.layer()); + } + + bool BindLayerToHandle(RefPtr<Layer> aLayer, const LayerHandle& aHandle); + + Layer* AsLayer(const LayerHandle& aLayer); + + bool Attach(Layer* aLayer, CompositableHost* aCompositable, + bool aIsAsyncVideo); + + void AddIPDLReference() { + MOZ_ASSERT(mIPCOpen == false); + mIPCOpen = true; + AddRef(); + } + void ReleaseIPDLReference() { + MOZ_ASSERT(mIPCOpen == true); + mIPCOpen = false; + Release(); + } + friend class CompositorBridgeParent; + friend class ContentCompositorBridgeParent; + + private: + // This is a function so we can log or breakpoint on why hit + // testing tree changes are made. + void UpdateHitTestingTree(Layer* aLayer, const char* aWhy) { + mUpdateHitTestingTree = true; + } + + private: + RefPtr<HostLayerManager> mLayerManager; + CompositorBridgeParentBase* mCompositorBridge; + RefPtr<CompositorAnimationStorage> mAnimStorage; + + // Hold the root because it might be grafted under various + // containers in the "real" layer tree + RefPtr<Layer> mRoot; + + // Mapping from LayerHandles to Layers. + nsRefPtrHashtable<nsUint64HashKey, Layer> mLayerMap; + + LayersId mId; + + // These fields keep track of the latest epoch values in the child and the + // parent. mChildEpoch is the latest epoch value received from the child. + // mParentEpoch is the latest epoch value that we have told BrowserParent + // about (via ObserveLayerUpdate). + LayersObserverEpoch mChildEpoch; + LayersObserverEpoch mParentEpoch; + + TimeDuration mVsyncRate; + + struct PendingTransaction { + TransactionId mId; + VsyncId mTxnVsyncId; + TimeStamp mVsyncStartTime; + TimeStamp mRefreshStartTime; + TimeStamp mTxnStartTime; + TimeStamp mTxnEndTime; + TimeStamp mFwdTime; + nsCString mTxnURL; + bool mContainsSVG; + }; + AutoTArray<PendingTransaction, 2> mPendingTransactions; + + // When the widget/frame/browser stuff in this process begins its + // destruction process, we need to Disconnect() all the currently + // live shadow layers, because some of them might be orphaned from + // the layer tree. This happens in Destroy() above. After we + // Destroy() ourself, there's a window in which that information + // hasn't yet propagated back to the child side and it might still + // send us layer transactions. We want to ignore those transactions + // because they refer to "zombie layers" on this side. So, we track + // that state with |mDestroyed|. This is similar to, but separate + // from, |mLayerManager->IsDestroyed()|; we might have had Destroy() + // called on us but the mLayerManager might not be destroyed, or + // vice versa. In both cases though, we want to ignore shadow-layer + // transactions posted by the child. + + bool mDestroyed; + bool mIPCOpen; + + // This is set during RecvUpdate to track whether we'll need to update + // APZ's hit test regions. + bool mUpdateHitTestingTree; +}; + +} // namespace layers +} // namespace mozilla + +#endif // MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H 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..a3454f2505 --- /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; + 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..1bef016108 --- /dev/null +++ b/gfx/layers/ipc/LayersMessageUtils.h @@ -0,0 +1,980 @@ +/* -*- 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/LayerAttributes.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/MatrixMessage.h" +#include "mozilla/layers/RepaintRequest.h" +#include "nsSize.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(Message* msg, const paramType& param) { + WriteParam(msg, param.mId); + WriteParam(msg, param.mTime); + WriteParam(msg, param.mOutputTime); + } + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return ReadParam(msg, iter, &result->mId) && + ReadParam(msg, iter, &result->mTime) && + ReadParam(msg, iter, &result->mOutputTime); + } +}; + +template <> +struct ParamTraits<mozilla::layers::MatrixMessage> { + typedef mozilla::layers::MatrixMessage paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mMatrix); + WriteParam(aMsg, aParam.mTopLevelViewportVisibleRectInBrowserCoords); + WriteParam(aMsg, aParam.mLayersId); + } + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mMatrix) && + ReadParam(aMsg, aIter, + &aResult->mTopLevelViewportVisibleRectInBrowserCoords) && + ReadParam(aMsg, aIter, &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(Message* msg, const paramType& param) { + WriteParam(msg, param.mHandle); + } + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return ReadParam(msg, iter, &result->mHandle); + } +}; + +template <> +struct ParamTraits<mozilla::layers::CompositableHandle> { + typedef mozilla::layers::CompositableHandle paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.mHandle); + } + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return ReadParam(msg, iter, &result->mHandle); + } +}; + +template <> +struct ParamTraits<mozilla::layers::FrameMetrics> + : BitfieldHelper<mozilla::layers::FrameMetrics> { + typedef mozilla::layers::FrameMetrics paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mScrollId); + WriteParam(aMsg, aParam.mPresShellResolution); + WriteParam(aMsg, aParam.mCompositionBounds); + WriteParam(aMsg, aParam.mDisplayPort); + WriteParam(aMsg, aParam.mCriticalDisplayPort); + WriteParam(aMsg, aParam.mScrollableRect); + WriteParam(aMsg, aParam.mCumulativeResolution); + WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel); + WriteParam(aMsg, aParam.mScrollOffset); + WriteParam(aMsg, aParam.mZoom); + WriteParam(aMsg, aParam.mScrollGeneration); + WriteParam(aMsg, aParam.mRootCompositionSize); + WriteParam(aMsg, aParam.mDisplayPortMargins); + WriteParam(aMsg, aParam.mPresShellId); + WriteParam(aMsg, aParam.mLayoutViewport); + WriteParam(aMsg, aParam.mExtraResolution); + WriteParam(aMsg, aParam.mPaintRequestTime); + WriteParam(aMsg, aParam.mVisualDestination); + WriteParam(aMsg, aParam.mVisualScrollUpdateType); + WriteParam(aMsg, aParam.mFixedLayerMargins); + WriteParam(aMsg, aParam.mCompositionSizeWithoutDynamicToolbar); + WriteParam(aMsg, aParam.mIsRootContent); + WriteParam(aMsg, aParam.mIsScrollInfoLayer); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mScrollId) && + ReadParam(aMsg, aIter, &aResult->mPresShellResolution) && + ReadParam(aMsg, aIter, &aResult->mCompositionBounds) && + ReadParam(aMsg, aIter, &aResult->mDisplayPort) && + ReadParam(aMsg, aIter, &aResult->mCriticalDisplayPort) && + ReadParam(aMsg, aIter, &aResult->mScrollableRect) && + ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) && + ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) && + ReadParam(aMsg, aIter, &aResult->mScrollOffset) && + ReadParam(aMsg, aIter, &aResult->mZoom) && + ReadParam(aMsg, aIter, &aResult->mScrollGeneration) && + ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) && + ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) && + ReadParam(aMsg, aIter, &aResult->mPresShellId) && + ReadParam(aMsg, aIter, &aResult->mLayoutViewport) && + ReadParam(aMsg, aIter, &aResult->mExtraResolution) && + ReadParam(aMsg, aIter, &aResult->mPaintRequestTime) && + ReadParam(aMsg, aIter, &aResult->mVisualDestination) && + ReadParam(aMsg, aIter, &aResult->mVisualScrollUpdateType) && + ReadParam(aMsg, aIter, &aResult->mFixedLayerMargins) && + ReadParam(aMsg, aIter, + &aResult->mCompositionSizeWithoutDynamicToolbar) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetIsRootContent) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetIsScrollInfoLayer)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::RepaintRequest> + : BitfieldHelper<mozilla::layers::RepaintRequest> { + typedef mozilla::layers::RepaintRequest paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mScrollId); + WriteParam(aMsg, aParam.mPresShellResolution); + WriteParam(aMsg, aParam.mCompositionBounds); + WriteParam(aMsg, aParam.mCumulativeResolution); + WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel); + WriteParam(aMsg, aParam.mScrollOffset); + WriteParam(aMsg, aParam.mZoom); + WriteParam(aMsg, aParam.mScrollGeneration); + WriteParam(aMsg, aParam.mDisplayPortMargins); + WriteParam(aMsg, aParam.mPresShellId); + WriteParam(aMsg, aParam.mLayoutViewport); + WriteParam(aMsg, aParam.mExtraResolution); + WriteParam(aMsg, aParam.mPaintRequestTime); + WriteParam(aMsg, aParam.mScrollUpdateType); + WriteParam(aMsg, aParam.mIsRootContent); + WriteParam(aMsg, aParam.mIsAnimationInProgress); + WriteParam(aMsg, aParam.mIsScrollInfoLayer); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mScrollId) && + ReadParam(aMsg, aIter, &aResult->mPresShellResolution) && + ReadParam(aMsg, aIter, &aResult->mCompositionBounds) && + ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) && + ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) && + ReadParam(aMsg, aIter, &aResult->mScrollOffset) && + ReadParam(aMsg, aIter, &aResult->mZoom) && + ReadParam(aMsg, aIter, &aResult->mScrollGeneration) && + ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) && + ReadParam(aMsg, aIter, &aResult->mPresShellId) && + ReadParam(aMsg, aIter, &aResult->mLayoutViewport) && + ReadParam(aMsg, aIter, &aResult->mExtraResolution) && + ReadParam(aMsg, aIter, &aResult->mPaintRequestTime) && + ReadParam(aMsg, aIter, &aResult->mScrollUpdateType) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetIsRootContent) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetIsAnimationInProgress) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetIsScrollInfoLayer)); + } +}; + +template <> +struct ParamTraits<nsSize> { + typedef nsSize paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.width); + WriteParam(aMsg, aParam.height); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->width) && + ReadParam(aMsg, aIter, &aResult->height); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ScrollSnapInfo::ScrollSnapRange> { + typedef mozilla::layers::ScrollSnapInfo::ScrollSnapRange paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mStart); + WriteParam(aMsg, aParam.mEnd); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mStart) && + ReadParam(aMsg, aIter, &aResult->mEnd); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ScrollSnapInfo> { + typedef mozilla::layers::ScrollSnapInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mScrollSnapStrictnessX); + WriteParam(aMsg, aParam.mScrollSnapStrictnessY); + WriteParam(aMsg, aParam.mSnapPositionX); + WriteParam(aMsg, aParam.mSnapPositionY); + WriteParam(aMsg, aParam.mXRangeWiderThanSnapport); + WriteParam(aMsg, aParam.mYRangeWiderThanSnapport); + WriteParam(aMsg, aParam.mSnapportSize); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mScrollSnapStrictnessX) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapStrictnessY) && + ReadParam(aMsg, aIter, &aResult->mSnapPositionX) && + ReadParam(aMsg, aIter, &aResult->mSnapPositionY) && + ReadParam(aMsg, aIter, &aResult->mXRangeWiderThanSnapport) && + ReadParam(aMsg, aIter, &aResult->mYRangeWiderThanSnapport) && + ReadParam(aMsg, aIter, &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(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mBehaviorX); + WriteParam(aMsg, aParam.mBehaviorY); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mBehaviorX) && + ReadParam(aMsg, aIter, &aResult->mBehaviorY)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::LayerClip> { + typedef mozilla::layers::LayerClip paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mClipRect); + WriteParam(aMsg, aParam.mMaskLayerIndex); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mClipRect) && + ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex)); + } +}; + +template <> +struct ParamTraits<mozilla::ScrollGeneration> + : PlainOldDataSerializer<mozilla::ScrollGeneration> {}; + +template <> +struct ParamTraits<mozilla::ScrollPositionUpdate> + : PlainOldDataSerializer<mozilla::ScrollPositionUpdate> {}; + +template <> +struct ParamTraits<mozilla::layers::ScrollMetadata> + : BitfieldHelper<mozilla::layers::ScrollMetadata> { + typedef mozilla::layers::ScrollMetadata paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mMetrics); + WriteParam(aMsg, aParam.mSnapInfo); + WriteParam(aMsg, aParam.mScrollParentId); + WriteParam(aMsg, aParam.mBackgroundColor); + WriteParam(aMsg, aParam.GetContentDescription()); + WriteParam(aMsg, aParam.mLineScrollAmount); + WriteParam(aMsg, aParam.mPageScrollAmount); + WriteParam(aMsg, aParam.mScrollClip); + WriteParam(aMsg, aParam.mHasScrollgrab); + WriteParam(aMsg, aParam.mIsLayersIdRoot); + WriteParam(aMsg, aParam.mIsAutoDirRootContentRTL); + WriteParam(aMsg, aParam.mForceDisableApz); + WriteParam(aMsg, aParam.mResolutionUpdated); + WriteParam(aMsg, aParam.mIsRDMTouchSimulationActive); + WriteParam(aMsg, aParam.mDidContentGetPainted); + WriteParam(aMsg, aParam.mDisregardedDirection); + WriteParam(aMsg, aParam.mOverscrollBehavior); + WriteParam(aMsg, aParam.mScrollUpdates); + } + + static bool ReadContentDescription(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + nsCString str; + if (!ReadParam(aMsg, aIter, &str)) { + return false; + } + aResult->SetContentDescription(str); + return true; + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mMetrics) && + ReadParam(aMsg, aIter, &aResult->mSnapInfo) && + ReadParam(aMsg, aIter, &aResult->mScrollParentId) && + ReadParam(aMsg, aIter, &aResult->mBackgroundColor) && + ReadContentDescription(aMsg, aIter, aResult) && + ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) && + ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) && + ReadParam(aMsg, aIter, &aResult->mScrollClip) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetHasScrollgrab) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetIsLayersIdRoot) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetIsAutoDirRootContentRTL) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetForceDisableApz) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetResolutionUpdated) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetIsRDMTouchSimulationActive)) && + ReadBoolForBitfield(aMsg, aIter, aResult, + ¶mType::SetDidContentGetPainted) && + ReadParam(aMsg, aIter, &aResult->mDisregardedDirection) && + ReadParam(aMsg, aIter, &aResult->mOverscrollBehavior) && + ReadParam(aMsg, aIter, &aResult->mScrollUpdates); + } +}; + +template <> +struct ParamTraits<mozilla::layers::TextureFactoryIdentifier> { + typedef mozilla::layers::TextureFactoryIdentifier paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mParentBackend); + WriteParam(aMsg, aParam.mWebRenderBackend); + WriteParam(aMsg, aParam.mWebRenderCompositor); + WriteParam(aMsg, aParam.mParentProcessType); + WriteParam(aMsg, aParam.mMaxTextureSize); + WriteParam(aMsg, aParam.mSupportsTextureDirectMapping); + WriteParam(aMsg, aParam.mCompositorUseANGLE); + WriteParam(aMsg, aParam.mCompositorUseDComp); + WriteParam(aMsg, aParam.mUseCompositorWnd); + WriteParam(aMsg, aParam.mSupportsTextureBlitting); + WriteParam(aMsg, aParam.mSupportsPartialUploads); + WriteParam(aMsg, aParam.mSupportsComponentAlpha); + WriteParam(aMsg, aParam.mUsingAdvancedLayers); + WriteParam(aMsg, aParam.mSyncHandle); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + bool result = + ReadParam(aMsg, aIter, &aResult->mParentBackend) && + ReadParam(aMsg, aIter, &aResult->mWebRenderBackend) && + ReadParam(aMsg, aIter, &aResult->mWebRenderCompositor) && + ReadParam(aMsg, aIter, &aResult->mParentProcessType) && + ReadParam(aMsg, aIter, &aResult->mMaxTextureSize) && + ReadParam(aMsg, aIter, &aResult->mSupportsTextureDirectMapping) && + ReadParam(aMsg, aIter, &aResult->mCompositorUseANGLE) && + ReadParam(aMsg, aIter, &aResult->mCompositorUseDComp) && + ReadParam(aMsg, aIter, &aResult->mUseCompositorWnd) && + ReadParam(aMsg, aIter, &aResult->mSupportsTextureBlitting) && + ReadParam(aMsg, aIter, &aResult->mSupportsPartialUploads) && + ReadParam(aMsg, aIter, &aResult->mSupportsComponentAlpha) && + ReadParam(aMsg, aIter, &aResult->mUsingAdvancedLayers) && + ReadParam(aMsg, aIter, &aResult->mSyncHandle); + return result; + } +}; + +template <> +struct ParamTraits<mozilla::layers::TextureInfo> { + typedef mozilla::layers::TextureInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mCompositableType); + WriteParam(aMsg, aParam.mTextureFlags); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mCompositableType) && + ReadParam(aMsg, aIter, &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(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mLayersId); + WriteParam(aMsg, aParam.mPresShellId); + WriteParam(aMsg, aParam.mScrollId); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mLayersId) && + ReadParam(aMsg, aIter, &aResult->mPresShellId) && + ReadParam(aMsg, aIter, &aResult->mScrollId)); + } +}; + +template <> +struct ParamTraits<nsEventStatus> + : public ContiguousEnumSerializer<nsEventStatus, nsEventStatus_eIgnore, + nsEventStatus_eSentinel> {}; + +template <> +struct ParamTraits<mozilla::layers::APZHandledResult> + : public ContiguousEnumSerializer< + mozilla::layers::APZHandledResult, + mozilla::layers::APZHandledResult::Unhandled, + mozilla::layers::APZHandledResult::Last> {}; + +template <> +struct ParamTraits<mozilla::layers::APZEventResult> { + typedef mozilla::layers::APZEventResult paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mStatus); + WriteParam(aMsg, aParam.mTargetGuid); + WriteParam(aMsg, aParam.mInputBlockId); + WriteParam(aMsg, aParam.mHandledResult); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mStatus) && + ReadParam(aMsg, aIter, &aResult->mTargetGuid) && + ReadParam(aMsg, aIter, &aResult->mInputBlockId) && + ReadParam(aMsg, aIter, &aResult->mHandledResult)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ZoomConstraints> { + typedef mozilla::layers::ZoomConstraints paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mAllowZoom); + WriteParam(aMsg, aParam.mAllowDoubleTapZoom); + WriteParam(aMsg, aParam.mMinZoom); + WriteParam(aMsg, aParam.mMaxZoom); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mAllowZoom) && + ReadParam(aMsg, aIter, &aResult->mAllowDoubleTapZoom) && + ReadParam(aMsg, aIter, &aResult->mMinZoom) && + ReadParam(aMsg, aIter, &aResult->mMaxZoom)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::EventRegions> { + typedef mozilla::layers::EventRegions paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mHitRegion); + WriteParam(aMsg, aParam.mDispatchToContentHitRegion); + WriteParam(aMsg, aParam.mNoActionRegion); + WriteParam(aMsg, aParam.mHorizontalPanRegion); + WriteParam(aMsg, aParam.mVerticalPanRegion); + WriteParam(aMsg, aParam.mDTCRequiresTargetConfirmation); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mHitRegion) && + ReadParam(aMsg, aIter, &aResult->mDispatchToContentHitRegion) && + ReadParam(aMsg, aIter, &aResult->mNoActionRegion) && + ReadParam(aMsg, aIter, &aResult->mHorizontalPanRegion) && + ReadParam(aMsg, aIter, &aResult->mVerticalPanRegion) && + ReadParam(aMsg, aIter, &aResult->mDTCRequiresTargetConfirmation)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::FocusTarget::ScrollTargets> { + typedef mozilla::layers::FocusTarget::ScrollTargets paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mHorizontal); + WriteParam(aMsg, aParam.mVertical); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mHorizontal) && + ReadParam(aMsg, aIter, &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(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mSequenceNumber); + WriteParam(aMsg, aParam.mFocusHasKeyEventListeners); + WriteParam(aMsg, aParam.mData); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mSequenceNumber) || + !ReadParam(aMsg, aIter, &aResult->mFocusHasKeyEventListeners) || + !ReadParam(aMsg, aIter, &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(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mType); + WriteParam(aMsg, aParam.mForward); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mType) && + ReadParam(aMsg, aIter, &aResult->mForward); + } +}; + +template <> +struct ParamTraits<mozilla::layers::KeyboardShortcut> { + typedef mozilla::layers::KeyboardShortcut paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mAction); + WriteParam(aMsg, aParam.mKeyCode); + WriteParam(aMsg, aParam.mCharCode); + WriteParam(aMsg, aParam.mModifiers); + WriteParam(aMsg, aParam.mModifiersMask); + WriteParam(aMsg, aParam.mEventType); + WriteParam(aMsg, aParam.mDispatchToContent); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mAction) && + ReadParam(aMsg, aIter, &aResult->mKeyCode) && + ReadParam(aMsg, aIter, &aResult->mCharCode) && + ReadParam(aMsg, aIter, &aResult->mModifiers) && + ReadParam(aMsg, aIter, &aResult->mModifiersMask) && + ReadParam(aMsg, aIter, &aResult->mEventType) && + ReadParam(aMsg, aIter, &aResult->mDispatchToContent); + } +}; + +template <> +struct ParamTraits<mozilla::layers::KeyboardMap> { + typedef mozilla::layers::KeyboardMap paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.Shortcuts()); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + nsTArray<mozilla::layers::KeyboardShortcut> shortcuts; + if (!ReadParam(aMsg, aIter, &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(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mViewId); + WriteParam(aMsg, aParam.mPresShellId); + WriteParam(aMsg, aParam.mDragStartSequenceNumber); + WriteParam(aMsg, aParam.mScrollbarDragOffset); + WriteParam(aMsg, aParam.mDirection); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mViewId) && + ReadParam(aMsg, aIter, &aResult->mPresShellId) && + ReadParam(aMsg, aIter, &aResult->mDragStartSequenceNumber) && + ReadParam(aMsg, aIter, &aResult->mScrollbarDragOffset) && + ReadParam(aMsg, aIter, &aResult->mDirection)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::CompositorOptions> { + typedef mozilla::layers::CompositorOptions paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mUseAPZ); + WriteParam(aMsg, aParam.mUseWebRender); + WriteParam(aMsg, aParam.mUseAdvancedLayers); + WriteParam(aMsg, aParam.mInitiallyPaused); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mUseAPZ) && + ReadParam(aMsg, aIter, &aResult->mUseWebRender) && + ReadParam(aMsg, aIter, &aResult->mUseAdvancedLayers) && + ReadParam(aMsg, aIter, &aResult->mInitiallyPaused); + } +}; + +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(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mDirection); + WriteParam(aMsg, aParam.mScrollbarLayerType); + WriteParam(aMsg, aParam.mThumbRatio); + WriteParam(aMsg, aParam.mThumbStart); + WriteParam(aMsg, aParam.mThumbLength); + WriteParam(aMsg, aParam.mThumbIsAsyncDraggable); + WriteParam(aMsg, aParam.mScrollTrackStart); + WriteParam(aMsg, aParam.mScrollTrackLength); + WriteParam(aMsg, aParam.mTargetViewId); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mDirection) && + ReadParam(aMsg, aIter, &aResult->mScrollbarLayerType) && + ReadParam(aMsg, aIter, &aResult->mThumbRatio) && + ReadParam(aMsg, aIter, &aResult->mThumbStart) && + ReadParam(aMsg, aIter, &aResult->mThumbLength) && + ReadParam(aMsg, aIter, &aResult->mThumbIsAsyncDraggable) && + ReadParam(aMsg, aIter, &aResult->mScrollTrackStart) && + ReadParam(aMsg, aIter, &aResult->mScrollTrackLength) && + ReadParam(aMsg, aIter, &aResult->mTargetViewId); + } +}; + +template <> +struct ParamTraits<mozilla::layers::SimpleLayerAttributes::FixedPositionData> + : public PlainOldDataSerializer< + mozilla::layers::SimpleLayerAttributes::FixedPositionData> {}; + +template <> +struct ParamTraits<mozilla::layers::SimpleLayerAttributes::StickyPositionData> + : public PlainOldDataSerializer< + mozilla::layers::SimpleLayerAttributes::StickyPositionData> {}; + +template <> +struct ParamTraits<mozilla::layers::SimpleLayerAttributes> { + typedef mozilla::layers::SimpleLayerAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mTransform); + WriteParam(aMsg, aParam.mTransformIsPerspective); + WriteParam(aMsg, aParam.mScrolledClip); + WriteParam(aMsg, aParam.mPostXScale); + WriteParam(aMsg, aParam.mPostYScale); + WriteParam(aMsg, aParam.mContentFlags); + WriteParam(aMsg, aParam.mOpacity); + WriteParam(aMsg, aParam.mIsFixedPosition); + WriteParam(aMsg, aParam.mIsAsyncZoomContainerForViewId); + WriteParam(aMsg, aParam.mScrollbarData); + WriteParam(aMsg, aParam.mMixBlendMode); + WriteParam(aMsg, aParam.mForceIsolatedGroup); + WriteParam(aMsg, aParam.mFixedPositionData); + WriteParam(aMsg, aParam.mStickyPositionData); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mTransform) && + ReadParam(aMsg, aIter, &aResult->mTransformIsPerspective) && + ReadParam(aMsg, aIter, &aResult->mScrolledClip) && + ReadParam(aMsg, aIter, &aResult->mPostXScale) && + ReadParam(aMsg, aIter, &aResult->mPostYScale) && + ReadParam(aMsg, aIter, &aResult->mContentFlags) && + ReadParam(aMsg, aIter, &aResult->mOpacity) && + ReadParam(aMsg, aIter, &aResult->mIsFixedPosition) && + ReadParam(aMsg, aIter, &aResult->mIsAsyncZoomContainerForViewId) && + ReadParam(aMsg, aIter, &aResult->mScrollbarData) && + ReadParam(aMsg, aIter, &aResult->mMixBlendMode) && + ReadParam(aMsg, aIter, &aResult->mForceIsolatedGroup) && + ReadParam(aMsg, aIter, &aResult->mFixedPositionData) && + ReadParam(aMsg, aIter, &aResult->mStickyPositionData); + } +}; + +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(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mType); + WriteParam(aMsg, aParam.mTimeStamp); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mType) && + ReadParam(aMsg, aIter, &aResult->mTimeStamp); + } +}; + +template <> +struct ParamTraits<mozilla::RayReferenceData> { + typedef mozilla::RayReferenceData paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mInitialPosition); + WriteParam(aMsg, aParam.mContainingBlockRect); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mInitialPosition) && + ReadParam(aMsg, aIter, &aResult->mContainingBlockRect)); + } +}; + +#define IMPL_PARAMTRAITS_BY_SERDE(type_) \ + template <> \ + struct ParamTraits<mozilla::type_> { \ + typedef mozilla::type_ paramType; \ + static void Write(Message* aMsg, const paramType& aParam) { \ + mozilla::ipc::ByteBuf v; \ + mozilla::DebugOnly<bool> rv = Servo_##type_##_Serialize(&aParam, &v); \ + MOZ_ASSERT(rv, "Serialize ##type_## failed"); \ + WriteParam(aMsg, std::move(v)); \ + } \ + static bool Read(const Message* aMsg, PickleIterator* aIter, \ + paramType* aResult) { \ + mozilla::ipc::ByteBuf in; \ + bool rv = ReadParam(aMsg, aIter, &in); \ + if (!rv) { \ + return false; \ + } \ + return in.mData && Servo_##type_##_Deserialize(&in, aResult); \ + } \ + }; + +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) + +} /* 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..ee283795e8 --- /dev/null +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -0,0 +1,573 @@ +/* -*- 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 "ImageLayers.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 struct mozilla::gfx::Point from "mozilla/gfx/Point.h"; +using struct mozilla::gfx::Point3D from "mozilla/gfx/Point.h"; +using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h"; +using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h"; +using class 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 class 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 hal::ScreenOrientation from "mozilla/HalScreenConfiguration.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::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::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::EventRegions 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::FocusTarget from "mozilla/layers/FocusTarget.h"; +using struct mozilla::layers::ScrollMetadata from "FrameMetrics.h"; +using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.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"; +using mozilla::layers::SimpleLayerAttributes from "mozilla/layers/LayerAttributes.h"; +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::TransactionId from "mozilla/layers/LayersTypes.h"; +using mozilla::VsyncId from "mozilla/VsyncDispatcher.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"; + +namespace mozilla { +namespace layers { + +struct TargetConfig { + IntRect naturalBounds; + ScreenRotation rotation; + ScreenOrientation orientation; + nsIntRegion clearRegion; +}; + +// Create a shadow layer for |layer| +struct OpCreatePaintedLayer { LayerHandle layer; }; +struct OpCreateContainerLayer { LayerHandle layer; }; +struct OpCreateImageLayer { LayerHandle layer; }; +struct OpCreateColorLayer { LayerHandle layer; }; +struct OpCreateCanvasLayer { LayerHandle layer; }; +struct OpCreateRefLayer { LayerHandle layer; }; + +struct OpAttachCompositable { + LayerHandle layer; + CompositableHandle compositable; +}; + +struct OpAttachAsyncCompositable { + LayerHandle layer; + CompositableHandle compositable; +}; + +struct ThebesBufferData { + IntRect rect; + IntPoint rotation; +}; + +struct CubicBezierFunction { + float x1; + float y1; + float x2; + float y2; +}; + +struct StepFunction { + int steps; + uint8_t type; // Converted from StyleStepPosition. +}; + +union TimingFunction { + null_t; + CubicBezierFunction; + StepFunction; +}; + +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; + TimingFunction 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; +}; + +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. + TimingFunction 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 + // 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; +}; + +struct CompositorAnimations { + Animation[] animations; + // This id is used to map the layer animations between content + // and compositor side + uint64_t id; +}; + +// Change a layer's attributes +struct CommonLayerAttributes { + LayerIntRegion visibleRegion; + EventRegions eventRegions; + bool useClipRect; + ParentLayerIntRect clipRect; + LayerHandle maskLayer; + LayerHandle[] ancestorMaskLayers; + // Animated colors will only honored for ColorLayers. + CompositorAnimations compositorAnimations; + nsIntRegion invalidRegion; + ScrollMetadata[] scrollMetadata; + nsCString displayListLog; +}; + +struct PaintedLayerAttributes { + nsIntRegion validRegion; +}; +struct ContainerLayerAttributes { + float preXScale; + float preYScale; + float inheritedXScale; + float inheritedYScale; + float presShellResolution; +}; + +struct ColorLayerAttributes { LayerColor color; IntRect bounds; }; +struct CanvasLayerAttributes { SamplingFilter samplingFilter; IntRect bounds; }; +struct RefLayerAttributes { + LayersId id; + EventRegionsOverride eventRegionsOverride; + LayerIntSize remoteDocumentSize; +}; +struct ImageLayerAttributes { SamplingFilter samplingFilter; IntSize scaleToSize; ScaleMode scaleMode; }; + +union SpecificLayerAttributes { + null_t; + PaintedLayerAttributes; + ContainerLayerAttributes; + ColorLayerAttributes; + CanvasLayerAttributes; + RefLayerAttributes; + ImageLayerAttributes; +}; + +struct LayerAttributes { + CommonLayerAttributes common; + SpecificLayerAttributes specific; +}; + +// See nsIWidget Configurations +comparable struct PluginWindowData { + uintptr_t windowId; + LayoutDeviceIntRect[] clip; + LayoutDeviceIntRect bounds; + bool visible; +}; + +struct OpSetSimpleLayerAttributes { + LayerHandle layer; + SimpleLayerAttributes attrs; +}; + +struct OpSetLayerAttributes { + LayerHandle layer; + LayerAttributes attrs; +}; + +// Monkey with the tree structure +struct OpSetRoot { LayerHandle root; }; +struct OpInsertAfter { LayerHandle container; LayerHandle childLayer; LayerHandle after; }; +struct OpPrependChild { LayerHandle container; LayerHandle childLayer; }; +struct OpRemoveChild { LayerHandle container; LayerHandle childLayer; }; +struct OpRepositionChild { LayerHandle container; LayerHandle childLayer; LayerHandle after; }; +struct OpRaiseToTopChild { LayerHandle container; LayerHandle childLayer; }; + +struct OpSetDiagnosticTypes { DiagnosticTypes diagnostics; }; + +struct ShmemSection { + Shmem shmem; + uint32_t offset; + uint32_t size; +}; + +struct CrossProcessSemaphoreDescriptor { + CrossProcessSemaphoreHandle sem; +}; + +union ReadLockDescriptor { + ShmemSection; + CrossProcessSemaphoreDescriptor; + uintptr_t; + null_t; +}; + +struct TexturedTileDescriptor { + PTexture texture; + PTexture? textureOnWhite; + IntRect updateRect; + bool readLocked; + bool readLockedOnWhite; + bool wasPlaceholder; +}; + +struct PlaceholderTileDescriptor { +}; + +union TileDescriptor { + TexturedTileDescriptor; + PlaceholderTileDescriptor; +}; + +struct SurfaceDescriptorTiles { + nsIntRegion validRegion; + TileDescriptor[] tiles; + IntPoint tileOrigin; + IntSize tileSize; + int firstTileX; + int firstTileY; + int retainedWidth; + int retainedHeight; + float resolution; + float frameXResolution; + float frameYResolution; + bool isProgressive; +}; + +struct OpUseTiledLayerBuffer { + SurfaceDescriptorTiles tileLayerDescriptor; +}; + +struct OpPaintTextureRegion { + ThebesBufferData bufferData; + nsIntRegion updatedRegion; +}; + +/** + * 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 OpUseComponentAlphaTextures { + PTexture textureOnBlack; + PTexture textureOnWhite; + bool readLockedBlack; + bool readLockedWhite; +}; + +struct OpNotifyNotUsed { + uint64_t TextureId; + uint64_t fwdTransactionId; +}; + +struct OpDeliverAcquireFence { + PTexture texture; + FileDescriptor fenceFd; +}; + +struct OpDeliverReleaseFence { + FileDescriptor? fenceFd; + uint64_t bufferId; + uint64_t fwdTransactionId; + bool usesImageBridge; +}; + +union CompositableOperationDetail { + OpPaintTextureRegion; + + OpUseTiledLayerBuffer; + + OpRemoveTexture; + + OpUseTexture; + OpUseComponentAlphaTextures; + + OpDeliverAcquireFence; +}; + +struct CompositableOperation { + CompositableHandle compositable; + CompositableOperationDetail detail; +}; + +// A unit of a changeset; a set of these comprise a changeset +// If adding a new edit type that requires the hit testing tree to be updated, +// set the updateHitTestingTree flag to true in RecvUpdate() +union Edit { + OpCreatePaintedLayer; + OpCreateContainerLayer; + OpCreateImageLayer; + OpCreateColorLayer; + OpCreateCanvasLayer; + OpCreateRefLayer; + + OpSetDiagnosticTypes; + + OpSetRoot; + OpInsertAfter; + OpPrependChild; + OpRemoveChild; + OpRepositionChild; + OpRaiseToTopChild; + + OpAttachCompositable; + OpAttachAsyncCompositable; + + CompositableOperation; +}; + +// 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; + OpDeliverReleaseFence; +}; + +struct PaintTiming { + float serializeMs; + float sendMs; + float dlMs; + float dl2Ms; + float flbMs; + float rasterMs; +}; + +struct TransactionInfo +{ + Edit[] cset; + OpSetSimpleLayerAttributes[] setSimpleAttrs; + OpSetLayerAttributes[] setAttrs; + CompositableOperation[] paints; + OpDestroy[] toDestroy; + uint64_t fwdTransactionId; + TransactionId id; + TargetConfig targetConfig; + PluginWindowData[] plugins; + bool isFirstPaint; + FocusTarget focusTarget; + bool scheduleComposite; + uint32_t paintSequenceNumber; + bool isRepeatTransaction; + VsyncId vsyncId; + TimeStamp vsyncStart; + TimeStamp refreshStart; + TimeStamp transactionStart; + bool containsSVG; + nsCString url; + TimeStamp fwdTime; + /* This provides some timing information on any content that is meant to be + * presented during this transaction. + */ + CompositionPayload[] payload; +}; + +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..8661d6748f --- /dev/null +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -0,0 +1,204 @@ +/* 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"; + +using struct gfxPoint from "gfxPoint.h"; +using nsIntRegion from "nsRegion.h"; +using struct mozilla::layers::SurfaceDescriptorX11 from "gfxipc/SurfaceDescriptor.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::ColorDepth from "mozilla/gfx/Types.h"; +using mozilla::gfx::ColorRange 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::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; +using gfxImageFormat from "gfxTypes.h"; +using mozilla::layers::MaybeVideoBridgeSource from "mozilla/layers/VideoBridgeUtils.h"; + +namespace mozilla { +namespace layers { + +comparable struct SurfaceDescriptorFileMapping { + WindowsHandle handle; + SurfaceFormat format; + IntSize size; +}; + +comparable struct SurfaceDescriptorDIB { + // gfxWindowsSurface* + uintptr_t surface; +}; + +comparable struct SurfaceDescriptorD3D10 { + WindowsHandle handle; + SurfaceFormat format; + IntSize size; + YUVColorSpace yUVColorSpace; + 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; + double scaleFactor; + 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[] format; + uint32_t[] strides; + uint32_t[] offsets; + YUVColorSpace yUVColorSpace; + FileDescriptor[] fence; + uint32_t uid; + FileDescriptor[] refCount; +}; + +comparable struct SurfaceTextureDescriptor { + uint64_t handle; + IntSize size; + SurfaceFormat format; + bool continuous; + bool ignoreTransform; +}; + +comparable struct SurfaceDescriptorAndroidHardwareBuffer { + FileDescriptor handle; + 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 union RemoteDecoderVideoSubDescriptor { + SurfaceDescriptorD3D10; + SurfaceDescriptorDXGIYCbCr; + SurfaceDescriptorDMABuf; + SurfaceDescriptorMacIOSurface; + null_t; +}; + +comparable struct SurfaceDescriptorRemoteDecoder { + uint64_t handle; + RemoteDecoderVideoSubDescriptor subdesc; + MaybeVideoBridgeSource source; +}; + +comparable struct SurfaceDescriptorPlugin { + uint64_t id; + SurfaceDescriptorD3D10 pluginSurf; + SurfaceDescriptorD3D10 displaySurf; +}; + +comparable union SurfaceDescriptorGPUVideo { + SurfaceDescriptorRemoteDecoder; + SurfaceDescriptorPlugin; +}; + +comparable struct RGBDescriptor { + IntSize size; + SurfaceFormat format; + bool hasIntermediateBuffer; +}; + +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; + bool hasIntermediateBuffer; +}; + +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 union SurfaceDescriptor { + SurfaceDescriptorBuffer; + SurfaceDescriptorDIB; + SurfaceDescriptorD3D10; + SurfaceDescriptorFileMapping; + SurfaceDescriptorDXGIYCbCr; + SurfaceDescriptorX11; + SurfaceDescriptorDMABuf; + SurfaceTextureDescriptor; + SurfaceDescriptorAndroidHardwareBuffer; + EGLImageDescriptor; + SurfaceDescriptorMacIOSurface; + SurfaceDescriptorSharedGLTexture; + SurfaceDescriptorGPUVideo; + SurfaceDescriptorRecorded; + null_t; +}; + +} // namespace +} // namespace diff --git a/gfx/layers/ipc/PAPZ.ipdl b/gfx/layers/ipc/PAPZ.ipdl new file mode 100644 index 0000000000..9f0a3db340 --- /dev/null +++ b/gfx/layers/ipc/PAPZ.ipdl @@ -0,0 +1,78 @@ +/* -*- 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 protocol PCompositorBridge; + +using 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::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.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. + */ +sync protocol PAPZ +{ + manager PCompositorBridge; + +parent: + async __delete__(); + +child: + async LayerTransforms(MatrixMessage[] aTransforms); + + 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); + + 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..e6d70a9ddf --- /dev/null +++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl @@ -0,0 +1,86 @@ +/* -*- 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 CSSRect from "Units.h"; +using LayoutDeviceCoord from "Units.h"; +using mozilla::LayoutDevicePoint from "Units.h"; +using ScreenPoint from "Units.h"; +using mozilla::layers::MaybeZoomConstraints 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 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. + */ +protocol PAPZCTreeManager +{ +manager PCompositorBridge; + +parent: + + // These messages correspond to the methods + // on the IAPZCTreeManager interface + + async ZoomToRect(ScrollableLayerGuid aGuid, CSSRect aRect, uint32_t Flags); + + async ContentReceivedInputBlock(uint64_t aInputBlockId, bool PreventDefault); + + async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] Targets); + + async UpdateZoomConstraints(ScrollableLayerGuid aGuid, MaybeZoomConstraints 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 __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); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/layers/ipc/PAPZInputBridge.ipdl b/gfx/layers/ipc/PAPZInputBridge.ipdl new file mode 100644 index 0000000000..160a74f81c --- /dev/null +++ b/gfx/layers/ipc/PAPZInputBridge.ipdl @@ -0,0 +1,82 @@ +/* -*- 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/. */ + +using 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 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"; + +include protocol PGPU; + +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 main thread in the UI process, + * and 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 refcounted protocol PAPZInputBridge +{ +manager PGPU; + +parent: + // The following messages are used to + // implement the ReceiveInputEvent methods + + sync ReceiveMultiTouchInputEvent(MultiTouchInput aEvent) + returns (APZEventResult aOutResult, + MultiTouchInput aOutEvent); + + sync ReceiveMouseInputEvent(MouseInput aEvent) + returns (APZEventResult aOutResult, + MouseInput aOutEvent); + + sync ReceivePanGestureInputEvent(PanGestureInput aEvent) + returns (APZEventResult aOutResult, + PanGestureInput aOutEvent); + + sync ReceivePinchGestureInputEvent(PinchGestureInput aEvent) + returns (APZEventResult aOutResult, + PinchGestureInput aOutEvent); + + sync ReceiveTapGestureInputEvent(TapGestureInput aEvent) + returns (APZEventResult aOutResult, + TapGestureInput aOutEvent); + + sync ReceiveScrollWheelInputEvent(ScrollWheelInput aEvent) + returns (APZEventResult aOutResult, + ScrollWheelInput aOutEvent); + + sync ReceiveKeyboardInputEvent(KeyboardInput aEvent) + returns (APZEventResult aOutResult, + KeyboardInput aOutEvent); + + async UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage); + + sync ProcessUnhandledEvent(LayoutDeviceIntPoint aRefPoint) + returns (LayoutDeviceIntPoint aOutRefPoint, + ScrollableLayerGuid aOutTargetGuid, + uint64_t aOutFocusSequenceNumber, + LayersId aOutLayersId); + + async __delete__(); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/layers/ipc/PCanvas.ipdl b/gfx/layers/ipc/PCanvas.ipdl new file mode 100644 index 0000000000..2be61bd142 --- /dev/null +++ b/gfx/layers/ipc/PCanvas.ipdl @@ -0,0 +1,50 @@ +/* -*- 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"; + +using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h"; +using mozilla::layers::TextureType from "mozilla/layers/LayersTypes.h"; +using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; + +namespace mozilla { +namespace layers { + +/** + * PCanvas is the IPDL for recorded Canvas drawing. + */ +async refcounted 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..1a14ac5d4b --- /dev/null +++ b/gfx/layers/ipc/PCompositorBridge.ipdl @@ -0,0 +1,310 @@ +/* -*- 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 PLayerTransaction; +include protocol PTexture; +include protocol PWebGL; +include protocol PWebRenderBridge; +include protocol PWebGPU; +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; +include "mozilla/layers/WebRenderMessageUtils.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::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.h"; +using mozilla::layers::WindowKind from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; +using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h"; +using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.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 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"; +using struct DxgiAdapterDesc from "mozilla/D3DMessageUtils.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. + */ +sync refcounted protocol PCompositorBridge +{ + manager PCompositorManager; + + manages PAPZ; + manages PAPZCTreeManager; + // A Compositor manages a single Layer Manager (PLayerTransaction) + manages PLayerTransaction; + manages PTexture; + manages PCompositorWidget; + manages PWebRenderBridge; + manages PWebGL; + manages PWebGPU; + +child: + // The child should invalidate retained layers. This is used for local + // compositor device resets, such as in CompositorD3D9, and ensures that + // TextureSources are recreated. + async InvalidateLayers(LayersId layersId); + + // 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. + prio(mediumhigh) async DidComposite(LayersId id, TransactionId transactionId, + TimeStamp compositeStart, + TimeStamp compositeEnd); + + async NotifyFrameStats(FrameStats[] aFrameStats); + + /** + * Parent informs the child that the graphics objects are ready for + * compositing. This usually means that the graphics objects (textures + * and the like) are available on the GPU. This is used for chrome UI. + * @see RequestNotifyAfterRemotePaint + * @see PBrowser + */ + async RemotePaintIsReady(); + + /** + * Bounce plugin widget configurations over to the main thread for + * application on the widgets. Used on Windows and Linux in managing + * plugin widgets. + */ + async UpdatePluginConfigurations(LayoutDeviceIntPoint aContentOffset, + LayoutDeviceIntRegion aVisibleRegion, + PluginWindowData[] aPlugins); + + /** + * Captures an image for all visible child plugins of a given widget for use + * during scrolling. + * @param aParentWidget parent of widgets to be captured + */ + async CaptureAllPlugins(uintptr_t aParentWidget); + + /** + * Hides all registered plugin widgets associated with a particular chrome + * widget. + */ + async HideAllPlugins(uintptr_t aParentWidget); + + 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); + + // Constructor for WebGPU IPDL + // Must be called before Initialize(). + async PWebGPU(); + + /** + * Confirmation callback for UpdatePluginConfigurations and HideAllPlugins. + */ + async RemotePluginsReady(); + + // 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); + + // Make a snapshot of the content that would have been drawn to our + // render target at the time this message is received. If the size + // or format of |inSnapshot| doesn't match our render target, + // results are undefined. + // + // NB: this message will result in animations, transforms, effects, + // and so forth being interpolated. That's what we want to happen. + sync MakeSnapshot(SurfaceDescriptor inSnapshot, IntRect dirtyRect); + + // Make sure any pending composites are started immediately and + // block until they are completed. + sync FlushRendering(); + + // Same as FlushRendering, but asynchronous, since not all platforms require + // synchronous repaints on resize. + async FlushRenderingAsync(); + + // 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(); + + sync StartFrameTimeRecording(int32_t bufferSize) + returns (uint32_t startIndex); + + sync StopFrameTimeRecording(uint32_t startIndex) + returns (float[] intervals); + + // layersBackendHints is an ordered list of preffered backends where + // layersBackendHints[0] is the best backend. If any hints are LayersBackend::LAYERS_NONE + // that hint is ignored. + async PLayerTransaction(LayersBackend[] layersBackendHints, LayersId id); + + // Notify the compositor that a region of the screen has been invalidated. + async NotifyRegionInvalidated(nsIntRegion region); + + /** + * The child (content/chrome thread) requests that the parent inform it when + * the graphics objects are ready to display. + * @see PBrowser + * @see RemotePaintIsReady + */ + async RequestNotifyAfterRemotePaint(); + + /** + * Sent when the child has finished CaptureAllPlugins. + */ + async AllPluginsCaptured(); + + 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 EndRecordingToDisk() + returns (bool success); + + async EndRecordingToMemory() + returns (CollectedFramesParams? frames); + + sync SupportsAsyncDXGISurface() + returns (bool value); + sync PreferredDXGIAdapter() + returns (DxgiAdapterDesc desc); + + // To set up sharing the composited output to Firefox Reality on Desktop + async RequestFxrOutput(); + + // Actor that represents one WebGL context. + async PWebGL(); + +child: + // Send back Compositor Frame Metrics from APZCs so tiled layers can + // update progressively. + async SharedCompositorFrameMetrics(Handle metrics, CrossProcessMutexHandle mutex, LayersId aLayersId, uint32_t aAPZCId); + async ReleaseSharedCompositorFrameMetrics(ViewID aId, uint32_t aAPZCId); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh b/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh new file mode 100644 index 0000000000..e1f3a76fdb --- /dev/null +++ b/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh @@ -0,0 +1,20 @@ +/* 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/. */ + +namespace mozilla { +namespace layers { + +struct CollectedFrameParams { + double timeOffset; + uint32_t length; +}; + +struct CollectedFramesParams { + double recordingStart; + CollectedFrameParams[] frames; + Shmem buffer; +}; + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/PCompositorManager.ipdl b/gfx/layers/ipc/PCompositorManager.ipdl new file mode 100644 index 0000000000..93f08e532b --- /dev/null +++ b/gfx/layers/ipc/PCompositorManager.ipdl @@ -0,0 +1,92 @@ +/* -*- 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 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"; +using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; +using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.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; +}; + +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. + */ +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); + +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..73afb55d4a --- /dev/null +++ b/gfx/layers/ipc/PImageBridge.ipdl @@ -0,0 +1,71 @@ +/* -*- 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. + */ +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, LayersBackend aBackend); + async ReleaseCompositable(CompositableHandle aHandle); + + sync MakeAsyncPluginSurfaces(SurfaceFormat format, IntSize size) + returns (SurfaceDescriptorPlugin aSD); + async UpdateAsyncPluginSurface(SurfaceDescriptorPlugin aSD); + sync ReadbackAsyncPluginSurface(SurfaceDescriptorPlugin aSD) + returns (SurfaceDescriptor result); + async RemoveAsyncPluginSurface(SurfaceDescriptorPlugin aSD, bool isFrontSurface); +}; + + +} // namespace +} // namespace + diff --git a/gfx/layers/ipc/PLayerTransaction.ipdl b/gfx/layers/ipc/PLayerTransaction.ipdl new file mode 100644 index 0000000000..ddc50216e5 --- /dev/null +++ b/gfx/layers/ipc/PLayerTransaction.ipdl @@ -0,0 +1,130 @@ +/* -*- 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 protocol PCompositorBridge; +include protocol PTexture; + +include "mozilla/GfxMessageUtils.h"; +include "mozilla/layers/LayersMessageUtils.h"; + +using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h"; +using struct mozilla::void_t from "mozilla/ipc/IPCCore.h"; +using struct mozilla::null_t from "mozilla/ipc/IPCCore.h"; +using class 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 mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h"; +using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.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::LayersObserverEpoch from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h"; + +/** + * The layers protocol is spoken between thread contexts that manage + * layer (sub)trees. The protocol comprises atomically publishing + * layer subtrees to a "shadow" thread context (which grafts the + * subtree into its own tree), and atomically updating a published + * subtree. ("Atomic" in this sense is wrt painting.) + */ + +namespace mozilla { +namespace layers { + +/** + * The PLayerTransaction protocol manages the layer tree for a single "browser". + * The "browser" can be a top-level browser window, in which case the PLayer- + * TransactionChild exists in the UI process. The "browser" can also be a content + * tab, in which case the PLayerTransactionChild exists in the content process. + * In either case, the PLayerTransactionParent exists in the GPU process (if + * there is one) or the UI process otherwise. + */ +sync protocol PLayerTransaction { + manager PCompositorBridge; + +parent: + // The isFirstPaint flag can be used to indicate that this is the first update + // for a particular document. + async Update(TransactionInfo txn); + + async PaintTime(TransactionId id, TimeDuration paintTime); + + async SetLayersObserverEpoch(LayersObserverEpoch aChildEpoch); + + // Create a new Compositable. + async NewCompositable(CompositableHandle handle, TextureInfo info); + + // Release an object that is no longer in use. + async ReleaseLayer(LayerHandle layer); + async ReleaseCompositable(CompositableHandle compositable); + + // Tell the compositor to notify APZ that a layer 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 |OMTAValue| applied to the layer. + sync GetAnimationValue(uint64_t aCompositorAnimationId) returns (OMTAValue value); + + // Returns the value of the transform applied to the layer by animation and + // APZC. + sync GetTransform(LayerHandle layer) returns (Matrix4x4? transform); + + // The next time the layer 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 id, float x, float y); + + // The next time the layer tree is composited, include this async zoom in + // for the given ViewID. + // Useful for testing rendering of async zooming. + sync SetAsyncZoom(ViewID id, float zoom); + + // Flush any pending APZ repaints to the main thread. + async FlushApzRepaints(); + + // Drop any front buffers that might be retained on the compositor + // side. + async ClearCachedResources(); + + // Schedule a composite if one isn't already scheduled. + async ScheduleComposite(); + + // 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); + + // Query a named property from the last frame + sync RequestProperty(nsString property) returns (float value); + + // Return the TextureFactoryIdentifier for this compositor. + sync GetTextureFactoryIdentifier() returns (TextureFactoryIdentifier aIdentifier); + + async RecordPaintTimes(PaintTiming timing); + + async Shutdown(); + sync ShutdownSync(); + +child: + async __delete__(); +}; + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/PTexture.ipdl b/gfx/layers/ipc/PTexture.ipdl new file mode 100644 index 0000000000..b82a757bbc --- /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 PLayerTransaction; +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. + */ +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..515fcfd023 --- /dev/null +++ b/gfx/layers/ipc/PUiCompositorController.ipdl @@ -0,0 +1,46 @@ +/* -*- 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 CSSRect from "Units.h"; +using CSSToScreenScale from "Units.h"; +using ScreenIntSize from "Units.h"; +using 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. + */ +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(); + sync ResumeAndResize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight); + + 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..5a4270981b --- /dev/null +++ b/gfx/layers/ipc/PVideoBridge.ipdl @@ -0,0 +1,33 @@ +/* -*- 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::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. + */ +sync protocol PVideoBridge +{ + manages PTexture; + +parent: + async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend, + TextureFlags aTextureFlags, 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..8e19731230 --- /dev/null +++ b/gfx/layers/ipc/PWebRenderBridge.ipdl @@ -0,0 +1,106 @@ +/* -*- 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::IdNamespace from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::MaybeIdNamespace from "mozilla/webrender/WebRenderTypes.h"; +using mozilla::wr::ExternalImageKeyPair from "mozilla/webrender/WebRenderTypes.h"; +using moveonly mozilla::layers::DisplayListData from "mozilla/layers/RenderRootTypes.h"; +using moveonly 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 { + +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(WebRenderParentCommand[] commands); + sync GetSnapshot(PTexture texture) returns (bool aNeedsYFlip); + async SetLayersObserverEpoch(LayersObserverEpoch childEpoch); + async ClearCachedResources(); + // Invalidate rendered frame + async InvalidateRenderedFrame(); + // Schedule a composite if one isn't already scheduled. + async ScheduleComposite(); + // Save the frame capture to disk + async Capture(); + // Start capturing each frame and save to disk, and if already started, stop. + async ToggleCaptureSequence(); + + // 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(); + + // These correspond exactly to the equivalent APIs in PLayerTransaction - + // see those for documentation. + async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets); + // More copied from PLayerTransaction, but these are only used for testing. + sync SetTestSampleTime(TimeStamp sampleTime); + sync LeaveTestMode(); + sync GetAnimationValue(uint64_t aCompositorAnimationsId) returns (OMTAValue value); + sync SetAsyncScrollOffset(ViewID scrollId, float x, float y); + sync SetAsyncZoom(ViewID scrollId, float zoom); + async FlushApzRepaints(); + sync GetAPZTestData() returns (APZTestData data); + 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..205fa990fd --- /dev/null +++ b/gfx/layers/ipc/RefCountedShmem.cpp @@ -0,0 +1,94 @@ +/* -*- 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 shmType = ipc::SharedMemory::SharedMemoryType::TYPE_BASIC; + auto size = aSize + SHM_REFCOUNT_HEADER_SIZE; + if (!aAllocator->AllocUnsafeShmem(size, shmType, &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..8b110f1397 --- /dev/null +++ b/gfx/layers/ipc/RemoteContentController.cpp @@ -0,0 +1,425 @@ +/* -*- 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) { + if (!mCompositorThread->IsOnCurrentThread()) { + // We have to send messages from the compositor thread + mCompositorThread->Dispatch( + NewRunnableMethod<ScrollableLayerGuid, APZStateChange, int>( + "layers::RemoteContentController::NotifyAPZStateChange", this, + &RemoteContentController::NotifyAPZStateChange, aGuid, aChange, + aArg)); + return; + } + + if (mCanSend) { + Unused << SendNotifyAPZStateChange(aGuid, aChange, aArg); + } +} + +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); + } + } +} + +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); + } + } +} + +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::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..1f18323a3c --- /dev/null +++ b/gfx/layers/ipc/RemoteContentController.h @@ -0,0 +1,115 @@ +/* -*- 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) 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 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); +}; + +} // 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..941ddded1f --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerUtils.h @@ -0,0 +1,42 @@ +/* -*- 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" + +#if defined(MOZ_X11) +# include "mozilla/layers/ShadowLayerUtilsX11.h" +#endif + +namespace IPC { + +#if !defined(MOZ_HAVE_SURFACEDESCRIPTORX11) +template <> +struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> { + typedef mozilla::layers::SurfaceDescriptorX11 paramType; + static void Write(Message*, const paramType&) {} + static bool Read(const Message*, PickleIterator*, paramType*) { + return false; + } +}; +#endif // !defined(MOZ_HAVE_XSURFACEDESCRIPTORX11) + +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/ShadowLayerUtilsMac.cpp b/gfx/layers/ipc/ShadowLayerUtilsMac.cpp new file mode 100644 index 0000000000..891b16e8a2 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerUtilsMac.cpp @@ -0,0 +1,31 @@ +/* -*- 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/gfx/Point.h" +#include "mozilla/layers/PLayerTransaction.h" +#include "mozilla/layers/ShadowLayers.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/CompositorTypes.h" + +#include "gfx2DGlue.h" +#include "gfxPlatform.h" + +using namespace mozilla::gl; + +namespace mozilla { +namespace layers { + +/*static*/ +void ShadowLayerForwarder::PlatformSyncBeforeUpdate() {} + +/*static*/ +void LayerManagerComposite::PlatformSyncBeforeReplyUpdate() {} + +/*static*/ +bool LayerManagerComposite::SupportsDirectTexturing() { return false; } + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp new file mode 100644 index 0000000000..25f6ceba21 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp @@ -0,0 +1,153 @@ +/* -*- 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 "ShadowLayerUtilsX11.h" +#include <X11/X.h> // for Drawable, XID +#include <X11/Xlib.h> // for Display, Visual, etc +#include <X11/extensions/Xrender.h> // for XRenderPictFormat, etc +#include <X11/extensions/render.h> // for PictFormat +#include "cairo-xlib.h" +#include "X11UndefineNone.h" +#include <stdint.h> // for uint32_t +#include "GLDefs.h" // for GLenum +#include "gfxPlatform.h" // for gfxPlatform +#include "gfxXlibSurface.h" // for gfxXlibSurface +#include "gfx2DGlue.h" // for Moz2D transistion helpers +#include "mozilla/X11Util.h" // for DefaultXDisplay, FinishX, etc +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/layers/CompositableForwarder.h" +#include "mozilla/layers/CompositorTypes.h" // for OpenMode +#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator, etc +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayersMessageUtils.h" +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc +#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder, etc +#include "mozilla/mozalloc.h" // for operator new +#include "gfxEnv.h" +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsDebug.h" // for NS_ERROR + +using namespace mozilla::gl; + +namespace mozilla { +namespace gl { +class GLContext; +class TextureImage; +} // namespace gl + +namespace layers { + +// Return true if we're likely compositing using X and so should use +// Xlib surfaces in shadow layers. +static bool UsingXCompositing() { + if (!gfxEnv::LayersEnableXlibSurfaces()) { + return false; + } + return (gfxSurfaceType::Xlib == + gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType()); +} + +// LookReturn a pointer to |aFormat| that lives in the Xrender library. +// All code using render formats assumes it doesn't need to copy. +static XRenderPictFormat* GetXRenderPictFormatFromId(Display* aDisplay, + PictFormat aFormatId) { + XRenderPictFormat tmplate; + tmplate.id = aFormatId; + return XRenderFindFormat(aDisplay, PictFormatID, &tmplate, 0); +} + +SurfaceDescriptorX11::SurfaceDescriptorX11(gfxXlibSurface* aSurf, + bool aForwardGLX) + : mId(aSurf->XDrawable()), mSize(aSurf->GetSize()), mGLXPixmap(X11None) { + const XRenderPictFormat* pictFormat = aSurf->XRenderFormat(); + if (pictFormat) { + mFormat = pictFormat->id; + } else { + mFormat = cairo_xlib_surface_get_visual(aSurf->CairoSurface())->visualid; + } + + if (aForwardGLX) { + mGLXPixmap = aSurf->GetGLXPixmap(); + } +} + +SurfaceDescriptorX11::SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID, + const gfx::IntSize& aSize) + : mId(aDrawable), mFormat(aFormatID), mSize(aSize), mGLXPixmap(X11None) {} + +already_AddRefed<gfxXlibSurface> SurfaceDescriptorX11::OpenForeign() const { + Display* display = DefaultXDisplay(); + if (!display) { + return nullptr; + } + Screen* screen = DefaultScreenOfDisplay(display); + + RefPtr<gfxXlibSurface> surf; + XRenderPictFormat* pictFormat = GetXRenderPictFormatFromId(display, mFormat); + if (pictFormat) { + surf = new gfxXlibSurface(screen, mId, pictFormat, mSize); + } else { + Visual* visual; + int depth; + FindVisualAndDepth(display, mFormat, &visual, &depth); + if (!visual) return nullptr; + + surf = new gfxXlibSurface(display, mId, visual, mSize); + } + + if (mGLXPixmap) surf->BindGLXPixmap(mGLXPixmap); + + return surf->CairoStatus() ? nullptr : surf.forget(); +} + +/*static*/ +void ShadowLayerForwarder::PlatformSyncBeforeUpdate() { + if (UsingXCompositing()) { + // If we're using X surfaces, then we need to finish all pending + // operations on the back buffers before handing them to the + // parent, otherwise the surface might be used by the parent's + // Display in between two operations queued by our Display. + FinishX(DefaultXDisplay()); + } +} + +/*static*/ +void LayerManagerComposite::PlatformSyncBeforeReplyUpdate() { + if (UsingXCompositing()) { + // If we're using X surfaces, we need to finish all pending + // operations on the *front buffers* before handing them back to + // the child, even though they will be read operations. + // Otherwise, the child might start scribbling on new back buffers + // that are still participating in requests as old front buffers. + FinishX(DefaultXDisplay()); + } +} + +/*static*/ +bool LayerManagerComposite::SupportsDirectTexturing() { return false; } + +} // namespace layers +} // namespace mozilla + +namespace IPC { + +void ParamTraits<mozilla::layers::SurfaceDescriptorX11>::Write( + Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mId); + WriteParam(aMsg, aParam.mSize); + WriteParam(aMsg, aParam.mFormat); + WriteParam(aMsg, aParam.mGLXPixmap); +} + +bool ParamTraits<mozilla::layers::SurfaceDescriptorX11>::Read( + const Message* aMsg, PickleIterator* aIter, paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mId) && + ReadParam(aMsg, aIter, &aResult->mSize) && + ReadParam(aMsg, aIter, &aResult->mFormat) && + ReadParam(aMsg, aIter, &aResult->mGLXPixmap)); +} + +} // namespace IPC diff --git a/gfx/layers/ipc/ShadowLayerUtilsX11.h b/gfx/layers/ipc/ShadowLayerUtilsX11.h new file mode 100644 index 0000000000..9cbee37cf1 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerUtilsX11.h @@ -0,0 +1,28 @@ +/* -*- 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_ShadowLayerUtilsX11_h +#define mozilla_layers_ShadowLayerUtilsX11_h + +#include "gfxipc/SurfaceDescriptor.h" +#include "ipc/IPCMessageUtils.h" + +namespace IPC { +class Message; + +template <> +struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> { + typedef mozilla::layers::SurfaceDescriptorX11 paramType; + + static void Write(Message* aMsg, const paramType& aParam); + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult); +}; + +} // namespace IPC + +#endif // mozilla_layers_ShadowLayerUtilsX11_h diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp new file mode 100644 index 0000000000..571a05d491 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -0,0 +1,1074 @@ +/* -*- 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 "ShadowLayers.h" + +#include <set> // for _Rb_tree_const_iterator, etc +#include <vector> // for vector + +#include "ClientLayerManager.h" // for ClientLayerManager +#include "GeckoProfiler.h" // for AUTO_PROFILER_LABEL +#include "IPDLActor.h" +#include "ISurfaceAllocator.h" // for IsSurfaceDescriptorValid +#include "Layers.h" // for Layer +#include "RenderTrace.h" // for RenderTraceScope +#include "gfx2DGlue.h" // for Moz2D transition helpers +#include "gfxPlatform.h" // for gfxImageFormat, gfxPlatform +#include "ipc/IPCMessageUtils.h" // for gfxContentType, null_t +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/layers/CompositableClient.h" // for CompositableClient, etc +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/ContentClient.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/ImageDataSerializer.h" +#include "mozilla/layers/LayerTransactionChild.h" +#include "mozilla/layers/LayersMessages.h" // for Edit, etc +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc +#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG +#include "mozilla/layers/PTextureChild.h" +#include "mozilla/layers/SyncObject.h" +#ifdef XP_DARWIN +# include "mozilla/layers/TextureSync.h" +#endif +#include "ShadowLayerUtils.h" +#include "mozilla/ReentrantMonitor.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/layers/TextureClient.h" // for TextureClient +#include "mozilla/mozalloc.h" // for operator new, etc +#include "nsIXULRuntime.h" // for BrowserTabsRemoteAutostart +#include "nsTArray.h" // for AutoTArray, nsTArray, etc +#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc + +namespace mozilla { +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layers { + +using namespace mozilla::gfx; +using namespace mozilla::gl; +using namespace mozilla::ipc; + +class ClientTiledLayerBuffer; + +typedef nsTArray<SurfaceDescriptor> BufferArray; +typedef nsTArray<Edit> EditVector; +typedef nsTHashtable<nsPtrHashKey<ShadowableLayer>> ShadowableLayerSet; +typedef nsTArray<OpDestroy> OpDestroyVector; + +class Transaction { + public: + Transaction() + : mTargetRotation(ROTATION_0), + mTargetOrientation(hal::eScreenOrientation_None), + mOpen(false), + mRotationChanged(false) {} + + void Begin(const gfx::IntRect& aTargetBounds, ScreenRotation aRotation, + hal::ScreenOrientation aOrientation) { + mOpen = true; + mTargetBounds = aTargetBounds; + if (aRotation != mTargetRotation) { + // the first time this is called, mRotationChanged will be false if + // aRotation is 0, but we should be OK because for the first transaction + // we should only compose if it is non-empty. See the caller(s) of + // RotationChanged. + mRotationChanged = true; + } + mTargetRotation = aRotation; + mTargetOrientation = aOrientation; + } + void AddEdit(const Edit& aEdit) { + MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); + mCset.AppendElement(aEdit); + } + void AddEdit(const CompositableOperation& aEdit) { AddEdit(Edit(aEdit)); } + + void AddNoSwapPaint(const CompositableOperation& aPaint) { + MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); + mPaints.AppendElement(Edit(aPaint)); + } + void AddMutant(ShadowableLayer* aLayer) { + MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); + mMutants.PutEntry(aLayer); + } + void AddSimpleMutant(ShadowableLayer* aLayer) { + MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); + mSimpleMutants.PutEntry(aLayer); + } + void End() { + mCset.Clear(); + mPaints.Clear(); + mMutants.Clear(); + mSimpleMutants.Clear(); + mDestroyedActors.Clear(); + mOpen = false; + mRotationChanged = false; + } + + bool Empty() const { + return mCset.IsEmpty() && mPaints.IsEmpty() && mMutants.IsEmpty() && + mSimpleMutants.IsEmpty() && mDestroyedActors.IsEmpty(); + } + bool RotationChanged() const { return mRotationChanged; } + bool Finished() const { return !mOpen && Empty(); } + + bool Opened() const { return mOpen; } + + EditVector mCset; + nsTArray<CompositableOperation> mPaints; + OpDestroyVector mDestroyedActors; + ShadowableLayerSet mMutants; + ShadowableLayerSet mSimpleMutants; + gfx::IntRect mTargetBounds; + ScreenRotation mTargetRotation; + hal::ScreenOrientation mTargetOrientation; + + private: + bool mOpen; + bool mRotationChanged; + + // disabled + Transaction(const Transaction&); + Transaction& operator=(const Transaction&); +}; +struct AutoTxnEnd final { + explicit AutoTxnEnd(Transaction* aTxn) : mTxn(aTxn) {} + ~AutoTxnEnd() { mTxn->End(); } + Transaction* mTxn; +}; + +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(); +} + +RefPtr<KnowsCompositor> ShadowLayerForwarder::GetForMedia() { + return MakeAndAddRef<KnowsCompositorMediaProxy>( + GetTextureFactoryIdentifier()); +} + +ShadowLayerForwarder::ShadowLayerForwarder( + ClientLayerManager* aClientLayerManager) + : mClientLayerManager(aClientLayerManager), + mThread(NS_GetCurrentThread()), + mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC), + mIsFirstPaint(false), + mNextLayerHandle(1) { + mTxn = new Transaction(); + mEventTarget = GetMainThreadSerialEventTarget(); + + MOZ_ASSERT(mEventTarget || !XRE_IsContentProcess()); + mActiveResourceTracker = MakeUnique<ActiveResourceTracker>( + 1000, "CompositableForwarder", mEventTarget); +} + +template <typename T> +struct ReleaseOnMainThreadTask : public Runnable { + UniquePtr<T> mObj; + + explicit ReleaseOnMainThreadTask(UniquePtr<T>& aObj) + : Runnable("layers::ReleaseOnMainThreadTask"), mObj(std::move(aObj)) {} + + NS_IMETHOD Run() override { + mObj = nullptr; + return NS_OK; + } +}; + +ShadowLayerForwarder::~ShadowLayerForwarder() { + MOZ_ASSERT(mTxn->Finished(), "unfinished transaction?"); + delete mTxn; + if (mShadowManager) { + mShadowManager->SetForwarder(nullptr); + if (NS_IsMainThread()) { + mShadowManager->Destroy(); + } else { + if (mEventTarget) { + mEventTarget->Dispatch( + NewRunnableMethod("LayerTransactionChild::Destroy", mShadowManager, + &LayerTransactionChild::Destroy), + nsIEventTarget::DISPATCH_NORMAL); + } else { + NS_DispatchToMainThread( + NewRunnableMethod("layers::LayerTransactionChild::Destroy", + mShadowManager, &LayerTransactionChild::Destroy)); + } + } + } + + if (!NS_IsMainThread()) { + RefPtr<ReleaseOnMainThreadTask<ActiveResourceTracker>> event = + new ReleaseOnMainThreadTask<ActiveResourceTracker>( + mActiveResourceTracker); + if (mEventTarget) { + mEventTarget->Dispatch(event.forget(), nsIEventTarget::DISPATCH_NORMAL); + } else { + NS_DispatchToMainThread(event); + } + } +} + +void ShadowLayerForwarder::BeginTransaction( + const gfx::IntRect& aTargetBounds, ScreenRotation aRotation, + hal::ScreenOrientation aOrientation) { + MOZ_ASSERT(IPCOpen(), "no manager to forward to"); + MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?"); + UpdateFwdTransactionId(); + mTxn->Begin(aTargetBounds, aRotation, aOrientation); +} + +static const LayerHandle& Shadow(ShadowableLayer* aLayer) { + return aLayer->GetShadow(); +} + +template <typename OpCreateT> +static void CreatedLayer(Transaction* aTxn, ShadowableLayer* aLayer) { + aTxn->AddEdit(OpCreateT(Shadow(aLayer))); +} + +void ShadowLayerForwarder::CreatedPaintedLayer(ShadowableLayer* aThebes) { + CreatedLayer<OpCreatePaintedLayer>(mTxn, aThebes); +} +void ShadowLayerForwarder::CreatedContainerLayer(ShadowableLayer* aContainer) { + CreatedLayer<OpCreateContainerLayer>(mTxn, aContainer); +} +void ShadowLayerForwarder::CreatedImageLayer(ShadowableLayer* aImage) { + CreatedLayer<OpCreateImageLayer>(mTxn, aImage); +} +void ShadowLayerForwarder::CreatedColorLayer(ShadowableLayer* aColor) { + CreatedLayer<OpCreateColorLayer>(mTxn, aColor); +} +void ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas) { + CreatedLayer<OpCreateCanvasLayer>(mTxn, aCanvas); +} +void ShadowLayerForwarder::CreatedRefLayer(ShadowableLayer* aRef) { + CreatedLayer<OpCreateRefLayer>(mTxn, aRef); +} + +void ShadowLayerForwarder::Mutated(ShadowableLayer* aMutant) { + mTxn->AddMutant(aMutant); +} + +void ShadowLayerForwarder::MutatedSimple(ShadowableLayer* aMutant) { + mTxn->AddSimpleMutant(aMutant); +} + +void ShadowLayerForwarder::SetRoot(ShadowableLayer* aRoot) { + mTxn->AddEdit(OpSetRoot(Shadow(aRoot))); +} +void ShadowLayerForwarder::InsertAfter(ShadowableLayer* aContainer, + ShadowableLayer* aChild, + ShadowableLayer* aAfter) { + if (!aChild->HasShadow()) { + return; + } + + while (aAfter && !aAfter->HasShadow()) { + aAfter = aAfter->AsLayer()->GetPrevSibling() + ? aAfter->AsLayer()->GetPrevSibling()->AsShadowableLayer() + : nullptr; + } + + if (aAfter) { + mTxn->AddEdit( + OpInsertAfter(Shadow(aContainer), Shadow(aChild), Shadow(aAfter))); + } else { + mTxn->AddEdit(OpPrependChild(Shadow(aContainer), Shadow(aChild))); + } +} +void ShadowLayerForwarder::RemoveChild(ShadowableLayer* aContainer, + ShadowableLayer* aChild) { + MOZ_LAYERS_LOG(("[LayersForwarder] OpRemoveChild container=%p child=%p\n", + aContainer->AsLayer(), aChild->AsLayer())); + + if (!aChild->HasShadow()) { + return; + } + + mTxn->AddEdit(OpRemoveChild(Shadow(aContainer), Shadow(aChild))); +} +void ShadowLayerForwarder::RepositionChild(ShadowableLayer* aContainer, + ShadowableLayer* aChild, + ShadowableLayer* aAfter) { + if (!aChild->HasShadow()) { + return; + } + + while (aAfter && !aAfter->HasShadow()) { + aAfter = aAfter->AsLayer()->GetPrevSibling() + ? aAfter->AsLayer()->GetPrevSibling()->AsShadowableLayer() + : nullptr; + } + + if (aAfter) { + MOZ_LAYERS_LOG( + ("[LayersForwarder] OpRepositionChild container=%p child=%p after=%p", + aContainer->AsLayer(), aChild->AsLayer(), aAfter->AsLayer())); + mTxn->AddEdit( + OpRepositionChild(Shadow(aContainer), Shadow(aChild), Shadow(aAfter))); + } else { + MOZ_LAYERS_LOG(("[LayersForwarder] OpRaiseToTopChild container=%p child=%p", + aContainer->AsLayer(), aChild->AsLayer())); + mTxn->AddEdit(OpRaiseToTopChild(Shadow(aContainer), Shadow(aChild))); + } +} + +#ifdef DEBUG +void ShadowLayerForwarder::CheckSurfaceDescriptor( + const SurfaceDescriptor* aDescriptor) const { + if (!aDescriptor) { + return; + } + + if (aDescriptor->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer && + aDescriptor->get_SurfaceDescriptorBuffer().data().type() == + MemoryOrShmem::TShmem) { + const Shmem& shmem = + aDescriptor->get_SurfaceDescriptorBuffer().data().get_Shmem(); + shmem.AssertInvariants(); + MOZ_ASSERT(mShadowManager && + mShadowManager->IsTrackingSharedMemory(shmem.mSegment)); + } +} +#endif + +void ShadowLayerForwarder::UseTiledLayerBuffer( + CompositableClient* aCompositable, + const SurfaceDescriptorTiles& aTileLayerDescriptor) { + MOZ_ASSERT(aCompositable); + + if (!aCompositable->IsConnected()) { + return; + } + + mTxn->AddNoSwapPaint( + CompositableOperation(aCompositable->GetIPCHandle(), + OpUseTiledLayerBuffer(aTileLayerDescriptor))); +} + +void ShadowLayerForwarder::UpdateTextureRegion( + CompositableClient* aCompositable, + const ThebesBufferData& aThebesBufferData, + const nsIntRegion& aUpdatedRegion) { + MOZ_ASSERT(aCompositable); + + if (!aCompositable->IsConnected()) { + return; + } + + mTxn->AddNoSwapPaint(CompositableOperation( + aCompositable->GetIPCHandle(), + OpPaintTextureRegion(aThebesBufferData, aUpdatedRegion))); +} + +void ShadowLayerForwarder::UseTextures( + CompositableClient* aCompositable, + const nsTArray<TimedTextureClient>& aTextures) { + MOZ_ASSERT(aCompositable); + + if (!aCompositable->IsConnected()) { + return; + } + + AutoTArray<TimedTexture, 4> textures; + + for (auto& t : aTextures) { + MOZ_ASSERT(t.mTextureClient); + MOZ_ASSERT(t.mTextureClient->GetIPDLActor()); + MOZ_RELEASE_ASSERT(t.mTextureClient->GetIPDLActor()->GetIPCChannel() == + mShadowManager->GetIPCChannel()); + bool readLocked = t.mTextureClient->OnForwardedToHost(); + textures.AppendElement( + TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), t.mTimeStamp, + t.mPictureRect, t.mFrameID, t.mProducerID, readLocked)); + mClientLayerManager->GetCompositorBridgeChild() + ->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient); + + auto fenceFd = t.mTextureClient->GetInternalData()->GetAcquireFence(); + if (fenceFd.IsValid()) { + mTxn->AddEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpDeliverAcquireFence(nullptr, t.mTextureClient->GetIPDLActor(), + fenceFd))); + } + } + mTxn->AddEdit(CompositableOperation(aCompositable->GetIPCHandle(), + OpUseTexture(textures))); +} + +void ShadowLayerForwarder::UseComponentAlphaTextures( + CompositableClient* aCompositable, TextureClient* aTextureOnBlack, + TextureClient* aTextureOnWhite) { + MOZ_ASSERT(aCompositable); + + if (!aCompositable->IsConnected()) { + return; + } + + MOZ_ASSERT(aTextureOnWhite); + MOZ_ASSERT(aTextureOnBlack); + MOZ_ASSERT(aCompositable->GetIPCHandle()); + MOZ_ASSERT(aTextureOnBlack->GetIPDLActor()); + MOZ_ASSERT(aTextureOnWhite->GetIPDLActor()); + MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize()); + MOZ_RELEASE_ASSERT(aTextureOnWhite->GetIPDLActor()->GetIPCChannel() == + mShadowManager->GetIPCChannel()); + MOZ_RELEASE_ASSERT(aTextureOnBlack->GetIPDLActor()->GetIPCChannel() == + mShadowManager->GetIPCChannel()); + + bool readLockedB = aTextureOnBlack->OnForwardedToHost(); + bool readLockedW = aTextureOnWhite->OnForwardedToHost(); + + mClientLayerManager->GetCompositorBridgeChild() + ->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack); + mClientLayerManager->GetCompositorBridgeChild() + ->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite); + + auto fenceFdB = aTextureOnBlack->GetInternalData()->GetAcquireFence(); + if (fenceFdB.IsValid()) { + mTxn->AddEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpDeliverAcquireFence(nullptr, aTextureOnBlack->GetIPDLActor(), + fenceFdB))); + } + + auto fenceFdW = aTextureOnWhite->GetInternalData()->GetAcquireFence(); + if (fenceFdW.IsValid()) { + mTxn->AddEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpDeliverAcquireFence(nullptr, aTextureOnWhite->GetIPDLActor(), + fenceFdW))); + } + + mTxn->AddEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpUseComponentAlphaTextures(nullptr, aTextureOnBlack->GetIPDLActor(), + nullptr, aTextureOnWhite->GetIPDLActor(), + readLockedB, readLockedW))); +} + +static bool AddOpDestroy(Transaction* aTxn, const OpDestroy& op) { + if (!aTxn->Opened()) { + return false; + } + + aTxn->mDestroyedActors.AppendElement(op); + return true; +} + +bool ShadowLayerForwarder::DestroyInTransaction(PTextureChild* aTexture) { + return AddOpDestroy(mTxn, OpDestroy(aTexture)); +} + +bool ShadowLayerForwarder::DestroyInTransaction( + const CompositableHandle& aHandle) { + return AddOpDestroy(mTxn, OpDestroy(aHandle)); +} + +void ShadowLayerForwarder::RemoveTextureFromCompositable( + CompositableClient* aCompositable, TextureClient* aTexture) { + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aTexture); + MOZ_ASSERT(aTexture->GetIPDLActor()); + MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() == + mShadowManager->GetIPCChannel()); + if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) { + // We don't have an actor anymore, don't try to use it! + return; + } + + mTxn->AddEdit(CompositableOperation( + aCompositable->GetIPCHandle(), + OpRemoveTexture(nullptr, aTexture->GetIPDLActor()))); +} + +bool ShadowLayerForwarder::InWorkerThread() { + return GetTextureForwarder()->GetThread()->IsOnCurrentThread(); +} + +void ShadowLayerForwarder::StorePluginWidgetConfigurations( + const nsTArray<nsIWidget::Configuration>& aConfigurations) { + // Cache new plugin widget configs here until we call update, at which + // point this data will get shipped over to chrome. + mPluginWindowData.Clear(); + for (uint32_t idx = 0; idx < aConfigurations.Length(); idx++) { + const nsIWidget::Configuration& configuration = aConfigurations[idx]; + mPluginWindowData.AppendElement( + PluginWindowData(configuration.mWindowID, configuration.mClipRegion, + configuration.mBounds, configuration.mVisible)); + } +} + +void ShadowLayerForwarder::SendPaintTime(TransactionId aId, + TimeDuration aPaintTime) { + if (!IPCOpen() || !mShadowManager->SendPaintTime(aId, aPaintTime)) { + NS_WARNING("Could not send paint times over IPC"); + } +} + +bool ShadowLayerForwarder::EndTransaction( + const nsIntRegion& aRegionToClear, TransactionId aId, + bool aScheduleComposite, uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, const mozilla::VsyncId& aVsyncId, + const mozilla::TimeStamp& aVsyncStart, + const mozilla::TimeStamp& aRefreshStart, + const mozilla::TimeStamp& aTransactionStart, bool aContainsSVG, + const nsCString& aURL, bool* aSent, + const nsTArray<CompositionPayload>& aPayload) { + *aSent = false; + + TransactionInfo info; + + MOZ_ASSERT(IPCOpen(), "no manager to forward to"); + if (!IPCOpen()) { + return false; + } + + Maybe<TimeStamp> startTime; + if (StaticPrefs::layers_acceleration_draw_fps()) { + startTime = Some(TimeStamp::Now()); + } + + GetCompositorBridgeChild()->WillEndTransaction(); + + MOZ_ASSERT(aId.IsValid()); + + AUTO_PROFILER_LABEL("ShadowLayerForwarder::EndTransaction", GRAPHICS); + + RenderTraceScope rendertrace("Foward Transaction", "000091"); + MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?"); + + DiagnosticTypes diagnostics = + gfxPlatform::GetPlatform()->GetLayerDiagnosticTypes(); + if (mDiagnosticTypes != diagnostics) { + mDiagnosticTypes = diagnostics; + mTxn->AddEdit(OpSetDiagnosticTypes(diagnostics)); + } + + AutoTxnEnd _(mTxn); + + if (mTxn->Empty() && !mTxn->RotationChanged()) { + MOZ_LAYERS_LOG( + ("[LayersForwarder] 0-length cset (?) and no rotation event, skipping " + "Update()")); + return true; + } + + if (!mTxn->mPaints.IsEmpty()) { + // With some platforms, telling the drawing backend that there will be no + // more drawing for this frame helps with preventing command queues from + // spanning across multiple frames. + gfxPlatform::GetPlatform()->FlushContentDrawing(); + } + + MOZ_LAYERS_LOG(("[LayersForwarder] destroying buffers...")); + + MOZ_LAYERS_LOG(("[LayersForwarder] building transaction...")); + + nsTArray<OpSetSimpleLayerAttributes> setSimpleAttrs; + for (ShadowableLayerSet::Iterator it(&mTxn->mSimpleMutants); !it.Done(); + it.Next()) { + ShadowableLayer* shadow = it.Get()->GetKey(); + if (!shadow->HasShadow()) { + continue; + } + + Layer* mutant = shadow->AsLayer(); + setSimpleAttrs.AppendElement(OpSetSimpleLayerAttributes( + Shadow(shadow), mutant->GetSimpleAttributes())); + } + + nsTArray<OpSetLayerAttributes> setAttrs; + + // We purposely add attribute-change ops to the final changeset + // before we add paint ops. This allows layers to record the + // attribute changes before new pixels arrive, which can be useful + // for setting up back/front buffers. + RenderTraceScope rendertrace2("Foward Transaction", "000092"); + for (ShadowableLayerSet::Iterator it(&mTxn->mMutants); !it.Done(); + it.Next()) { + ShadowableLayer* shadow = it.Get()->GetKey(); + + if (!shadow->HasShadow()) { + continue; + } + Layer* mutant = shadow->AsLayer(); + MOZ_ASSERT(!!mutant, "unshadowable layer?"); + + OpSetLayerAttributes op; + op.layer() = Shadow(shadow); + + LayerAttributes& attrs = op.attrs(); + CommonLayerAttributes& common = attrs.common(); + common.visibleRegion() = mutant->GetVisibleRegion(); + common.eventRegions() = mutant->GetEventRegions(); + common.useClipRect() = !!mutant->GetClipRect(); + common.clipRect() = + (common.useClipRect() ? *mutant->GetClipRect() : ParentLayerIntRect()); + if (Layer* maskLayer = mutant->GetMaskLayer()) { + common.maskLayer() = Shadow(maskLayer->AsShadowableLayer()); + } else { + common.maskLayer() = LayerHandle(); + } + common.compositorAnimations().id() = mutant->GetCompositorAnimationsId(); + common.compositorAnimations().animations() = + mutant->GetAnimations().Clone(); + common.invalidRegion() = mutant->GetInvalidRegion().GetRegion(); + common.scrollMetadata() = mutant->GetAllScrollMetadata().Clone(); + for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) { + auto layer = + Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer()); + common.ancestorMaskLayers().AppendElement(layer); + } + nsCString log; + mutant->GetDisplayListLog(log); + common.displayListLog() = log; + + attrs.specific() = null_t(); + mutant->FillSpecificAttributes(attrs.specific()); + + MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant)); + + setAttrs.AppendElement(op); + } + + if (mTxn->mCset.IsEmpty() && mTxn->mPaints.IsEmpty() && setAttrs.IsEmpty() && + !mTxn->RotationChanged()) { + return true; + } + + info.cset() = std::move(mTxn->mCset); + info.setSimpleAttrs() = std::move(setSimpleAttrs); + info.setAttrs() = std::move(setAttrs); + info.paints() = std::move(mTxn->mPaints); + info.toDestroy() = mTxn->mDestroyedActors.Clone(); + info.fwdTransactionId() = GetFwdTransactionId(); + info.id() = aId; + info.plugins() = mPluginWindowData.Clone(); + info.isFirstPaint() = mIsFirstPaint; + info.focusTarget() = mFocusTarget; + info.scheduleComposite() = aScheduleComposite; + info.paintSequenceNumber() = aPaintSequenceNumber; + info.isRepeatTransaction() = aIsRepeatTransaction; + info.vsyncId() = aVsyncId; + info.vsyncStart() = aVsyncStart; + info.refreshStart() = aRefreshStart; + info.transactionStart() = aTransactionStart; + info.url() = aURL; + info.containsSVG() = aContainsSVG; +#if defined(ENABLE_FRAME_LATENCY_LOG) + info.fwdTime() = TimeStamp::Now(); +#endif + info.payload() = aPayload.Clone(); + + TargetConfig targetConfig(mTxn->mTargetBounds, mTxn->mTargetRotation, + mTxn->mTargetOrientation, aRegionToClear); + info.targetConfig() = targetConfig; + + if (!GetTextureForwarder()->IsSameProcess()) { + MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send...")); + PlatformSyncBeforeUpdate(); + } + + if (startTime) { + mPaintTiming.serializeMs() = + (TimeStamp::Now() - startTime.value()).ToMilliseconds(); + startTime = Some(TimeStamp::Now()); + } + + // We delay at the last possible minute, to give the paint thread a chance to + // finish. If it does we don't have to delay messages at all. + GetCompositorBridgeChild()->PostponeMessagesIfAsyncPainting(); + + MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction...")); + RenderTraceScope rendertrace3("Forward Transaction", "000093"); + if (!mShadowManager->SendUpdate(info)) { + MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!")); + return false; + } + + if (startTime) { + mPaintTiming.sendMs() = + (TimeStamp::Now() - startTime.value()).ToMilliseconds(); + mShadowManager->SendRecordPaintTimes(mPaintTiming); + } + + *aSent = true; + mIsFirstPaint = false; + mFocusTarget = FocusTarget(); + MOZ_LAYERS_LOG(("[LayersForwarder] ... done")); + return true; +} + +RefPtr<CompositableClient> ShadowLayerForwarder::FindCompositable( + const CompositableHandle& aHandle) { + CompositableClient* client = nullptr; + if (!mCompositables.Get(aHandle.Value(), &client)) { + return nullptr; + } + return client; +} + +void ShadowLayerForwarder::SetLayersObserverEpoch(LayersObserverEpoch aEpoch) { + if (!IPCOpen()) { + return; + } + Unused << mShadowManager->SendSetLayersObserverEpoch(aEpoch); +} + +void ShadowLayerForwarder::UpdateTextureLocks() { +#ifdef XP_DARWIN + if (!IPCOpen()) { + return; + } + + auto compositorBridge = GetCompositorBridgeChild(); + if (compositorBridge) { + auto pid = compositorBridge->OtherPid(); + TextureSync::UpdateTextureLocks(pid); + } +#endif +} + +void ShadowLayerForwarder::SyncTextures(const nsTArray<uint64_t>& aSerials) { +#ifdef XP_DARWIN + if (!IPCOpen()) { + return; + } + + auto compositorBridge = GetCompositorBridgeChild(); + if (compositorBridge) { + auto pid = compositorBridge->OtherPid(); + TextureSync::WaitForTextures(pid, aSerials); + } +#endif +} + +void ShadowLayerForwarder::ReleaseLayer(const LayerHandle& aHandle) { + if (!IPCOpen()) { + return; + } + Unused << mShadowManager->SendReleaseLayer(aHandle); +} + +bool ShadowLayerForwarder::IPCOpen() const { + return HasShadowManager() && mShadowManager->IPCOpen(); +} + +/** + * We bail out when we have no shadow manager. That can happen when the + * layer manager is created by the preallocated process. + * See bug 914843 for details. + */ +LayerHandle ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer) { + return LayerHandle(mNextLayerHandle++); +} + +#if !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS) + +/*static*/ +void ShadowLayerForwarder::PlatformSyncBeforeUpdate() {} + +#endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS) + +void ShadowLayerForwarder::Connect(CompositableClient* aCompositable, + ImageContainer* aImageContainer) { +#ifdef GFX_COMPOSITOR_LOGGING + printf("ShadowLayerForwarder::Connect(Compositable)\n"); +#endif + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(mShadowManager); + if (!IPCOpen()) { + return; + } + + static uint64_t sNextID = 1; + uint64_t id = sNextID++; + + mCompositables.Put(id, aCompositable); + + CompositableHandle handle(id); + aCompositable->InitIPDL(handle); + mShadowManager->SendNewCompositable(handle, aCompositable->GetTextureInfo()); +} + +void ShadowLayerForwarder::Attach(CompositableClient* aCompositable, + ShadowableLayer* aLayer) { + MOZ_ASSERT(aLayer); + MOZ_ASSERT(aCompositable); + mTxn->AddEdit( + OpAttachCompositable(Shadow(aLayer), aCompositable->GetIPCHandle())); +} + +void ShadowLayerForwarder::AttachAsyncCompositable( + const CompositableHandle& aHandle, ShadowableLayer* aLayer) { + MOZ_ASSERT(aLayer); + MOZ_ASSERT(aHandle); + mTxn->AddEdit(OpAttachAsyncCompositable(Shadow(aLayer), aHandle)); +} + +void ShadowLayerForwarder::SetShadowManager( + PLayerTransactionChild* aShadowManager) { + mShadowManager = static_cast<LayerTransactionChild*>(aShadowManager); + mShadowManager->SetForwarder(this); +} + +void ShadowLayerForwarder::StopReceiveAsyncParentMessge() { + if (!IPCOpen()) { + return; + } + mShadowManager->SetForwarder(nullptr); +} + +void ShadowLayerForwarder::ClearCachedResources() { + if (!IPCOpen()) { + return; + } + mShadowManager->SendClearCachedResources(); +} + +void ShadowLayerForwarder::ScheduleComposite() { + if (!IPCOpen()) { + return; + } + mShadowManager->SendScheduleComposite(); +} + +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()); +} + +already_AddRefed<gfx::DrawTarget> GetDrawTargetForDescriptor( + const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend) { + uint8_t* data = GetAddressFromDescriptor(aDescriptor); + auto rgb = + aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor(); + uint32_t stride = ImageDataSerializer::GetRGBStride(rgb); + return gfx::Factory::CreateDrawTargetForData( + gfx::BackendType::CAIRO, data, rgb.size(), stride, rgb.format()); +} + +void DestroySurfaceDescriptor(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(); +} + +bool ShadowLayerForwarder::AllocSurfaceDescriptor(const gfx::IntSize& aSize, + gfxContentType aContent, + SurfaceDescriptor* aBuffer) { + if (!IPCOpen()) { + return false; + } + return AllocSurfaceDescriptorWithCaps(aSize, aContent, DEFAULT_BUFFER_CAPS, + aBuffer); +} + +bool ShadowLayerForwarder::AllocSurfaceDescriptorWithCaps( + const gfx::IntSize& aSize, gfxContentType aContent, uint32_t aCaps, + SurfaceDescriptor* aBuffer) { + if (!IPCOpen()) { + return false; + } + gfx::SurfaceFormat format = + gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aContent); + size_t size = ImageDataSerializer::ComputeRGBBufferSize(aSize, format); + if (!size) { + return false; + } + + MemoryOrShmem bufferDesc; + if (GetTextureForwarder()->IsSameProcess()) { + uint8_t* data = new (std::nothrow) uint8_t[size]; + if (!data) { + return false; + } + GfxMemoryImageReporter::DidAlloc(data); + memset(data, 0, size); + bufferDesc = reinterpret_cast<uintptr_t>(data); + } else { + mozilla::ipc::Shmem shmem; + if (!GetTextureForwarder()->AllocUnsafeShmem(size, OptimalShmemType(), + &shmem)) { + return false; + } + + bufferDesc = std::move(shmem); + } + + // Use an intermediate buffer by default. Skipping the intermediate buffer is + // only possible in certain configurations so let's keep it simple here for + // now. + const bool hasIntermediateBuffer = true; + *aBuffer = SurfaceDescriptorBuffer( + RGBDescriptor(aSize, format, hasIntermediateBuffer), bufferDesc); + + return true; +} + +/* static */ +bool ShadowLayerForwarder::IsShmem(SurfaceDescriptor* aSurface) { + return aSurface && + (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer) && + (aSurface->get_SurfaceDescriptorBuffer().data().type() == + MemoryOrShmem::TShmem); +} + +void ShadowLayerForwarder::DestroySurfaceDescriptor( + SurfaceDescriptor* aSurface) { + MOZ_ASSERT(aSurface); + MOZ_ASSERT(IPCOpen()); + if (!IPCOpen() || !aSurface) { + return; + } + + ::mozilla::layers::DestroySurfaceDescriptor(GetTextureForwarder(), aSurface); +} + +void ShadowLayerForwarder::UpdateFwdTransactionId() { + auto compositorBridge = GetCompositorBridgeChild(); + if (compositorBridge) { + compositorBridge->UpdateFwdTransactionId(); + } +} + +uint64_t ShadowLayerForwarder::GetFwdTransactionId() { + auto compositorBridge = GetCompositorBridgeChild(); + MOZ_DIAGNOSTIC_ASSERT(compositorBridge); + return compositorBridge ? compositorBridge->GetFwdTransactionId() : 0; +} + +CompositorBridgeChild* ShadowLayerForwarder::GetCompositorBridgeChild() { + if (mCompositorBridgeChild) { + return mCompositorBridgeChild; + } + if (!mShadowManager) { + return nullptr; + } + mCompositorBridgeChild = + static_cast<CompositorBridgeChild*>(mShadowManager->Manager()); + return mCompositorBridgeChild; +} + +void ShadowLayerForwarder::SyncWithCompositor() { + auto compositorBridge = GetCompositorBridgeChild(); + if (compositorBridge && compositorBridge->IPCOpen()) { + compositorBridge->SendSyncWithCompositor(); + } +} + +void ShadowLayerForwarder::ReleaseCompositable( + const CompositableHandle& aHandle) { + AssertInForwarderThread(); + if (!DestroyInTransaction(aHandle)) { + if (!IPCOpen()) { + return; + } + mShadowManager->SendReleaseCompositable(aHandle); + } + mCompositables.Remove(aHandle.Value()); +} + +void ShadowLayerForwarder::SynchronouslyShutdown() { + if (IPCOpen()) { + mShadowManager->SendShutdownSync(); + mShadowManager->MarkDestroyed(); + } +} + +ShadowableLayer::~ShadowableLayer() { + if (mShadow) { + mForwarder->ReleaseLayer(GetShadow()); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h new file mode 100644 index 0000000000..055aba63de --- /dev/null +++ b/gfx/layers/ipc/ShadowLayers.h @@ -0,0 +1,481 @@ +/* -*- 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_ShadowLayers_h +#define mozilla_layers_ShadowLayers_h 1 + +#include <stddef.h> // for size_t +#include <stdint.h> // for uint64_t +#include "gfxTypes.h" +#include "mozilla/Attributes.h" // for override +#include "mozilla/gfx/Rect.h" +#include "mozilla/WidgetUtils.h" // for ScreenRotation +#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc +#include "mozilla/HalScreenConfiguration.h" // for ScreenOrientation +#include "mozilla/layers/CompositableForwarder.h" +#include "mozilla/layers/FocusTarget.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/TextureForwarder.h" +#include "mozilla/layers/CompositorTypes.h" // for OpenMode, etc +#include "mozilla/layers/CompositorBridgeChild.h" +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsRegion.h" // for nsIntRegion +#include "nsTArrayForwardDeclare.h" // for nsTArray +#include "nsIWidget.h" +#include <vector> + +namespace mozilla { +namespace layers { + +class ClientLayerManager; +class CompositorBridgeChild; +class FixedSizeSmallShmemSectionAllocator; +class ImageContainer; +class Layer; +class PLayerTransactionChild; +class LayerTransactionChild; +class ShadowableLayer; +class SurfaceDescriptor; +class TextureClient; +class ThebesBuffer; +class ThebesBufferData; +class Transaction; + +/** + * We want to share layer trees across thread contexts and address + * spaces for several reasons; chief among them + * + * - a parent process can paint a child process's layer tree while + * the child process is blocked, say on content script. This is + * important on mobile devices where UI responsiveness is key. + * + * - a dedicated "compositor" process can asynchronously (wrt the + * browser process) composite and animate layer trees, allowing a + * form of pipeline parallelism between compositor/browser/content + * + * - a dedicated "compositor" process can take all responsibility for + * accessing the GPU, which is desirable on systems with + * buggy/leaky drivers because the compositor process can die while + * browser and content live on (and failover mechanisms can be + * installed to quickly bring up a replacement compositor) + * + * The Layers model has a crisply defined API, which makes it easy to + * safely "share" layer trees. The ShadowLayers API extends Layers to + * allow a remote, parent process to access a child process's layer + * tree. + * + * ShadowLayerForwarder publishes a child context's layer tree to a + * parent context. This comprises recording layer-tree modifications + * into atomic transactions and pushing them over IPC. + * + * LayerManagerComposite grafts layer subtrees published by child-context + * ShadowLayerForwarder(s) into a parent-context layer tree. + * + * (Advanced note: because our process tree may have a height >2, a + * non-leaf subprocess may both receive updates from child processes + * and publish them to parent processes. Put another way, + * LayerManagers may be both LayerManagerComposites and + * ShadowLayerForwarders.) + * + * There are only shadow types for layers that have different shadow + * vs. not-shadow behavior. ColorLayers and ContainerLayers behave + * the same way in both regimes (so far). + * + * + * The mecanism to shadow the layer tree on the compositor through IPC works as + * follows: + * The layer tree is managed on the content thread, and shadowed in the + * compositor thread. The shadow layer tree is only kept in sync with whatever + * happens in the content thread. To do this we use IPDL protocols. IPDL is a + * domain specific language that describes how two processes or thread should + * communicate. C++ code is generated from .ipdl files to implement the message + * passing, synchronization and serialization logic. To use the generated code + * we implement classes that inherit the generated IPDL actor. the ipdl actors + * of a protocol PX are PXChild or PXParent (the generated class), and we + * conventionally implement XChild and XParent. The Parent side of the protocol + * is the one that lives on the compositor thread. Think of IPDL actors as + * endpoints of communication. they are useful to send messages and also to + * dispatch the message to the right actor on the other side. One nice property + * of an IPDL actor is that when an actor, say PXChild is sent in a message, the + * PXParent comes out in the other side. we use this property a lot to dispatch + * messages to the right layers and compositable, each of which have their own + * ipdl actor on both side. + * + * Most of the synchronization logic happens in layer transactions and + * compositable transactions. + * A transaction is a set of changes to the layers and/or the compositables + * that are sent and applied together to the compositor thread to keep the + * LayerComposite in a coherent state. + * Layer transactions maintain the shape of the shadow layer tree, and + * synchronize the texture data held by compositables. Layer transactions + * are always between the content thread and the compositor thread. + * Compositable transactions are subset of a layer transaction with which only + * compositables and textures can be manipulated, and does not always originate + * from the content thread. (See CompositableForwarder.h and ImageBridgeChild.h) + */ + +class ShadowLayerForwarder final : public LayersIPCActor, + public CompositableForwarder, + public LegacySurfaceDescriptorAllocator { + friend class ClientLayerManager; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ShadowLayerForwarder, override); + + /** + * Setup the IPDL actor for aCompositable to be part of layers + * transactions. + */ + void Connect(CompositableClient* aCompositable, + ImageContainer* aImageContainer) override; + + /** + * Adds an edit in the layers transaction in order to attach + * the corresponding compositable and layer on the compositor side. + * Connect must have been called on aCompositable beforehand. + */ + void Attach(CompositableClient* aCompositable, ShadowableLayer* aLayer); + + /** + * Adds an edit in the transaction in order to attach a Compositable that + * is not managed by this ShadowLayerForwarder (for example, by ImageBridge + * in the case of async-video). + * Since the compositable is not managed by this forwarder, we can't use + * the compositable or it's IPDL actor here, so we use an ID instead, that + * is matched on the compositor side. + */ + void AttachAsyncCompositable(const CompositableHandle& aHandle, + ShadowableLayer* aLayer); + + /** + * Begin recording a transaction to be forwarded atomically to a + * LayerManagerComposite. + */ + void BeginTransaction(const gfx::IntRect& aTargetBounds, + ScreenRotation aRotation, + hal::ScreenOrientation aOrientation); + + /** + * The following methods may only be called after BeginTransaction() + * but before EndTransaction(). They mirror the LayerManager + * interface in Layers.h. + */ + + /** + * Notify the shadow manager that a new, "real" layer has been + * created, and a corresponding shadow layer should be created in + * the compositing process. + */ + void CreatedPaintedLayer(ShadowableLayer* aThebes); + void CreatedContainerLayer(ShadowableLayer* aContainer); + void CreatedImageLayer(ShadowableLayer* aImage); + void CreatedColorLayer(ShadowableLayer* aColor); + void CreatedCanvasLayer(ShadowableLayer* aCanvas); + void CreatedRefLayer(ShadowableLayer* aRef); + + /** + * At least one attribute of |aMutant| has changed, and |aMutant| + * needs to sync to its shadow layer. This initial implementation + * forwards all attributes when any of the appropriate attribute + * set is mutated. + */ + void Mutated(ShadowableLayer* aMutant); + void MutatedSimple(ShadowableLayer* aMutant); + + void SetRoot(ShadowableLayer* aRoot); + /** + * Insert |aChild| after |aAfter| in |aContainer|. |aAfter| can be + * nullptr to indicated that |aChild| should be appended to the end of + * |aContainer|'s child list. + */ + void InsertAfter(ShadowableLayer* aContainer, ShadowableLayer* aChild, + ShadowableLayer* aAfter = nullptr); + void RemoveChild(ShadowableLayer* aContainer, ShadowableLayer* aChild); + void RepositionChild(ShadowableLayer* aContainer, ShadowableLayer* aChild, + ShadowableLayer* aAfter = nullptr); + + /** + * Set aMaskLayer as the mask on aLayer. + * Note that only image layers are properly supported + * LayerTransactionParent::UpdateMask and accompanying ipdl + * will need changing to update properties for other kinds + * of mask layer. + */ + void SetMask(ShadowableLayer* aLayer, ShadowableLayer* aMaskLayer); + + /** + * See CompositableForwarder::UseTiledLayerBuffer + */ + void UseTiledLayerBuffer( + CompositableClient* aCompositable, + const SurfaceDescriptorTiles& aTileLayerDescriptor) override; + + void ReleaseCompositable(const CompositableHandle& aHandle) override; + bool DestroyInTransaction(PTextureChild* aTexture) override; + bool DestroyInTransaction(const CompositableHandle& aHandle); + + void RemoveTextureFromCompositable(CompositableClient* aCompositable, + TextureClient* aTexture) override; + + /** + * Communicate to the compositor that aRegion in the texture identified by + * aLayer and aIdentifier has been updated to aThebesBuffer. + */ + void UpdateTextureRegion(CompositableClient* aCompositable, + const ThebesBufferData& aThebesBufferData, + const nsIntRegion& aUpdatedRegion) override; + + /** + * See CompositableForwarder::UseTextures + */ + void UseTextures(CompositableClient* aCompositable, + const nsTArray<TimedTextureClient>& aTextures) override; + void UseComponentAlphaTextures(CompositableClient* aCompositable, + TextureClient* aClientOnBlack, + TextureClient* aClientOnWhite) override; + + /** + * Used for debugging to tell the compositor how long this frame took to + * paint. + */ + void SendPaintTime(TransactionId aId, TimeDuration aPaintTime); + + /** + * End the current transaction and forward it to LayerManagerComposite. + * |aReplies| are directions from the LayerManagerComposite to the + * caller of EndTransaction(). + */ + bool EndTransaction(const nsIntRegion& aRegionToClear, TransactionId aId, + bool aScheduleComposite, uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + const mozilla::VsyncId& aVsyncId, + const mozilla::TimeStamp& aVsyncTime, + const mozilla::TimeStamp& aRefreshStart, + const mozilla::TimeStamp& aTransactionStart, + bool aContainsSVG, const nsCString& aURL, bool* aSent, + const nsTArray<CompositionPayload>& aPayload = + nsTArray<CompositionPayload>()); + + /** + * Set an actor through which layer updates will be pushed. + */ + void SetShadowManager(PLayerTransactionChild* aShadowManager); + + /** + * Layout calls here to cache current plugin widget configuration + * data. We ship this across with the rest of the layer updates when + * we update. Chrome handles applying these changes. + */ + void StorePluginWidgetConfigurations( + const nsTArray<nsIWidget::Configuration>& aConfigurations); + + void StopReceiveAsyncParentMessge(); + + void ClearCachedResources(); + + void ScheduleComposite(); + + /** + * True if this is forwarding to a LayerManagerComposite. + */ + bool HasShadowManager() const { return !!mShadowManager; } + LayerTransactionChild* GetShadowManager() const { + return mShadowManager.get(); + } + + // Send a synchronous message asking the LayerTransactionParent in the + // compositor to shutdown. + void SynchronouslyShutdown(); + + /** + * The following Alloc/Open/Destroy interfaces abstract over the + * details of working with surfaces that are shared across + * processes. They provide the glue between C++ Layers and the + * LayerComposite IPC system. + * + * The basic lifecycle is + * + * - a Layer needs a buffer. Its ShadowableLayer subclass calls + * AllocBuffer(), then calls one of the Created*Buffer() methods + * above to transfer the (temporary) front buffer to its + * LayerComposite in the other process. The Layer needs a + * gfxASurface to paint, so the ShadowableLayer uses + * OpenDescriptor(backBuffer) to get that surface, and hands it + * out to the Layer. + * + * - a Layer has painted new pixels. Its ShadowableLayer calls one + * of the Painted*Buffer() methods above with the back buffer + * descriptor. This notification is forwarded to the LayerComposite, + * which uses OpenDescriptor() to access the newly-painted pixels. + * The LayerComposite then updates its front buffer in a Layer- and + * platform-dependent way, and sends a surface descriptor back to + * the ShadowableLayer that becomes its new back back buffer. + * + * - a Layer wants to destroy its buffers. Its ShadowableLayer + * calls Destroyed*Buffer(), which gives up control of the back + * buffer descriptor. The actual back buffer surface is then + * destroyed using DestroySharedSurface() just before notifying + * the parent process. When the parent process is notified, the + * LayerComposite also calls DestroySharedSurface() on its front + * buffer, and the double-buffer pair is gone. + */ + + bool IPCOpen() const override; + + /** + * Construct a shadow of |aLayer| on the "other side", at the + * LayerManagerComposite. + */ + LayerHandle ConstructShadowFor(ShadowableLayer* aLayer); + + /** + * Flag the next paint as the first for a document. + */ + void SetIsFirstPaint() { mIsFirstPaint = true; } + bool GetIsFirstPaint() const { return mIsFirstPaint; } + + /** + * Set the current focus target to be sent with the next paint. + */ + void SetFocusTarget(const FocusTarget& aFocusTarget) { + mFocusTarget = aFocusTarget; + } + + void SetLayersObserverEpoch(LayersObserverEpoch aEpoch); + + static void PlatformSyncBeforeUpdate(); + + bool AllocSurfaceDescriptor(const gfx::IntSize& aSize, + gfxContentType aContent, + SurfaceDescriptor* aBuffer) override; + + bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize, + gfxContentType aContent, uint32_t aCaps, + SurfaceDescriptor* aBuffer) override; + + void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) override; + + void UpdateFwdTransactionId() override; + uint64_t GetFwdTransactionId() override; + + void UpdateTextureLocks(); + void SyncTextures(const nsTArray<uint64_t>& aSerials); + + void ReleaseLayer(const LayerHandle& aHandle); + + bool InForwarderThread() override { return NS_IsMainThread(); } + + PaintTiming& GetPaintTiming() { return mPaintTiming; } + + ShadowLayerForwarder* AsLayerForwarder() override { return this; } + + // Returns true if aSurface wraps a Shmem. + static bool IsShmem(SurfaceDescriptor* aSurface); + + void SyncWithCompositor() override; + + TextureForwarder* GetTextureForwarder() override { + return GetCompositorBridgeChild(); + } + LayersIPCActor* GetLayersIPCActor() override { return this; } + + ActiveResourceTracker* GetActiveResourceTracker() override { + return mActiveResourceTracker.get(); + } + + CompositorBridgeChild* GetCompositorBridgeChild(); + + nsISerialEventTarget* GetEventTarget() { return mEventTarget; }; + + bool IsThreadSafe() const override { return false; } + + RefPtr<KnowsCompositor> GetForMedia() override; + + protected: + virtual ~ShadowLayerForwarder(); + + explicit ShadowLayerForwarder(ClientLayerManager* aClientLayerManager); + +#ifdef DEBUG + void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const; +#else + void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const {} +#endif + + RefPtr<CompositableClient> FindCompositable( + const CompositableHandle& aHandle); + + bool InWorkerThread(); + + RefPtr<LayerTransactionChild> mShadowManager; + RefPtr<CompositorBridgeChild> mCompositorBridgeChild; + + private: + ClientLayerManager* mClientLayerManager; + Transaction* mTxn; + nsCOMPtr<nsISerialEventTarget> mThread; + DiagnosticTypes mDiagnosticTypes; + bool mIsFirstPaint; + FocusTarget mFocusTarget; + nsTArray<PluginWindowData> mPluginWindowData; + UniquePtr<ActiveResourceTracker> mActiveResourceTracker; + uint64_t mNextLayerHandle; + nsDataHashtable<nsUint64HashKey, CompositableClient*> mCompositables; + PaintTiming mPaintTiming; + /** + * ShadowLayerForwarder might dispatch tasks to main while puppet widget and + * browserChild don't exist anymore; therefore we hold the event target since + * its lifecycle is independent of these objects. + */ + nsCOMPtr<nsISerialEventTarget> mEventTarget; +}; + +class CompositableClient; + +/** + * A ShadowableLayer is a Layer can be shared with a parent context + * through a ShadowLayerForwarder. A ShadowableLayer maps to a + * Shadow*Layer in a parent context. + * + * Note that ShadowLayers can themselves be ShadowableLayers. + */ +class ShadowableLayer { + public: + virtual ~ShadowableLayer(); + + virtual Layer* AsLayer() = 0; + + /** + * True if this layer has a shadow in a parent process. + */ + bool HasShadow() { return mShadow.IsValid(); } + + /** + * Return the IPC handle to a Shadow*Layer referring to this if one + * exists, nullptr if not. + */ + const LayerHandle& GetShadow() { return mShadow; } + + void SetShadow(ShadowLayerForwarder* aForwarder, const LayerHandle& aShadow) { + MOZ_ASSERT(!mShadow, "can't have two shadows (yet)"); + mForwarder = aForwarder; + mShadow = aShadow; + } + + virtual CompositableClient* GetCompositableClient() { return nullptr; } + + protected: + ShadowableLayer() = default; + + private: + RefPtr<ShadowLayerForwarder> mForwarder; + LayerHandle mShadow; +}; + +} // namespace layers +} // namespace mozilla + +#endif // ifndef mozilla_layers_ShadowLayers_h diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp new file mode 100644 index 0000000000..8beec0f02d --- /dev/null +++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp @@ -0,0 +1,179 @@ +/* -*- 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 (through Allocate(aData)) + // allocate it. This code path is slower than the one used when Allocate has + // been called since it will trigger a full copy. + PlanarYCbCrData data = aData; + if (!mTextureClient && !Allocate(data)) { + 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::Allocate(PlanarYCbCrData& aData) { + MOZ_ASSERT(!mTextureClient, "This image already has allocated data"); + + TextureFlags flags = + mCompositable ? mCompositable->GetTextureFlags() : TextureFlags::DEFAULT; + { + YCbCrTextureClientAllocationHelper helper(aData, 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"); + } + + aData.mYChannel = mapped.y.data; + aData.mCbChannel = mapped.cb.data; + aData.mCrChannel = mapped.cr.data; + + // copy some of aData's values in mData (most of them) + mData.mYChannel = aData.mYChannel; + mData.mCbChannel = aData.mCbChannel; + mData.mCrChannel = aData.mCrChannel; + mData.mYSize = aData.mYSize; + mData.mCbCrSize = aData.mCbCrSize; + mData.mPicX = aData.mPicX; + mData.mPicY = aData.mPicY; + mData.mPicSize = aData.mPicSize; + mData.mStereoMode = aData.mStereoMode; + mData.mYUVColorSpace = aData.mYUVColorSpace; + mData.mColorDepth = aData.mColorDepth; + // 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( + mData.mYSize, mData.mYStride, mData.mCbCrSize, mData.mCbCrStride); + mSize = mData.mPicSize; + mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY); + + 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..6e99322268 --- /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 Allocate(PlanarYCbCrData& aData); + + 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..c7cc028109 --- /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 "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..55ece9abdf --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesChild.cpp @@ -0,0 +1,657 @@ +/* -*- 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/gfx/gfxVars.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() || !gfxVars::UseWebRender())) { + // 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. + auto pid = manager->OtherPid(); + if (pid == base::GetCurrentProcId()) { + 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->ShareToProcess(pid, 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->ShareToProcess(pid, 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, 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(ImageContainer* aContainer, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::ImageKey& aKey, + ContainerProducerID aProducerId) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aContainer); + MOZ_ASSERT(aManager); + + if (aContainer->IsAsync()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + AutoTArray<ImageContainer::OwningImage, 4> images; + aContainer->GetCurrentImages(&images); + if (images.IsEmpty()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (aProducerId != kContainerProducerID_Invalid && + images[0].mProducerID != aProducerId) { + // If the producer ID of the surface in the container does not match the + // expected producer ID, then we do not want to proceed with sharing. This + // is useful for when callers are unsure if given container is for the same + // producer / underlying image request. + return NS_ERROR_FAILURE; + } + + RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface(); + if (!surface) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + auto sharedSurface = AsSourceSurfaceSharedData(surface); + if (!sharedSurface) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + SharedSurfacesAnimation* anim = aContainer->GetSharedSurfacesAnimation(); + if (anim) { + return anim->UpdateKey(sharedSurface, aManager, aResources, aKey); + } + + 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()); +} + +/* static */ +nsresult SharedSurfacesChild::UpdateAnimation(ImageContainer* aContainer, + SourceSurface* aSurface, + const IntRect& aDirtyRect) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aContainer); + MOZ_ASSERT(!aContainer->IsAsync()); + MOZ_ASSERT(aSurface); + + // If we aren't using shared surfaces, then is nothing to do. + auto sharedSurface = SharedSurfacesChild::AsSourceSurfaceSharedData(aSurface); + if (!sharedSurface) { + MOZ_ASSERT(!aContainer->GetSharedSurfacesAnimation()); + return NS_ERROR_NOT_IMPLEMENTED; + } + + SharedSurfacesAnimation* anim = aContainer->EnsureSharedSurfacesAnimation(); + MOZ_ASSERT(anim); + + return anim->SetCurrentFrame(sharedSurface, aDirtyRect); +} + +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..cc0f2fc2f5 --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesChild.h @@ -0,0 +1,267 @@ +/* -*- 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 ImageContainer; +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); + + /** + * Request that the first surface in the image container's current images be + * mapped into the compositor thread's memory space, and a valid ImageKey be + * generated for it for use with WebRender. If a different method should be + * used to share the image data for this particular container, it will return + * NS_ERROR_NOT_IMPLEMENTED. This must be called from the main thread. + */ + static nsresult Share(ImageContainer* aContainer, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::ImageKey& aKey, ContainerProducerID aProducerId); + + /** + * 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); + + static nsresult UpdateAnimation(ImageContainer* aContainer, + gfx::SourceSurface* aSurface, + const gfx::IntRect& aDirtyRect); + + 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..57d9edddb7 --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesMemoryReport.h @@ -0,0 +1,60 @@ +/* -*- 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(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mSurfaces); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &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..844e3d83b3 --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesParent.cpp @@ -0,0 +1,257 @@ +/* -*- 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/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/RenderSharedSurfaceTextureHostSWGL.h" +#include "mozilla/webrender/RenderThread.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +StaticMutex SharedSurfacesParent::sMutex; +StaticAutoPtr<SharedSurfacesParent> SharedSurfacesParent::sInstance; + +SharedSurfacesParent::SharedSurfacesParent() = default; + +SharedSurfacesParent::~SharedSurfacesParent() { + for (auto i = mSurfaces.Iter(); !i.Done(); i.Next()) { + // 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(i.Key()); + } +} + +/* static */ +void SharedSurfacesParent::Initialize() { + MOZ_ASSERT(NS_IsMainThread()); + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + sInstance = new SharedSurfacesParent(); + } +} + +/* static */ +void SharedSurfacesParent::Shutdown() { + // 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()); + StaticMutexAutoLock lock(sMutex); + sInstance = nullptr; +} + +/* static */ +already_AddRefed<DataSourceSurface> SharedSurfacesParent::Get( + const wr::ExternalImageId& aId) { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd"; + return nullptr; + } + + RefPtr<SourceSurfaceSharedDataWrapper> surface; + sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); + return surface.forget(); +} + +/* static */ +already_AddRefed<DataSourceSurface> SharedSurfacesParent::Acquire( + const wr::ExternalImageId& aId) { + StaticMutexAutoLock lock(sMutex); + 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) { + StaticMutexAutoLock lock(sMutex); + 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)) { + wr::RenderThread::Get()->UnregisterExternalImage(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()); + StaticMutexAutoLock lock(sMutex); + 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)); + + RefPtr<wr::RenderTextureHost> texture; + if (gfx::gfxVars::UseSoftwareWebRender()) { + texture = new wr::RenderSharedSurfaceTextureHostSWGL(surface); + } else { + texture = new wr::RenderSharedSurfaceTextureHost(surface); + } + wr::RenderThread::Get()->RegisterExternalImage(id, texture.forget()); + + surface->AddConsumer(); + sInstance->mSurfaces.Put(id, std::move(surface)); +} + +/* static */ +void SharedSurfacesParent::DestroyProcess(base::ProcessId aPid) { + StaticMutexAutoLock lock(sMutex); + 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)) { + wr::RenderThread::Get()->UnregisterExternalImage(i.Key()); + i.Remove(); + } + } +} + +/* static */ +void SharedSurfacesParent::Add(const wr::ExternalImageId& aId, + const SurfaceDescriptorShared& aDesc, + base::ProcessId aPid) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(aPid != base::GetCurrentProcId()); + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " shtd"; + return; + } + + // Note that the surface wrapper maps in the given handle as read only. + RefPtr<SourceSurfaceSharedDataWrapper> surface = + new SourceSurfaceSharedDataWrapper(); + if (NS_WARN_IF(!surface->Init(aDesc.size(), aDesc.stride(), aDesc.format(), + aDesc.handle(), aPid))) { + gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " init"; + return; + } + + uint64_t id = wr::AsUint64(aId); + MOZ_ASSERT(!sInstance->mSurfaces.Contains(id)); + + RefPtr<wr::RenderTextureHost> texture; + if (gfx::gfxVars::UseSoftwareWebRender()) { + texture = new wr::RenderSharedSurfaceTextureHostSWGL(surface); + } else { + texture = new wr::RenderSharedSurfaceTextureHost(surface); + } + wr::RenderThread::Get()->RegisterExternalImage(id, texture.forget()); + + surface->AddConsumer(); + sInstance->mSurfaces.Put(id, std::move(surface)); +} + +/* static */ +void SharedSurfacesParent::Remove(const wr::ExternalImageId& aId) { + DebugOnly<bool> rv = Release(aId, /* aForCreator */ true); + MOZ_ASSERT(rv); +} + +/* static */ +void SharedSurfacesParent::AccumulateMemoryReport( + base::ProcessId aPid, SharedSurfacesMemoryReport& aReport) { + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + return; + } + + for (auto i = sInstance->mSurfaces.ConstIter(); !i.Done(); i.Next()) { + SourceSurfaceSharedDataWrapper* surface = i.Data(); + if (surface->GetCreatorPid() == aPid) { + aReport.mSurfaces.insert(std::make_pair( + i.Key(), 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() != -1) { + return false; + } + } else if (!XRE_IsGPUProcess()) { + return false; + } + + StaticMutexAutoLock lock(sMutex); + if (!sInstance) { + return true; + } + + for (auto i = sInstance->mSurfaces.ConstIter(); !i.Done(); i.Next()) { + SourceSurfaceSharedDataWrapper* surface = i.Data(); + aReport.mSurfaces.insert(std::make_pair( + i.Key(), + 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..a990c1b19d --- /dev/null +++ b/gfx/layers/ipc/SharedSurfacesParent.h @@ -0,0 +1,82 @@ +/* -*- 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/StaticMutex.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/webrender/WebRenderTypes.h" // for wr::ExternalImageId +#include "nsRefPtrHashtable.h" + +namespace mozilla { +namespace gfx { +class DataSourceSurface; +class SourceSurfaceSharedData; +class SourceSurfaceSharedDataWrapper; +} // namespace gfx + +namespace layers { + +class SharedSurfacesChild; +class SharedSurfacesMemoryReport; + +class SharedSurfacesParent final { + public: + static void Initialize(); + 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, + const 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); + + ~SharedSurfacesParent(); + + private: + friend class SharedSurfacesChild; + + SharedSurfacesParent(); + + static void AddSameProcess(const wr::ExternalImageId& aId, + gfx::SourceSurfaceSharedData* aSurface); + + static StaticMutex sMutex; + + static StaticAutoPtr<SharedSurfacesParent> sInstance; + + nsRefPtrHashtable<nsUint64HashKey, gfx::SourceSurfaceSharedDataWrapper> + mSurfaces; +}; + +} // 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..17ee00c6ae --- /dev/null +++ b/gfx/layers/ipc/SurfaceDescriptor.h @@ -0,0 +1,71 @@ +/* -*- 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 + +#if defined(MOZ_X11) +# include "mozilla/AlreadyAddRefed.h" +# include "mozilla/gfx/Point.h" + +# define MOZ_HAVE_SURFACEDESCRIPTORX11 +# define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS + +typedef unsigned long XID; +typedef XID Drawable; + +class gfxXlibSurface; + +namespace mozilla { +namespace layers { + +struct SurfaceDescriptorX11 { + SurfaceDescriptorX11() = default; + + explicit SurfaceDescriptorX11(gfxXlibSurface* aSurf, + bool aForwardGLX = false); + + SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID, + const gfx::IntSize& aSize); + + // Default copy ctor and operator= are OK + + bool operator==(const SurfaceDescriptorX11& aOther) const { + // Define == as two descriptors having the same XID for now, + // ignoring size and render format. If the two indeed refer to + // the same valid XID, then size/format are "actually" the same + // anyway, regardless of the values of the fields in + // SurfaceDescriptorX11. + return mId == aOther.mId; + } + + already_AddRefed<gfxXlibSurface> OpenForeign() const; + + MOZ_INIT_OUTSIDE_CTOR Drawable mId; + MOZ_INIT_OUTSIDE_CTOR XID mFormat; // either a PictFormat or VisualID + MOZ_INIT_OUTSIDE_CTOR gfx::IntSize mSize; + MOZ_INIT_OUTSIDE_CTOR Drawable + mGLXPixmap; // used to prevent multiple bindings to the same GLXPixmap + // in-process +}; + +} // namespace layers +} // namespace mozilla +#else +namespace mozilla { +namespace layers { +struct SurfaceDescriptorX11 { + bool operator==(const SurfaceDescriptorX11&) const { return false; } +}; +} // namespace layers +} // namespace mozilla +#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..fc6acda81b --- /dev/null +++ b/gfx/layers/ipc/SynchronousTask.h @@ -0,0 +1,55 @@ +/* -*- 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) {} + + void Wait() { + while (!mDone) { + mMonitor.Wait(); + } + } + + private: + void Complete() { + mDone = true; + mMonitor.NotifyAll(); + } + + private: + ReentrantMonitor mMonitor; + 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..bbd9b72153 --- /dev/null +++ b/gfx/layers/ipc/TextureForwarder.h @@ -0,0 +1,91 @@ +/* -*- 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/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, const ReadLockDescriptor& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId, + nsISerialEventTarget* aTarget = nullptr) = 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..fc12c61641 --- /dev/null +++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp @@ -0,0 +1,322 @@ +/* -*- 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/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) + +static bool IsOnUiThread() { return GetUiThread()->IsOnCurrentThread(); } + +namespace mozilla { +namespace layers { + +// public: +/* static */ +RefPtr<UiCompositorControllerChild> +UiCompositorControllerChild::CreateForSameProcess( + const LayersId& aRootLayerTreeId) { + RefPtr<UiCompositorControllerChild> child = + new UiCompositorControllerChild(0); + 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) { + RefPtr<UiCompositorControllerChild> child = + new UiCompositorControllerChild(aProcessToken); + + 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; + } + return SendResume(); +} + +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; + } + return SendResumeAndResize(aX, aY, aWidth, aHeight); +} + +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() { + if (!IsOnUiThread()) { + GetUiThread()->Dispatch( + NewRunnableMethod("layers::UiCompositorControllerChild::Destroy", this, + &UiCompositorControllerChild::Destroy), + nsIThread::DISPATCH_SYNC); + return; + } + + 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; + } +} + +void UiCompositorControllerChild::SetBaseWidget(nsBaseWidget* aWidget) { + mWidget = aWidget; +} + +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::ActorDealloc() { + if (mParent) { + mParent = nullptr; + } + Release(); +} + +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) const { + 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) + : mIsOpen(false), mProcessToken(aProcessToken), mWidget(nullptr) {} + +UiCompositorControllerChild::~UiCompositorControllerChild() = default; + +void UiCompositorControllerChild::OpenForSameProcess() { + MOZ_ASSERT(IsOnUiThread()); + + mIsOpen = Open(mParent->GetIPCChannel(), mozilla::layers::CompositorThread(), + mozilla::ipc::ChildSide); + + if (!mIsOpen) { + mParent = nullptr; + return; + } + + mParent->InitializeForSameProcess(); + AddRef(); + SendCachedValues(); + // Let Ui thread know the connection is open; + RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN); +} + +void UiCompositorControllerChild::OpenForGPUProcess( + Endpoint<PUiCompositorControllerChild>&& aEndpoint) { + MOZ_ASSERT(IsOnUiThread()); + + 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; + } + + AddRef(); + SendCachedValues(); + // Let Ui thread know the connection is open; + RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN); +} + +void UiCompositorControllerChild::SendCachedValues() { + MOZ_ASSERT(mIsOpen); + if (mResize) { + SendResumeAndResize(mResize.ref().x, mResize.ref().y, mResize.ref().width, + mResize.ref().height); + mResize.reset(); + } + if (mMaxToolbarHeight) { + SendMaxToolbarHeight(mMaxToolbarHeight.ref()); + mMaxToolbarHeight.reset(); + } + if (mDefaultClearColor) { + SendDefaultClearColor(mDefaultClearColor.ref()); + mDefaultClearColor.reset(); + } + if (mLayerUpdateEnabled) { + SendEnableLayerUpdateNotifications(mLayerUpdateEnabled.ref()); + mLayerUpdateEnabled.reset(); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/UiCompositorControllerChild.h b/gfx/layers/ipc/UiCompositorControllerChild.h new file mode 100644 index 0000000000..fb822eaddf --- /dev/null +++ b/gfx/layers/ipc/UiCompositorControllerChild.h @@ -0,0 +1,87 @@ +/* -*- 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" + +class nsBaseWidget; + +namespace mozilla { +namespace layers { + +class UiCompositorControllerChild final + : protected PUiCompositorControllerChild { + friend class PUiCompositorControllerChild; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerChild) + + static RefPtr<UiCompositorControllerChild> CreateForSameProcess( + const LayersId& aRootLayerTreeId); + static RefPtr<UiCompositorControllerChild> CreateForGPUProcess( + const uint64_t& aProcessToken, + Endpoint<PUiCompositorControllerChild>&& aEndpoint); + + 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(); + + void SetBaseWidget(nsBaseWidget* aWidget); + bool DeallocPixelBuffer(Shmem& aMem); + + protected: + void ActorDestroy(ActorDestroyReason aWhy) override; + void ActorDealloc() override; + void ProcessingError(Result aCode, const char* aReason) override; + void HandleFatalError(const char* aMsg) const 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); + 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; +}; + +} // 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..9cf60df6e1 --- /dev/null +++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp @@ -0,0 +1,305 @@ +/* -*- 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/layers/AsyncCompositionManager.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/LayerManagerComposite.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() { + CompositorBridgeParent* parent = + CompositorBridgeParent::GetCompositorBridgeParentFromLayersId( + mRootLayerTreeId); + if (parent) { + 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) { + CompositorBridgeParent* parent = + CompositorBridgeParent::GetCompositorBridgeParentFromLayersId( + mRootLayerTreeId); + if (parent) { + // Front-end expects a first paint callback upon resume/resize. + parent->ForceIsFirstPaint(); + parent->ResumeCompositionAndResize(aX, aY, aWidth, aHeight); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult +UiCompositorControllerParent::RecvInvalidateAndRender() { + CompositorBridgeParent* parent = + CompositorBridgeParent::GetCompositorBridgeParentFromLayersId( + mRootLayerTreeId); + if (parent) { + parent->Invalidate(); + parent->ScheduleComposition(); + } + 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->mLayerManager) { + Compositor* compositor = state->mLayerManager->GetCompositor(); + if (compositor) { + // Android Color is ARGB which is apparently unusual. + compositor->SetDefaultClearColor( + gfx::DeviceColor::UnusualFromARGB(aColor)); + } + } else 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->mLayerManager && state->mParent) { + state->mLayerManager->RequestScreenPixels(this); + state->mParent->Invalidate(); + state->mParent->ScheduleComposition(); + } else if (state && state->mWrBridge) { + state->mWrBridge->RequestScreenPixels(this); + state->mWrBridge->ScheduleForcedGenerateFrame(); + } +#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) {} + +void UiCompositorControllerParent::ActorDealloc() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + Shutdown(); + Release(); // For AddRef in Initialize() +} + +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, ipc::SharedMemory::TYPE_BASIC, 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) + CSSToScreenScale scale = ViewTargetAs<ScreenPixel>( + aMetrics.mZoom.ToScaleFactor(), + 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()) { + 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()); + AddRef(); + 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..2093a92c10 --- /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) + + 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(); + mozilla::ipc::IPCResult RecvResumeAndResize(const int32_t& aX, + const int32_t& aY, + const int32_t& aHeight, + const int32_t& aWidth); + 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; + void ActorDealloc() 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..d417442919 --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeChild.cpp @@ -0,0 +1,184 @@ +/* -*- 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 "transport/runnable_utils.h" +#include "SynchronousTask.h" + +namespace mozilla { +namespace layers { + +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); +} + +void VideoBridgeChild::Open(Endpoint<PVideoBridgeChild>&& aEndpoint) { + 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() { + if (sVideoBridge) { + sVideoBridge->Close(); + sVideoBridge = nullptr; + } +} + +VideoBridgeChild::VideoBridgeChild() + : mIPDLSelfRef(this), + mThread(GetCurrentSerialEventTarget()), + mCanSend(true) {} + +VideoBridgeChild::~VideoBridgeChild() = default; + +VideoBridgeChild* VideoBridgeChild::GetSingleton() { return sVideoBridge; } + +bool VideoBridgeChild::AllocUnsafeShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (!mThread->IsOnCurrentThread()) { + return DispatchAllocShmemInternal(aSize, aType, aShmem, + true); // true: unsafe + } + + if (!CanSend()) { + return false; + } + + return PVideoBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem); +} + +bool VideoBridgeChild::AllocShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + MOZ_ASSERT(CanSend()); + return PVideoBridgeChild::AllocShmem(aSize, aType, aShmem); +} + +void VideoBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize, + SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem, bool aUnsafe, + bool* aSuccess) { + AutoCompleteTask complete(aTask); + + if (!CanSend()) { + return; + } + + bool ok = false; + if (aUnsafe) { + ok = AllocUnsafeShmem(aSize, aType, aShmem); + } else { + ok = AllocShmem(aSize, aType, aShmem); + } + *aSuccess = ok; +} + +bool VideoBridgeChild::DispatchAllocShmemInternal( + size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem, + bool aUnsafe) { + SynchronousTask task("AllocatorProxy alloc"); + + bool success = false; + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<VideoBridgeChild>(this), &VideoBridgeChild::ProxyAllocShmemNow, + &task, aSize, aType, 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&, + const ReadLockDescriptor&, + const LayersBackend&, + const TextureFlags&, + 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; +} + +void VideoBridgeChild::ActorDealloc() { mIPDLSelfRef = nullptr; } + +PTextureChild* VideoBridgeChild::CreateTexture( + const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) { + MOZ_ASSERT(CanSend()); + return SendPTextureConstructor(aSharedData, aReadLock, aLayersBackend, aFlags, + aSerial); +} + +bool VideoBridgeChild::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void VideoBridgeChild::HandleFatalError(const char* aMsg) const { + 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..9370bab04e --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeChild.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_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, + const ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aSerial); + bool DeallocPTextureChild(PTextureChild* actor); + + void ActorDestroy(ActorDestroyReason aWhy) override; + void ActorDealloc() override; + + // ISurfaceAllocator + bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) override; + bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) override; + bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + + // TextureForwarder + PTextureChild* CreateTexture( + const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock, + LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial, + wr::MaybeExternalImageId& aExternalImageId, + nsISerialEventTarget* aTarget = nullptr) 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) const override; + bool DispatchAllocShmemInternal(size_t aSize, + SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem, bool aUnsafe); + void ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize, + SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem, bool aUnsafe, + bool* aSuccess); + void ProxyDeallocShmemNow(SynchronousTask* aTask, mozilla::ipc::Shmem* aShmem, + bool* aResult); + + private: + VideoBridgeChild(); + virtual ~VideoBridgeChild(); + + RefPtr<VideoBridgeChild> mIPDLSelfRef; + 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..89acfa0601 --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeParent.cpp @@ -0,0 +1,155 @@ +/* -*- 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/ipc/Endpoint.h" +#include "mozilla/layers/TextureHost.h" +#include "mozilla/layers/VideoBridgeUtils.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::ipc; +using namespace mozilla::gfx; + +static VideoBridgeParent* sVideoBridgeFromRddProcess; +static VideoBridgeParent* sVideoBridgeFromGpuProcess; + +VideoBridgeParent::VideoBridgeParent(VideoBridgeSource aSource) + : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()), + mClosed(false) { + mSelfRef = this; + switch (aSource) { + default: + MOZ_CRASH("Unhandled case"); + case VideoBridgeSource::RddProcess: + sVideoBridgeFromRddProcess = this; + break; + case VideoBridgeSource::GpuProcess: + sVideoBridgeFromGpuProcess = this; + break; + } +} + +VideoBridgeParent::~VideoBridgeParent() { + if (sVideoBridgeFromRddProcess == this) { + sVideoBridgeFromRddProcess = nullptr; + } + if (sVideoBridgeFromGpuProcess == this) { + sVideoBridgeFromGpuProcess = 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()); + switch (aSource.value()) { + default: + MOZ_CRASH("Unhandled case"); + case VideoBridgeSource::RddProcess: + MOZ_ASSERT(sVideoBridgeFromRddProcess); + return sVideoBridgeFromRddProcess; + case VideoBridgeSource::GpuProcess: + MOZ_ASSERT(sVideoBridgeFromGpuProcess); + return sVideoBridgeFromGpuProcess; + } +} + +TextureHost* VideoBridgeParent::LookupTexture(uint64_t aSerial) { + MOZ_DIAGNOSTIC_ASSERT(CompositorThread() && + CompositorThread()->IsOnCurrentThread()); + return TextureHost::AsTextureHost(mTextureMap[aSerial]); +} + +void VideoBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { + // Can't alloc/dealloc shmems from now on. + mClosed = true; +} + +void VideoBridgeParent::ActorDealloc() { + mCompositorThreadHolder = nullptr; + mSelfRef = nullptr; +} + +PTextureParent* VideoBridgeParent::AllocPTextureParent( + const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, const TextureFlags& aFlags, + const uint64_t& aSerial) { + PTextureParent* parent = TextureHost::CreateIPDLActor( + this, aSharedData, aReadLock, aLayersBackend, aFlags, 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::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (mClosed) { + return false; + } + return PVideoBridgeParent::AllocShmem(aSize, aType, aShmem); +} + +bool VideoBridgeParent::AllocUnsafeShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (mClosed) { + return false; + } + return PVideoBridgeParent::AllocUnsafeShmem(aSize, aType, 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) {} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/VideoBridgeParent.h b/gfx/layers/ipc/VideoBridgeParent.h new file mode 100644 index 0000000000..855c018df3 --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeParent.h @@ -0,0 +1,82 @@ +/* -*- 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: + ~VideoBridgeParent(); + + static VideoBridgeParent* GetSingleton( + const Maybe<VideoBridgeSource>& aSource); + + static void Open(Endpoint<PVideoBridgeParent>&& aEndpoint, + VideoBridgeSource aSource); + + TextureHost* LookupTexture(uint64_t aSerial); + + // PVideoBridgeParent + void ActorDestroy(ActorDestroyReason aWhy) override; + PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const ReadLockDescriptor& aReadLock, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + 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::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) override; + + bool AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) override; + + bool DeallocShmem(ipc::Shmem& aShmem) override; + + private: + explicit VideoBridgeParent(VideoBridgeSource aSource); + void Bind(Endpoint<PVideoBridgeParent>&& aEndpoint); + + void ActorDealloc() override; + + // This keeps us alive until ActorDestroy(), at which point we do a + // deferred destruction of ourselves. + RefPtr<VideoBridgeParent> mSelfRef; + 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..e8e7090063 --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeUtils.h @@ -0,0 +1,37 @@ +/* -*- 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, + _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..dda247b9e0 --- /dev/null +++ b/gfx/layers/ipc/WebRenderMessages.ipdlh @@ -0,0 +1,213 @@ +/* -*- 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::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 class 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 OpAddPrivateExternalImage { + ExternalImageId externalImageId; + ImageKey key; + ImageDescriptor descriptor; +}; + +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; + bool isAsync; +}; + +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 OpUpdatePrivateExternalImage { + ExternalImageId externalImageId; + ImageKey key; + ImageDescriptor descriptor; + ImageIntRect dirtyRect; +}; + +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; + OpAddPrivateExternalImage; + OpAddSharedExternalImage; + OpPushExternalImageForTexture; + OpUpdatePrivateExternalImage; + OpUpdateSharedExternalImage; +}; + +} // namespace +} // namespace diff --git a/gfx/layers/ipc/fuzztest/compositor_manager_parent_ipc_libfuzz.cpp b/gfx/layers/ipc/fuzztest/compositor_manager_parent_ipc_libfuzz.cpp new file mode 100644 index 0000000000..6e76035031 --- /dev/null +++ b/gfx/layers/ipc/fuzztest/compositor_manager_parent_ipc_libfuzz.cpp @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=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 "gtest/gtest.h" + +#include "FuzzingInterface.h" +#include "ProtocolFuzzer.h" + +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" + +int FuzzingInitCompositorManagerParentIPC(int* argc, char*** argv) { + mozilla::ipc::ProtocolFuzzerHelper::CompositorBridgeParentSetup(); + mozilla::layers::LayerTreeOwnerTracker::Initialize(); + return 0; +} + +static int RunCompositorManagerParentIPCFuzzing(const uint8_t* data, + size_t size) { + static mozilla::layers::CompositorManagerParent* p = + mozilla::layers::CompositorManagerParent::CreateSameProcess().take(); + + static nsTArray<nsCString> ignored = mozilla::ipc::LoadIPCMessageBlacklist( + getenv("MOZ_IPC_MESSAGE_FUZZ_BLACKLIST")); + + mozilla::ipc::FuzzProtocol(p, data, size, ignored); + + return 0; +} + +MOZ_FUZZING_INTERFACE_RAW(FuzzingInitCompositorManagerParentIPC, + RunCompositorManagerParentIPCFuzzing, + CompositorManagerParentIPC); diff --git a/gfx/layers/ipc/fuzztest/moz.build b/gfx/layers/ipc/fuzztest/moz.build new file mode 100644 index 0000000000..a60293a520 --- /dev/null +++ b/gfx/layers/ipc/fuzztest/moz.build @@ -0,0 +1,16 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Library("FuzzingCompositorManagerParentIPC") + +SOURCES += ["compositor_manager_parent_ipc_libfuzz.cpp"] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" + +# Add libFuzzer configuration directives +include("/tools/fuzzing/libfuzzer-config.mozbuild") |