diff options
Diffstat (limited to 'gfx/layers/ipc')
78 files changed, 17529 insertions, 0 deletions
diff --git a/gfx/layers/ipc/APZCTreeManagerChild.cpp b/gfx/layers/ipc/APZCTreeManagerChild.cpp new file mode 100644 index 000000000..f5971dd38 --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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/TabParent.h" // for TabParent +#include "mozilla/layers/APZCCallbackHelper.h" // for APZCCallbackHelper +#include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession + +namespace mozilla { +namespace layers { + +APZCTreeManagerChild::APZCTreeManagerChild() + : mCompositorSession(nullptr) +{ +} + +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; +} + +nsEventStatus +APZCTreeManagerChild::ReceiveInputEvent( + InputData& aEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) +{ + switch (aEvent.mInputType) { + case MULTITOUCH_INPUT: { + MultiTouchInput& event = aEvent.AsMultiTouchInput(); + MultiTouchInput processedEvent; + + nsEventStatus res; + SendReceiveMultiTouchInputEvent(event, + &res, + &processedEvent, + aOutTargetGuid, + aOutInputBlockId); + + event = processedEvent; + return res; + } + case MOUSE_INPUT: { + MouseInput& event = aEvent.AsMouseInput(); + MouseInput processedEvent; + + nsEventStatus res; + SendReceiveMouseInputEvent(event, + &res, + &processedEvent, + aOutTargetGuid, + aOutInputBlockId); + + event = processedEvent; + return res; + } + case PANGESTURE_INPUT: { + PanGestureInput& event = aEvent.AsPanGestureInput(); + PanGestureInput processedEvent; + + nsEventStatus res; + SendReceivePanGestureInputEvent(event, + &res, + &processedEvent, + aOutTargetGuid, + aOutInputBlockId); + + event = processedEvent; + return res; + } + case PINCHGESTURE_INPUT: { + PinchGestureInput& event = aEvent.AsPinchGestureInput(); + PinchGestureInput processedEvent; + + nsEventStatus res; + SendReceivePinchGestureInputEvent(event, + &res, + &processedEvent, + aOutTargetGuid, + aOutInputBlockId); + + event = processedEvent; + return res; + } + case TAPGESTURE_INPUT: { + TapGestureInput& event = aEvent.AsTapGestureInput(); + TapGestureInput processedEvent; + + nsEventStatus res; + SendReceiveTapGestureInputEvent(event, + &res, + &processedEvent, + aOutTargetGuid, + aOutInputBlockId); + + event = processedEvent; + return res; + } + case SCROLLWHEEL_INPUT: { + ScrollWheelInput& event = aEvent.AsScrollWheelInput(); + ScrollWheelInput processedEvent; + + nsEventStatus res; + SendReceiveScrollWheelInputEvent(event, + &res, + &processedEvent, + aOutTargetGuid, + aOutInputBlockId); + + event = processedEvent; + return res; + } + default: { + MOZ_ASSERT_UNREACHABLE("Invalid InputData type."); + return nsEventStatus_eConsumeNoDefault; + } + } +} + +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) +{ + SendUpdateZoomConstraints(aGuid, aConstraints); +} + +void +APZCTreeManagerChild::CancelAnimation(const ScrollableLayerGuid &aGuid) +{ + SendCancelAnimation(aGuid); +} + +void +APZCTreeManagerChild::AdjustScrollForSurfaceShift(const ScreenPoint& aShift) +{ + SendAdjustScrollForSurfaceShift(aShift); +} + +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); +} + +void +APZCTreeManagerChild::SetLongTapEnabled(bool aTapGestureEnabled) +{ + SendSetLongTapEnabled(aTapGestureEnabled); +} + +void +APZCTreeManagerChild::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) +{ + SendProcessTouchVelocity(aTimestampMs, aSpeedY); +} + +void +APZCTreeManagerChild::UpdateWheelTransaction( + LayoutDeviceIntPoint aRefPoint, + EventMessage aEventMessage) +{ + SendUpdateWheelTransaction(aRefPoint, aEventMessage); +} + +void APZCTreeManagerChild::TransformEventRefPoint( + LayoutDeviceIntPoint* aRefPoint, + ScrollableLayerGuid* aOutTargetGuid) +{ + SendTransformEventRefPoint(*aRefPoint, aRefPoint, aOutTargetGuid); +} + +bool +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()) { + mCompositorSession->GetContentController()->HandleTap(aType, aPoint, + aModifiers, aGuid, aInputBlockId); + return true; + } + dom::TabParent* tab = dom::TabParent::GetTabParentFromLayersId(aGuid.mLayersId); + if (tab) { + tab->SendHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId); + } + return true; +} + +bool +APZCTreeManagerChild::RecvNotifyPinchGesture(const PinchGestureType& aType, + const ScrollableLayerGuid& aGuid, + 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, aSpanChange, aModifiers, mCompositorSession->GetWidget()); + } + return true; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/APZCTreeManagerChild.h b/gfx/layers/ipc/APZCTreeManagerChild.h new file mode 100644 index 000000000..3e7a2f260 --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerChild.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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/IAPZCTreeManager.h" +#include "mozilla/layers/PAPZCTreeManagerChild.h" + +namespace mozilla { +namespace layers { + +class RemoteCompositorSession; + +class APZCTreeManagerChild + : public IAPZCTreeManager + , public PAPZCTreeManagerChild +{ +public: + APZCTreeManagerChild(); + + void SetCompositorSession(RemoteCompositorSession* aSession); + + nsEventStatus + ReceiveInputEvent( + InputData& aEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) 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 + CancelAnimation(const ScrollableLayerGuid &aGuid) override; + + void + AdjustScrollForSurfaceShift(const ScreenPoint& aShift) 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; + + void + SetLongTapEnabled(bool aTapGestureEnabled) override; + + void + ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) override; + + void + TransformEventRefPoint( + LayoutDeviceIntPoint* aRefPoint, + ScrollableLayerGuid* aOutTargetGuid) override; + + void + UpdateWheelTransaction( + LayoutDeviceIntPoint aRefPoint, + EventMessage aEventMessage) override; + +protected: + bool RecvHandleTap(const TapType& aType, + const LayoutDevicePoint& aPoint, + const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) override; + + bool RecvNotifyPinchGesture(const PinchGestureType& aType, + const ScrollableLayerGuid& aGuid, + const LayoutDeviceCoord& aSpanChange, + const Modifiers& aModifiers) override; + + virtual + ~APZCTreeManagerChild() { } + +private: + MOZ_NON_OWNING_REF RemoteCompositorSession* mCompositorSession; +}; + +} // 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 000000000..33cd6ffff --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp @@ -0,0 +1,313 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "mozilla/layers/APZCTreeManager.h" +#include "mozilla/layers/APZThreadUtils.h" + +namespace mozilla { +namespace layers { + +APZCTreeManagerParent::APZCTreeManagerParent(uint64_t aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager) + : mLayersId(aLayersId) + , mTreeManager(aAPZCTreeManager) +{ + MOZ_ASSERT(aAPZCTreeManager != nullptr); +} + +APZCTreeManagerParent::~APZCTreeManagerParent() +{ +} + +void +APZCTreeManagerParent::ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager) +{ + MOZ_ASSERT(aAPZCTreeManager != nullptr); + mTreeManager = aAPZCTreeManager; +} + +bool +APZCTreeManagerParent::RecvReceiveMultiTouchInputEvent( + const MultiTouchInput& aEvent, + nsEventStatus* aOutStatus, + MultiTouchInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) +{ + MultiTouchInput event = aEvent; + + *aOutStatus = mTreeManager->ReceiveInputEvent( + event, + aOutTargetGuid, + aOutInputBlockId); + *aOutEvent = event; + + return true; +} + +bool +APZCTreeManagerParent::RecvReceiveMouseInputEvent( + const MouseInput& aEvent, + nsEventStatus* aOutStatus, + MouseInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) +{ + MouseInput event = aEvent; + + *aOutStatus = mTreeManager->ReceiveInputEvent( + event, + aOutTargetGuid, + aOutInputBlockId); + *aOutEvent = event; + + return true; +} + +bool +APZCTreeManagerParent::RecvReceivePanGestureInputEvent( + const PanGestureInput& aEvent, + nsEventStatus* aOutStatus, + PanGestureInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) +{ + PanGestureInput event = aEvent; + + *aOutStatus = mTreeManager->ReceiveInputEvent( + event, + aOutTargetGuid, + aOutInputBlockId); + *aOutEvent = event; + + return true; +} + +bool +APZCTreeManagerParent::RecvReceivePinchGestureInputEvent( + const PinchGestureInput& aEvent, + nsEventStatus* aOutStatus, + PinchGestureInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) +{ + PinchGestureInput event = aEvent; + + *aOutStatus = mTreeManager->ReceiveInputEvent( + event, + aOutTargetGuid, + aOutInputBlockId); + *aOutEvent = event; + + return true; +} + +bool +APZCTreeManagerParent::RecvReceiveTapGestureInputEvent( + const TapGestureInput& aEvent, + nsEventStatus* aOutStatus, + TapGestureInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) +{ + TapGestureInput event = aEvent; + + *aOutStatus = mTreeManager->ReceiveInputEvent( + event, + aOutTargetGuid, + aOutInputBlockId); + *aOutEvent = event; + + return true; +} + +bool +APZCTreeManagerParent::RecvReceiveScrollWheelInputEvent( + const ScrollWheelInput& aEvent, + nsEventStatus* aOutStatus, + ScrollWheelInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) +{ + ScrollWheelInput event = aEvent; + + *aOutStatus = mTreeManager->ReceiveInputEvent( + event, + aOutTargetGuid, + aOutInputBlockId); + *aOutEvent = event; + + return true; +} + +bool +APZCTreeManagerParent::RecvZoomToRect( + const ScrollableLayerGuid& aGuid, + const CSSRect& aRect, + const uint32_t& aFlags) +{ + if (aGuid.mLayersId != mLayersId) { + // Guard against bad data from hijacked child processes + NS_ERROR("Unexpected layers id in RecvZoomToRect; dropping message..."); + return false; + } + + mTreeManager->ZoomToRect(aGuid, aRect, aFlags); + return true; +} + +bool +APZCTreeManagerParent::RecvContentReceivedInputBlock( + const uint64_t& aInputBlockId, + const bool& aPreventDefault) +{ + APZThreadUtils::RunOnControllerThread( + NewRunnableMethod<uint64_t, bool>(mTreeManager, + &IAPZCTreeManager::ContentReceivedInputBlock, + aInputBlockId, + aPreventDefault)); + + return true; +} + +bool +APZCTreeManagerParent::RecvSetTargetAPZC( + const uint64_t& aInputBlockId, + nsTArray<ScrollableLayerGuid>&& aTargets) +{ + for (size_t i = 0; i < aTargets.Length(); i++) { + if (aTargets[i].mLayersId != mLayersId) { + // Guard against bad data from hijacked child processes + NS_ERROR("Unexpected layers id in RecvSetTargetAPZC; dropping message..."); + return false; + } + } + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + <uint64_t, + StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>> + (mTreeManager, &IAPZCTreeManager::SetTargetAPZC, aInputBlockId, aTargets)); + + return true; +} + +bool +APZCTreeManagerParent::RecvUpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const MaybeZoomConstraints& aConstraints) +{ + if (aGuid.mLayersId != mLayersId) { + // Guard against bad data from hijacked child processes + NS_ERROR("Unexpected layers id in RecvUpdateZoomConstraints; dropping message..."); + return false; + } + + mTreeManager->UpdateZoomConstraints(aGuid, aConstraints); + return true; +} + +bool +APZCTreeManagerParent::RecvCancelAnimation(const ScrollableLayerGuid& aGuid) +{ + if (aGuid.mLayersId != mLayersId) { + // Guard against bad data from hijacked child processes + NS_ERROR("Unexpected layers id in RecvCancelAnimation; dropping message..."); + return false; + } + + mTreeManager->CancelAnimation(aGuid); + return true; +} + +bool +APZCTreeManagerParent::RecvAdjustScrollForSurfaceShift(const ScreenPoint& aShift) +{ + mTreeManager->AdjustScrollForSurfaceShift(aShift); + return true; +} + +bool +APZCTreeManagerParent::RecvSetDPI(const float& aDpiValue) +{ + mTreeManager->SetDPI(aDpiValue); + return true; +} + +bool +APZCTreeManagerParent::RecvSetAllowedTouchBehavior( + const uint64_t& aInputBlockId, + nsTArray<TouchBehaviorFlags>&& aValues) +{ + APZThreadUtils::RunOnControllerThread(NewRunnableMethod + <uint64_t, + StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>> + (mTreeManager, + &IAPZCTreeManager::SetAllowedTouchBehavior, + aInputBlockId, Move(aValues))); + + return true; +} + +bool +APZCTreeManagerParent::RecvStartScrollbarDrag( + const ScrollableLayerGuid& aGuid, + const AsyncDragMetrics& aDragMetrics) +{ + if (aGuid.mLayersId != mLayersId) { + // Guard against bad data from hijacked child processes + NS_ERROR("Unexpected layers id in RecvStartScrollbarDrag; dropping message..."); + return false; + } + + APZThreadUtils::RunOnControllerThread( + NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>( + mTreeManager, + &IAPZCTreeManager::StartScrollbarDrag, + aGuid, aDragMetrics)); + + return true; +} + +bool +APZCTreeManagerParent::RecvSetLongTapEnabled(const bool& aTapGestureEnabled) +{ + mTreeManager->SetLongTapEnabled(aTapGestureEnabled); + return true; +} + +bool +APZCTreeManagerParent::RecvProcessTouchVelocity( + const uint32_t& aTimestampMs, + const float& aSpeedY) +{ + mTreeManager->ProcessTouchVelocity(aTimestampMs, aSpeedY); + return true; +} + +bool +APZCTreeManagerParent::RecvUpdateWheelTransaction( + const LayoutDeviceIntPoint& aRefPoint, + const EventMessage& aEventMessage) +{ + mTreeManager->UpdateWheelTransaction(aRefPoint, aEventMessage); + return true; +} + +bool +APZCTreeManagerParent::RecvTransformEventRefPoint( + const LayoutDeviceIntPoint& aRefPoint, + LayoutDeviceIntPoint* aOutRefPoint, + ScrollableLayerGuid* aOutTargetGuid) +{ + LayoutDeviceIntPoint refPoint = aRefPoint; + mTreeManager->TransformEventRefPoint(&refPoint, aOutTargetGuid); + *aOutRefPoint = refPoint; + + 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 000000000..f2d3aec57 --- /dev/null +++ b/gfx/layers/ipc/APZCTreeManagerParent.h @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 APZCTreeManagerParent + : public PAPZCTreeManagerParent +{ +public: + + explicit APZCTreeManagerParent(uint64_t aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager); + virtual ~APZCTreeManagerParent(); + + uint64_t LayersId() 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); + + bool + RecvReceiveMultiTouchInputEvent( + const MultiTouchInput& aEvent, + nsEventStatus* aOutStatus, + MultiTouchInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) override; + + bool + RecvReceiveMouseInputEvent( + const MouseInput& aEvent, + nsEventStatus* aOutStatus, + MouseInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) override; + + bool + RecvReceivePanGestureInputEvent( + const PanGestureInput& aEvent, + nsEventStatus* aOutStatus, + PanGestureInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) override; + + bool + RecvReceivePinchGestureInputEvent( + const PinchGestureInput& aEvent, + nsEventStatus* aOutStatus, + PinchGestureInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) override; + + bool + RecvReceiveTapGestureInputEvent( + const TapGestureInput& aEvent, + nsEventStatus* aOutStatus, + TapGestureInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) override; + + bool + RecvReceiveScrollWheelInputEvent( + const ScrollWheelInput& aEvent, + nsEventStatus* aOutStatus, + ScrollWheelInput* aOutEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) override; + + bool + RecvZoomToRect( + const ScrollableLayerGuid& aGuid, + const CSSRect& aRect, + const uint32_t& aFlags) override; + + bool + RecvContentReceivedInputBlock( + const uint64_t& aInputBlockId, + const bool& aPreventDefault) override; + + bool + RecvSetTargetAPZC( + const uint64_t& aInputBlockId, + nsTArray<ScrollableLayerGuid>&& aTargets) override; + + bool + RecvUpdateZoomConstraints( + const ScrollableLayerGuid& aGuid, + const MaybeZoomConstraints& aConstraints) override; + + bool + RecvCancelAnimation(const ScrollableLayerGuid& aGuid) override; + + bool + RecvAdjustScrollForSurfaceShift(const ScreenPoint& aShift) override; + + bool + RecvSetDPI(const float& aDpiValue) override; + + bool + RecvSetAllowedTouchBehavior( + const uint64_t& aInputBlockId, + nsTArray<TouchBehaviorFlags>&& aValues) override; + + bool + RecvStartScrollbarDrag( + const ScrollableLayerGuid& aGuid, + const AsyncDragMetrics& aDragMetrics) override; + + bool + RecvSetLongTapEnabled(const bool& aTapGestureEnabled) override; + + bool + RecvProcessTouchVelocity( + const uint32_t& aTimestampMs, + const float& aSpeedY) override; + + bool + RecvUpdateWheelTransaction( + const LayoutDeviceIntPoint& aRefPoint, + const EventMessage& aEventMessage) override; + + bool + RecvTransformEventRefPoint( + const LayoutDeviceIntPoint& aRefPoint, + LayoutDeviceIntPoint* aOutRefPoint, + ScrollableLayerGuid* aOutTargetGuid) override; + + void + ActorDestroy(ActorDestroyReason aWhy) override { } + +private: + uint64_t mLayersId; + RefPtr<APZCTreeManager> mTreeManager; +}; + +} // 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 000000000..2dd24d4cd --- /dev/null +++ b/gfx/layers/ipc/APZChild.cpp @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=4 ts=8 et 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/TabChild.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; + } +} + +bool +APZChild::RecvRequestContentRepaint(const FrameMetrics& aFrameMetrics) +{ + MOZ_ASSERT(mController->IsRepaintThread()); + + mController->RequestContentRepaint(aFrameMetrics); + return true; +} + +bool +APZChild::RecvUpdateOverscrollVelocity(const float& aX, const float& aY, const bool& aIsRootContent) +{ + mController->UpdateOverscrollVelocity(aX, aY, aIsRootContent); + return true; +} + +bool +APZChild::RecvUpdateOverscrollOffset(const float& aX, const float& aY, const bool& aIsRootContent) +{ + mController->UpdateOverscrollOffset(aX, aY, aIsRootContent); + return true; +} + +bool +APZChild::RecvSetScrollingRootContent(const bool& aIsRootContent) +{ + mController->SetScrollingRootContent(aIsRootContent); + return true; +} + +bool +APZChild::RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId, + const nsString& aEvent) +{ + mController->NotifyMozMouseScrollEvent(aScrollId, aEvent); + return true; +} + +bool +APZChild::RecvNotifyAPZStateChange(const ScrollableLayerGuid& aGuid, + const APZStateChange& aChange, + const int& aArg) +{ + mController->NotifyAPZStateChange(aGuid, aChange, aArg); + return true; +} + +bool +APZChild::RecvNotifyFlushComplete() +{ + MOZ_ASSERT(mController->IsRepaintThread()); + + mController->NotifyFlushComplete(); + return true; +} + +bool +APZChild::RecvDestroy() +{ + // mController->Destroy will be called in the destructor + PAPZChild::Send__delete__(this); + return true; +} + + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/APZChild.h b/gfx/layers/ipc/APZChild.h new file mode 100644 index 000000000..57865ee6d --- /dev/null +++ b/gfx/layers/ipc/APZChild.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=4 ts=8 et 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: + explicit APZChild(RefPtr<GeckoContentController> aController); + ~APZChild(); + + bool RecvRequestContentRepaint(const FrameMetrics& frame) override; + + bool RecvUpdateOverscrollVelocity(const float& aX, const float& aY, const bool& aIsRootContent) override; + + bool RecvUpdateOverscrollOffset(const float& aX, const float& aY, const bool& aIsRootContent) override; + + bool RecvSetScrollingRootContent(const bool& aIsRootContent) override; + + bool RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId, + const nsString& aEvent) override; + + bool RecvNotifyAPZStateChange(const ScrollableLayerGuid& aGuid, + const APZStateChange& aChange, + const int& aArg) override; + + bool RecvNotifyFlushComplete() override; + + bool RecvDestroy() override; + +private: + RefPtr<GeckoContentController> mController; +}; + +} // namespace layers + +} // namespace mozilla + +#endif // mozilla_layers_APZChild_h diff --git a/gfx/layers/ipc/CompositableForwarder.cpp b/gfx/layers/ipc/CompositableForwarder.cpp new file mode 100644 index 000000000..20f2f61ae --- /dev/null +++ b/gfx/layers/ipc/CompositableForwarder.cpp @@ -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/. */ + +#include "CompositableForwarder.h" +#include "mozilla/layers/CompositableChild.h" + +namespace mozilla { +namespace layers { + +void +CompositableForwarder::Destroy(CompositableChild* aCompositable) +{ + AssertInForwarderThread(); + + if (!aCompositable->CanSend()) { + return; + } + + if (!DestroyInTransaction(aCompositable, false)) { + aCompositable->SendDestroy(); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositableForwarder.h b/gfx/layers/ipc/CompositableForwarder.h new file mode 100644 index 000000000..b31754b5a --- /dev/null +++ b/gfx/layers/ipc/CompositableForwarder.h @@ -0,0 +1,127 @@ +/* -*- 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, uint64_t +#include "gfxTypes.h" +#include "mozilla/Attributes.h" // for override +#include "mozilla/UniquePtr.h" +#include "mozilla/layers/CompositableClient.h" // for CompositableClient +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator +#include "mozilla/layers/LayersTypes.h" // for LayersBackend +#include "mozilla/layers/TextureClient.h" // for TextureClient +#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder +#include "nsRegion.h" // for nsIntRegion +#include "mozilla/gfx/Rect.h" +#include "nsHashKeys.h" +#include "nsTHashtable.h" + +namespace mozilla { +namespace layers { + +class CompositableClient; +class ImageContainer; +class SurfaceDescriptor; +class SurfaceDescriptorTiles; +class ThebesBufferData; +class PTextureChild; + +/** + * 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: + /** + * 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 Destroy(CompositableChild* aCompositable); + + virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) = 0; + virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) = 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()); + } + +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 000000000..135011101 --- /dev/null +++ b/gfx/layers/ipc/CompositableTransactionParent.cpp @@ -0,0 +1,245 @@ +/* -*- 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 "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 + +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->GetCompositorID(); + if (!id) { + return false; + } + CompositorBridgeParent* cp = CompositorBridgeParent::GetCompositorBridgeParent(id); + if (!cp) { + return false; + } + cp->ScheduleComposition(); + return true; +} + +bool +CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation& aEdit, + EditReplyVector& replyv) +{ + // Ignore all operations on compositables created on stale compositors. We + // return true because the child is unable to handle errors. + CompositableHost* compositable = CompositableHost::FromIPDLActor(aEdit.compositableParent()); + if (compositable->GetCompositor() && !compositable->GetCompositor()->IsValid()) { + return true; + } + + switch (aEdit.detail().type()) { + case CompositableOperationDetail::TOpPaintTextureRegion: { + MOZ_LAYERS_LOG(("[ParentSide] Paint PaintedLayer")); + + const OpPaintTextureRegion& op = aEdit.detail().get_OpPaintTextureRegion(); + Layer* layer = compositable->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()); + + nsIntRegion frontUpdatedRegion; + if (!compositable->UpdateThebes(bufferData, + op.updatedRegion(), + thebes->GetValidRegion(), + &frontUpdatedRegion)) + { + return false; + } + replyv.push_back( + OpContentBufferSwap(aEdit.compositableParent(), nullptr, frontUpdatedRegion)); + + RenderTraceInvalidateEnd(thebes, "FF00FF"); + break; + } + case CompositableOperationDetail::TOpUseTiledLayerBuffer: { + MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer")); + const OpUseTiledLayerBuffer& op = aEdit.detail().get_OpUseTiledLayerBuffer(); + TiledContentHost* tiledHost = compositable->AsTiledContentHost(); + + NS_ASSERTION(tiledHost, "The compositable is not tiled"); + + const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor(); + + bool success = tiledHost->UseTiledLayerBuffer(this, tileDesc); + + const InfallibleTArray<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.textureOnWhite().type() == MaybeTexture::TPTextureParent) { + texture = TextureHost::AsTextureHost(texturedDesc.textureOnWhite().get_PTextureParent()); + 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 = aEdit.detail().get_OpRemoveTexture(); + + RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent()); + + MOZ_ASSERT(tex.get()); + compositable->RemoveTextureHost(tex); + break; + } + case CompositableOperationDetail::TOpUseTexture: { + const OpUseTexture& op = aEdit.detail().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(); + t->mTexture->DeserializeReadLock(timedTexture.sharedLock(), this); + } + if (textures.Length() > 0) { + compositable->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() && compositable->GetLayer()) { + ScheduleComposition(compositable); + } + break; + } + case CompositableOperationDetail::TOpUseComponentAlphaTextures: { + const OpUseComponentAlphaTextures& op = aEdit.detail().get_OpUseComponentAlphaTextures(); + RefPtr<TextureHost> texOnBlack = TextureHost::AsTextureHost(op.textureOnBlackParent()); + RefPtr<TextureHost> texOnWhite = TextureHost::AsTextureHost(op.textureOnWhiteParent()); + texOnBlack->DeserializeReadLock(op.sharedLockBlack(), this); + texOnWhite->DeserializeReadLock(op.sharedLockWhite(), this); + + MOZ_ASSERT(texOnBlack && texOnWhite); + compositable->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(compositable); + } + 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::TPCompositableParent: { + auto actor = aOp.get_PCompositableParent(); + CompositableHost::ReceivedDestroy(actor); + break; + } + default: { + MOZ_ASSERT(false, "unsupported type"); + } + } +} + +} // namespace layers +} // namespace mozilla + diff --git a/gfx/layers/ipc/CompositableTransactionParent.h b/gfx/layers/ipc/CompositableTransactionParent.h new file mode 100644 index 000000000..ca676c115 --- /dev/null +++ b/gfx/layers/ipc/CompositableTransactionParent.h @@ -0,0 +1,55 @@ +/* -*- 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/. */ + +#ifndef MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H +#define MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H + +#include <vector> // for vector +#include "mozilla/Attributes.h" // for override +#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator +#include "mozilla/layers/LayersMessages.h" // for EditReply, etc + +namespace mozilla { +namespace layers { + +class CompositableHost; + +typedef std::vector<mozilla::layers::EditReply> EditReplyVector; + +// 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() {} + + void DestroyActor(const OpDestroy& aOp); + + void UpdateFwdTransactionId(uint64_t aTransactionId) + { + MOZ_ASSERT(mFwdTransactionId < aTransactionId); + mFwdTransactionId = aTransactionId; + } + + uint64_t GetFwdTransactionId() { return mFwdTransactionId; } + +protected: + /** + * Handle the IPDL messages that affect PCompositable actors. + */ + bool ReceiveCompositableUpdate(const CompositableOperation& aEdit, + EditReplyVector& replyv); + + uint64_t mFwdTransactionId = 0; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/CompositorBench.cpp b/gfx/layers/ipc/CompositorBench.cpp new file mode 100644 index 000000000..945adafc1 --- /dev/null +++ b/gfx/layers/ipc/CompositorBench.cpp @@ -0,0 +1,345 @@ +/* -*- 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 "CompositorBench.h" + +#ifdef MOZ_COMPOSITOR_BENCH +#include "mozilla/gfx/2D.h" +#include "mozilla/layers/Compositor.h" +#include "mozilla/layers/Effects.h" +#include "mozilla/TimeStamp.h" +#include "gfxPrefs.h" +#include <math.h> +#include "GeckoProfiler.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() {} + + 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::Color(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::Color(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::Color(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(test->ToString()); + 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 = gfxPrefs::LayersBenchEnabled(); + 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 000000000..04c994495 --- /dev/null +++ b/gfx/layers/ipc/CompositorBench.h @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=4 ts=8 et 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 000000000..f623b10b8 --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -0,0 +1,1150 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 et 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/message_loop.h" // for MessageLoop +#include "base/task.h" // for NewRunnableMethod, etc +#include "gfxPrefs.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/APZChild.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "mozilla/layers/APZCTreeManagerChild.h" +#include "mozilla/layers/LayerTransactionChild.h" +#include "mozilla/layers/PLayerTransactionChild.h" +#include "mozilla/layers/TextureClient.h"// for TextureClient +#include "mozilla/layers/TextureClientPool.h"// for TextureClientPool +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/mozalloc.h" // for operator new, etc +#include "nsAutoPtr.h" +#include "nsDebug.h" // for NS_RUNTIMEABORT +#include "nsIObserver.h" // for nsIObserver +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "nsTArray.h" // for nsTArray, nsTArray_Impl +#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop, etc +#include "FrameLayerBuilder.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/Unused.h" +#include "mozilla/DebugOnly.h" +#if defined(XP_WIN) +#include "WinUtils.h" +#endif +#include "mozilla/widget/CompositorWidget.h" +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING +# include "mozilla/widget/CompositorWidgetChild.h" +#endif +#include "VsyncSource.h" + +using mozilla::layers::LayerTransactionChild; +using mozilla::dom::TabChildBase; +using mozilla::Unused; +using mozilla::gfx::GPUProcessManager; + +namespace mozilla { +namespace layers { + +static int sShmemCreationCounter = 0; + +static void ResetShmemCounter() +{ + sShmemCreationCounter = 0; +} + +static void ShmemAllocated(CompositorBridgeChild* aProtocol) +{ + sShmemCreationCounter++; + if (sShmemCreationCounter > 256) { + aProtocol->SendSyncWithCompositor(); + ResetShmemCounter(); + MOZ_PERFORMANCE_WARNING("gfx", "The number of shmem allocations is too damn high!"); + } +} + +static StaticRefPtr<CompositorBridgeChild> sCompositorBridge; + +Atomic<int32_t> KnowsCompositor::sSerialCounter(0); + +CompositorBridgeChild::CompositorBridgeChild(LayerManager *aLayerManager) + : mLayerManager(aLayerManager) + , mCanSend(false) + , mFwdTransactionId(0) + , mMessageLoop(MessageLoop::current()) + , mSectionAllocator(nullptr) +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +CompositorBridgeChild::~CompositorBridgeChild() +{ + if (mCanSend) { + gfxCriticalError() << "CompositorBridgeChild was not deinitialized"; + } +} + +bool +CompositorBridgeChild::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + +static void DeferredDestroyCompositor(RefPtr<CompositorBridgeParent> aCompositorBridgeParent, + RefPtr<CompositorBridgeChild> aCompositorBridgeChild) +{ + aCompositorBridgeChild->Close(); + + if (sCompositorBridge == aCompositorBridgeChild) { + sCompositorBridge = nullptr; + } +} + +void +CompositorBridgeChild::Destroy() +{ + // This must not be called from the destructor! + mTexturesWaitingRecycled.Clear(); + + if (!mCanSend) { + return; + } + + for (size_t i = 0; i < mTexturePools.Length(); i++) { + mTexturePools[i]->Destroy(); + } + + if (mSectionAllocator) { + delete mSectionAllocator; + mSectionAllocator = nullptr; + } + + // 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; + + if (mLayerManager) { + mLayerManager->Destroy(); + mLayerManager = nullptr; + } + + 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(); + } + + 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(); + } + } + + 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 MessageLoop get processed before the + // CompositorBridgeChild is destroyed, so we add a task to the MessageLoop to + // handle compositor desctruction. + + // From now on we can't send any message message. + MessageLoop::current()->PostTask( + NewRunnableFunction(DeferredDestroyCompositor, mCompositorBridgeParent, selfRef)); +} + +// static +void +CompositorBridgeChild::ShutDown() +{ + if (sCompositorBridge) { + sCompositorBridge->Destroy(); + do { + NS_ProcessNextEvent(nullptr, true); + } while (sCompositorBridge); + } +} + +bool +CompositorBridgeChild::LookupCompositorFrameMetrics(const FrameMetrics::ViewID aId, + FrameMetrics& aFrame) +{ + SharedFrameMetricsData* data = mFrameMetricsTable.Get(aId); + if (data) { + data->CopyFrameMetrics(&aFrame); + return true; + } + return false; +} + +/* static */ bool +CompositorBridgeChild::InitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint) +{ + // There's only one compositor per child process. + MOZ_ASSERT(!sCompositorBridge); + + RefPtr<CompositorBridgeChild> child(new CompositorBridgeChild(nullptr)); + if (!aEndpoint.Bind(child)) { + NS_RUNTIMEABORT("Couldn't Open() Compositor channel."); + return false; + } + child->InitIPDL(); + + // We release this ref in DeferredDestroyCompositor. + sCompositorBridge = child; + return true; +} + +/* static */ bool +CompositorBridgeChild::ReinitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint) +{ + MOZ_ASSERT(NS_IsMainThread()); + + 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(); + } + + return InitForContent(Move(aEndpoint)); +} + +CompositorBridgeParent* +CompositorBridgeChild::InitSameProcess(widget::CompositorWidget* aWidget, + const uint64_t& aLayerTreeId, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurface, + const gfx::IntSize& aSurfaceSize) +{ + TimeDuration vsyncRate = + gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().GetVsyncRate(); + + mCompositorBridgeParent = + new CompositorBridgeParent(aScale, vsyncRate, aUseExternalSurface, aSurfaceSize); + + bool ok = Open(mCompositorBridgeParent->GetIPCChannel(), + CompositorThreadHolder::Loop(), + ipc::ChildSide); + MOZ_RELEASE_ASSERT(ok); + + InitIPDL(); + mCompositorBridgeParent->InitSameProcess(aWidget, aLayerTreeId, aUseAPZ); + return mCompositorBridgeParent; +} + +/* static */ RefPtr<CompositorBridgeChild> +CompositorBridgeChild::CreateRemote(const uint64_t& aProcessToken, + LayerManager* aLayerManager, + Endpoint<PCompositorBridgeChild>&& aEndpoint) +{ + RefPtr<CompositorBridgeChild> child = new CompositorBridgeChild(aLayerManager); + if (!aEndpoint.Bind(child)) { + return nullptr; + } + child->InitIPDL(); + child->mProcessToken = aProcessToken; + return child; +} + +void +CompositorBridgeChild::InitIPDL() +{ + mCanSend = true; + AddRef(); +} + +void +CompositorBridgeChild::DeallocPCompositorBridgeChild() +{ + Release(); +} + +/*static*/ CompositorBridgeChild* +CompositorBridgeChild::Get() +{ + // This is only expected to be used in child processes. + MOZ_ASSERT(!XRE_IsParentProcess()); + return sCompositorBridge; +} + +// static +bool +CompositorBridgeChild::ChildProcessHasCompositorBridge() +{ + return sCompositorBridge != nullptr; +} + +PLayerTransactionChild* +CompositorBridgeChild::AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints, + const uint64_t& aId, + TextureFactoryIdentifier*, + bool*) +{ + LayerTransactionChild* c = new LayerTransactionChild(aId); + c->AddIPDLReference(); + return c; +} + +bool +CompositorBridgeChild::DeallocPLayerTransactionChild(PLayerTransactionChild* actor) +{ + uint64_t childId = static_cast<LayerTransactionChild*>(actor)->GetId(); + + for (auto iter = mFrameMetricsTable.Iter(); !iter.Done(); iter.Next()) { + nsAutoPtr<SharedFrameMetricsData>& data = iter.Data(); + if (data->GetLayersId() == childId) { + iter.Remove(); + } + } + static_cast<LayerTransactionChild*>(actor)->ReleaseIPDLReference(); + return true; +} + +bool +CompositorBridgeChild::RecvInvalidateLayers(const uint64_t& aLayersId) +{ + if (mLayerManager) { + MOZ_ASSERT(aLayersId == 0); + FrameLayerBuilder::InvalidateAllLayers(mLayerManager); + } else if (aLayersId != 0) { + if (dom::TabChild* child = dom::TabChild::GetFrom(aLayersId)) { + child->InvalidateLayers(); + } + } + return true; +} + +bool +CompositorBridgeChild::RecvCompositorUpdated(const uint64_t& aLayersId, + const TextureFactoryIdentifier& aNewIdentifier) +{ + if (mLayerManager) { + // This case is handled directly by nsBaseWidget. + MOZ_ASSERT(aLayersId == 0); + } else if (aLayersId != 0) { + if (dom::TabChild* child = dom::TabChild::GetFrom(aLayersId)) { + child->CompositorUpdated(aNewIdentifier); + + // If we still get device reset here, something must wrong when creating + // d3d11 devices. + if (gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) { + gfxCriticalError() << "Unexpected reset device processing when \ + updating compositor."; + } + } + if (!mCanSend) { + return true; + } + SendAcknowledgeCompositorUpdate(aLayersId); + } + return true; +} + +#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 + +bool +CompositorBridgeChild::RecvUpdatePluginConfigurations(const LayoutDeviceIntPoint& aContentOffset, + const LayoutDeviceIntRegion& aParentLayerVisibleRegion, + nsTArray<PluginWindowData>&& aPlugins) +{ +#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) + NS_NOTREACHED("CompositorBridgeChild::RecvUpdatePluginConfigurations calls " + "unexpected on this platform."); + return false; +#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. + rv = widget->Resize(aContentOffset.x + bounds.x, + aContentOffset.y + bounds.y, + bounds.width, bounds.height, true); + NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure"); + } + + rv = widget->Enable(isVisible); + NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure"); + + // visible state - updated after clipping, prior to invalidating + rv = widget->Show(isVisible); + NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure"); + + // 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 + rv = widget->Invalidate(visibleBounds); + NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure"); +#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 true; + } + SendRemotePluginsReady(); + return true; +#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) +} + +#if defined(XP_WIN) +static void +ScheduleSendAllPluginsCaptured(CompositorBridgeChild* aThis, MessageLoop* aLoop) +{ + aLoop->PostTask(NewNonOwningRunnableMethod( + aThis, &CompositorBridgeChild::SendAllPluginsCaptured)); +} +#endif + +bool +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 loop, + // to make sure that the image updates on that thread have been processed. + ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask( + NewRunnableFunction(&ScheduleSendAllPluginsCaptured, this, + MessageLoop::current())); + return true; +#else + MOZ_ASSERT_UNREACHABLE( + "CompositorBridgeChild::RecvCaptureAllPlugins calls unexpected."); + return false; +#endif +} + +bool +CompositorBridgeChild::RecvHideAllPlugins(const uintptr_t& aParentWidget) +{ +#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) + NS_NOTREACHED("CompositorBridgeChild::RecvHideAllPlugins calls " + "unexpected on this platform."); + return false; +#else + MOZ_ASSERT(NS_IsMainThread()); + nsTArray<uintptr_t> list; + nsIWidget::UpdateRegisteredPluginWindowVisibility(aParentWidget, list); + if (!mCanSend) { + return true; + } + SendRemotePluginsReady(); + return true; +#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK) +} + +bool +CompositorBridgeChild::RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId, + const TimeStamp& aCompositeStart, + const TimeStamp& aCompositeEnd) +{ + // Hold a reference to keep texture pools alive. See bug 1387799 + AutoTArray<RefPtr<TextureClientPool>,2> texturePools = mTexturePools; + + if (mLayerManager) { + MOZ_ASSERT(aId == 0); + RefPtr<ClientLayerManager> m = mLayerManager->AsClientLayerManager(); + MOZ_ASSERT(m); + m->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd); + } else if (aId != 0) { + RefPtr<dom::TabChild> child = dom::TabChild::GetFrom(aId); + if (child) { + child->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd); + } + } + + for (size_t i = 0; i < texturePools.Length(); i++) { + texturePools[i]->ReturnDeferredClients(); + } + + return true; +} + +bool +CompositorBridgeChild::RecvOverfill(const uint32_t &aOverfill) +{ + for (size_t i = 0; i < mOverfillObservers.Length(); i++) { + mOverfillObservers[i]->RunOverfillCallback(aOverfill); + } + mOverfillObservers.Clear(); + return true; +} + +void +CompositorBridgeChild::AddOverfillObserver(ClientLayerManager* aLayerManager) +{ + MOZ_ASSERT(aLayerManager); + mOverfillObservers.AppendElement(aLayerManager); +} + +bool +CompositorBridgeChild::RecvClearCachedResources(const uint64_t& aId) +{ + dom::TabChild* child = dom::TabChild::GetFrom(aId); + if (child) { + child->ClearCachedResources(); + } + return true; +} + +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"; + } + + mCanSend = false; + + if (mProcessToken && XRE_IsParentProcess()) { + GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); + } +} + +bool +CompositorBridgeChild::RecvSharedCompositorFrameMetrics( + const mozilla::ipc::SharedMemoryBasic::Handle& metrics, + const CrossProcessMutexHandle& handle, + const uint64_t& aLayersId, + const uint32_t& aAPZCId) +{ + SharedFrameMetricsData* data = new SharedFrameMetricsData( + metrics, handle, aLayersId, aAPZCId); + mFrameMetricsTable.Put(data->GetViewID(), data); + return true; +} + +bool +CompositorBridgeChild::RecvReleaseSharedCompositorFrameMetrics( + const ViewID& aId, + const uint32_t& aAPZCId) +{ + SharedFrameMetricsData* data = mFrameMetricsTable.Get(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 (data && (data->GetAPZCId() == aAPZCId)) { + mFrameMetricsTable.Remove(aId); + } + return true; +} + +CompositorBridgeChild::SharedFrameMetricsData::SharedFrameMetricsData( + const ipc::SharedMemoryBasic::Handle& metrics, + const CrossProcessMutexHandle& handle, + const uint64_t& aLayersId, + const uint32_t& aAPZCId) + : mMutex(nullptr) + , mLayersId(aLayersId) + , mAPZCId(aAPZCId) +{ + mBuffer = new ipc::SharedMemoryBasic; + mBuffer->SetHandle(metrics); + 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) +{ + FrameMetrics* frame = static_cast<FrameMetrics*>(mBuffer->memory()); + MOZ_ASSERT(frame); + mMutex->Lock(); + *aFrame = *frame; + mMutex->Unlock(); +} + +FrameMetrics::ViewID +CompositorBridgeChild::SharedFrameMetricsData::GetViewID() +{ + FrameMetrics* frame = static_cast<FrameMetrics*>(mBuffer->memory()); + MOZ_ASSERT(frame); + // Not locking to read of mScrollId since it should not change after being + // initially set. + return frame->GetScrollId(); +} + +uint64_t +CompositorBridgeChild::SharedFrameMetricsData::GetLayersId() const +{ + return mLayersId; +} + +uint32_t +CompositorBridgeChild::SharedFrameMetricsData::GetAPZCId() +{ + return mAPZCId; +} + + +bool +CompositorBridgeChild::RecvRemotePaintIsReady() +{ + // Used on the content thread, this bounces the message to the + // TabParent (via the TabChild) 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<nsISupports> iTabChildBase(do_QueryReferent(mWeakTabChild)); + if (!iTabChildBase) { + MOZ_LAYERS_LOG(("[RemoteGfx] Note: TabChild was released before RemotePaintIsReady. " + "MozAfterRemotePaint will not be sent to listener.")); + return true; + } + TabChildBase* tabChildBase = static_cast<TabChildBase*>(iTabChildBase.get()); + TabChild* tabChild = static_cast<TabChild*>(tabChildBase); + MOZ_ASSERT(tabChild); + Unused << tabChild->SendRemotePaintIsReady(); + mWeakTabChild = nullptr; + return true; +} + + +void +CompositorBridgeChild::RequestNotifyAfterRemotePaint(TabChild* aTabChild) +{ + MOZ_ASSERT(aTabChild, "NULL TabChild not allowed in CompositorBridgeChild::RequestNotifyAfterRemotePaint"); + mWeakTabChild = do_GetWeakReference( static_cast<dom::TabChildBase*>(aTabChild) ); + if (!mCanSend) { + return; + } + Unused << SendRequestNotifyAfterRemotePaint(); +} + +void +CompositorBridgeChild::CancelNotifyAfterRemotePaint(TabChild* aTabChild) +{ + RefPtr<nsISupports> iTabChildBase(do_QueryReferent(mWeakTabChild)); + if (!iTabChildBase) { + return; + } + TabChildBase* tabChildBase = static_cast<TabChildBase*>(iTabChildBase.get()); + TabChild* tabChild = static_cast<TabChild*>(tabChildBase); + if (tabChild == aTabChild) { + mWeakTabChild = 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::SendNotifyChildCreated(const uint64_t& id) +{ + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendNotifyChildCreated(id); +} + +bool +CompositorBridgeChild::SendAdoptChild(const uint64_t& 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::SendClearApproximatelyVisibleRegions(uint64_t aLayersId, + uint32_t aPresShellId) +{ + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendClearApproximatelyVisibleRegions(aLayersId, + aPresShellId); +} + +bool +CompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) +{ + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(aGuid, aRegion); +} + +bool +CompositorBridgeChild::SendAllPluginsCaptured() +{ + if (!mCanSend) { + return false; + } + return PCompositorBridgeChild::SendAllPluginsCaptured(); +} + +PTextureChild* +CompositorBridgeChild::AllocPTextureChild(const SurfaceDescriptor&, + const LayersBackend&, + const TextureFlags&, + const uint64_t&, + const uint64_t& aSerial) +{ + return TextureClient::CreateIPDLActor(); +} + +bool +CompositorBridgeChild::DeallocPTextureChild(PTextureChild* actor) +{ + return TextureClient::DestroyIPDLActor(actor); +} + +bool +CompositorBridgeChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) +{ + for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) { + const AsyncParentMessageData& message = aMessages[i]; + + switch (message.type()) { + case AsyncParentMessageData::TOpNotifyNotUsed: { + const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed(); + NotifyNotUsed(op.TextureId(), op.fwdTransactionId()); + break; + } + default: + NS_ERROR("unknown AsyncParentMessageData type"); + return false; + } + } + return true; +} + +bool +CompositorBridgeChild::RecvObserveLayerUpdate(const uint64_t& aLayersId, + const uint64_t& 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); + MOZ_ASSERT(XRE_IsParentProcess()); + + if (RefPtr<dom::TabParent> tab = dom::TabParent::GetTabParentFromLayersId(aLayersId)) { + tab->LayerTreeUpdate(aEpoch, aActive); + } + return true; +} + +void +CompositorBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient) +{ + if (!aClient) { + return; + } + + if (!(aClient->GetFlags() & TextureFlags::RECYCLE)) { + return; + } + + aClient->SetLastFwdTransactionId(GetFwdTransactionId()); + mTexturesWaitingRecycled.Put(aClient->GetSerial(), aClient); +} + +void +CompositorBridgeChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId) +{ + RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); + if (!client) { + return; + } + if (aFwdTransactionId < client->GetLastFwdTransactionId()) { + // Released on host side, but client already requested newer use texture. + return; + } + mTexturesWaitingRecycled.Remove(aTextureId); +} + +void +CompositorBridgeChild::CancelWaitForRecycle(uint64_t aTextureId) +{ + RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); + if (!client) { + return; + } + mTexturesWaitingRecycled.Remove(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->GetCompositorBackendType(), + aAllocator->GetMaxTextureSize(), + aFormat, + gfx::gfxVars::TileSize(), + aFlags, + gfxPrefs::LayersTilePoolShrinkTimeout(), + gfxPrefs::LayersTilePoolClearTimeout(), + gfxPrefs::LayersTileInitialPoolSize(), + gfxPrefs::LayersTilePoolUnusedSize(), + 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() +{ + MOZ_ASSERT(IPCOpen()); + if (!IPCOpen()) { + return nullptr; + } + + if (!mSectionAllocator) { + mSectionAllocator = new FixedSizeSmallShmemSectionAllocator(this); + } + return mSectionAllocator; +} + + +PTextureChild* +CompositorBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData, + LayersBackend aLayersBackend, + TextureFlags aFlags, + uint64_t aSerial) +{ + return PCompositorBridgeChild::SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, 0 /* FIXME? */, aSerial); +} + +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 +} + +RefPtr<IAPZCTreeManager> +CompositorBridgeChild::GetAPZCTreeManager(uint64_t aLayerTreeId) +{ + bool apzEnabled = false; + Unused << SendAsyncPanZoomEnabled(aLayerTreeId, &apzEnabled); + + if (!apzEnabled) { + return nullptr; + } + + PAPZCTreeManagerChild* child = SendPAPZCTreeManagerConstructor(aLayerTreeId); + if (!child) { + return nullptr; + } + APZCTreeManagerChild* parent = static_cast<APZCTreeManagerChild*>(child); + + return RefPtr<IAPZCTreeManager>(parent); +} + +PAPZCTreeManagerChild* +CompositorBridgeChild::AllocPAPZCTreeManagerChild(const uint64_t& aLayersId) +{ + APZCTreeManagerChild* child = new APZCTreeManagerChild(); + child->AddRef(); + return child; +} + +PAPZChild* +CompositorBridgeChild::AllocPAPZChild(const uint64_t& 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* parent = static_cast<APZCTreeManagerChild*>(aActor); + parent->Release(); + return true; +} + +void +CompositorBridgeChild::ProcessingError(Result aCode, const char* aReason) +{ + if (aCode != MsgDropped) { + gfxDevCrash(gfx::LogReason::ProcessingError) << "Processing error in CompositorBridgeChild: " << int(aCode); + } +} + +void +CompositorBridgeChild::WillEndTransaction() +{ + ResetShmemCounter(); +} + +void +CompositorBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const +{ + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid()); +} + +} // namespace layers +} // namespace mozilla + diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h new file mode 100644 index 000000000..60d1a146d --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeChild.h @@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et 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/ipc/ProtocolUtils.h" +#include "mozilla/layers/PCompositorBridgeChild.h" +#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder +#include "nsClassHashtable.h" // for nsClassHashtable +#include "nsCOMPtr.h" // for nsCOMPtr +#include "nsHashKeys.h" // for nsUint64HashKey +#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING +#include "ThreadSafeRefcountingWithMainThreadDestruction.h" +#include "nsWeakReference.h" + +namespace mozilla { + +namespace dom { +class TabChild; +} // namespace dom + +namespace widget { +class CompositorWidget; +} // namespace widget + +namespace layers { + +using mozilla::dom::TabChild; + +class IAPZCTreeManager; +class APZCTreeManagerChild; +class ClientLayerManager; +class CompositorBridgeParent; +class TextureClient; +class TextureClientPool; +struct FrameMetrics; + +class CompositorBridgeChild final : public PCompositorBridgeChild, + public TextureForwarder +{ + typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorBridgeChild, override); + + explicit CompositorBridgeChild(LayerManager *aLayerManager); + + void Destroy(); + + /** + * Lookup the FrameMetrics shared by the compositor process with the + * associated FrameMetrics::ViewID. The returned FrameMetrics is used + * in progressive paint calculations. + */ + bool LookupCompositorFrameMetrics(const FrameMetrics::ViewID aId, FrameMetrics&); + + /** + * Initialize the singleton compositor bridge for a content process. + */ + static bool InitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint); + static bool ReinitForContent(Endpoint<PCompositorBridgeChild>&& aEndpoint); + + static RefPtr<CompositorBridgeChild> CreateRemote( + const uint64_t& aProcessToken, + LayerManager* aLayerManager, + Endpoint<PCompositorBridgeChild>&& aEndpoint); + + /** + * Initialize the CompositorBridgeChild, create CompositorBridgeParent, and + * open a same-process connection. + */ + CompositorBridgeParent* InitSameProcess( + widget::CompositorWidget* aWidget, + const uint64_t& aLayerTreeId, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurface, + const gfx::IntSize& aSurfaceSize); + + static CompositorBridgeChild* Get(); + + static bool ChildProcessHasCompositorBridge(); + + void AddOverfillObserver(ClientLayerManager* aLayerManager); + + virtual bool + RecvClearCachedResources(const uint64_t& id) override; + + virtual bool + RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId, + const TimeStamp& aCompositeStart, + const TimeStamp& aCompositeEnd) override; + + virtual bool + RecvInvalidateLayers(const uint64_t& aLayersId) override; + + virtual bool + RecvCompositorUpdated(const uint64_t& aLayersId, + const TextureFactoryIdentifier& aNewIdentifier) override; + + virtual bool + RecvOverfill(const uint32_t &aOverfill) override; + + virtual bool + RecvUpdatePluginConfigurations(const LayoutDeviceIntPoint& aContentOffset, + const LayoutDeviceIntRegion& aVisibleRegion, + nsTArray<PluginWindowData>&& aPlugins) override; + + virtual bool + RecvCaptureAllPlugins(const uintptr_t& aParentWidget) override; + + virtual bool + RecvHideAllPlugins(const uintptr_t& aParentWidget) override; + + virtual PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId, + const uint64_t& aSerial) override; + + virtual bool DeallocPTextureChild(PTextureChild* actor) override; + + virtual bool + RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) override; + virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData, + LayersBackend aLayersBackend, + TextureFlags aFlags, + uint64_t aSerial) override; + + virtual void HandleFatalError(const char* aName, const char* aMsg) const override; + + /** + * Request that the parent tell us when graphics are ready on GPU. + * When we get that message, we bounce it to the TabParent via + * the TabChild + * @param tabChild The object to bounce the note to. Non-NULL. + */ + void RequestNotifyAfterRemotePaint(TabChild* aTabChild); + + void CancelNotifyAfterRemotePaint(TabChild* aTabChild); + + // 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 SendNotifyChildCreated(const uint64_t& id); + bool SendAdoptChild(const uint64_t& 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 SendClearApproximatelyVisibleRegions(uint64_t aLayersId, uint32_t aPresShellId); + bool SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const mozilla::CSSIntRegion& aRegion); + bool SendAllPluginsCaptured(); + bool IsSameProcess() const override; + + virtual 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); + + virtual void CancelWaitForRecycle(uint64_t aTextureId) override; + + TextureClientPool* GetTexturePool(KnowsCompositor* aAllocator, + gfx::SurfaceFormat aFormat, + TextureFlags aFlags); + void ClearTexturePool(); + + virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() override; + + void HandleMemoryPressure(); + + virtual MessageLoop* GetMessageLoop() const override { return mMessageLoop; } + + virtual base::ProcessId GetParentPid() const override { return OtherPid(); } + + virtual bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) override; + virtual bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) override; + virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + + PCompositorWidgetChild* AllocPCompositorWidgetChild(const CompositorWidgetInitData& aInitData) override; + bool DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor) override; + + RefPtr<IAPZCTreeManager> GetAPZCTreeManager(uint64_t aLayerTreeId); + + PAPZCTreeManagerChild* AllocPAPZCTreeManagerChild(const uint64_t& aLayersId) override; + bool DeallocPAPZCTreeManagerChild(PAPZCTreeManagerChild* aActor) override; + + PAPZChild* AllocPAPZChild(const uint64_t& aLayersId) override; + bool DeallocPAPZChild(PAPZChild* aActor) override; + + void ProcessingError(Result aCode, const char* aReason) override; + + void WillEndTransaction(); + +private: + // Private destructor, to discourage deletion outside of Release(): + virtual ~CompositorBridgeChild(); + + void InitIPDL(); + void DeallocPCompositorBridgeChild() override; + + virtual PLayerTransactionChild* + AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints, + const uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool* aSuccess) override; + + virtual bool DeallocPLayerTransactionChild(PLayerTransactionChild *aChild) override; + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool RecvSharedCompositorFrameMetrics(const mozilla::ipc::SharedMemoryBasic::Handle& metrics, + const CrossProcessMutexHandle& handle, + const uint64_t& aLayersId, + const uint32_t& aAPZCId) override; + + virtual bool RecvReleaseSharedCompositorFrameMetrics(const ViewID& aId, + const uint32_t& aAPZCId) override; + + virtual bool + RecvRemotePaintIsReady() override; + + bool RecvObserveLayerUpdate(const uint64_t& aLayersId, + const uint64_t& aEpoch, + const bool& aActive) override; + + // Class used to store the shared FrameMetrics, mutex, and APZCId in a hash table + class SharedFrameMetricsData { + public: + SharedFrameMetricsData( + const mozilla::ipc::SharedMemoryBasic::Handle& metrics, + const CrossProcessMutexHandle& handle, + const uint64_t& aLayersId, + const uint32_t& aAPZCId); + + ~SharedFrameMetricsData(); + + void CopyFrameMetrics(FrameMetrics* aFrame); + FrameMetrics::ViewID GetViewID(); + uint64_t 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; + uint64_t mLayersId; + // Unique ID of the APZC that is sharing the FrameMetrics + uint32_t mAPZCId; + }; + + RefPtr<LayerManager> mLayerManager; + // 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 TabChild that made a request to be alerted when + // the transaction has been received. + nsWeakPtr mWeakTabChild; // type is TabChild + + DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeChild); + + // When we receive overfill numbers, notify these client layer managers + AutoTArray<ClientLayerManager*,0> mOverfillObservers; + + // True until the beginning of the two-step shutdown sequence of this actor. + bool mCanSend; + + /** + * 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. + */ + nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingRecycled; + + MessageLoop* mMessageLoop; + + AutoTArray<RefPtr<TextureClientPool>,2> mTexturePools; + + uint64_t mProcessToken; + + FixedSizeSmallShmemSectionAllocator* mSectionAllocator; +}; + +} // 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 000000000..6728e8841 --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -0,0 +1,2441 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 et 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 "LayerTransactionParent.h" // for LayerTransactionParent +#include "RenderTrace.h" // for RenderTraceLayers +#include "base/message_loop.h" // for MessageLoop +#include "base/process.h" // for ProcessId +#include "base/task.h" // for CancelableTask, etc +#include "base/thread.h" // for Thread +#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 "gfxPrefs.h" // for gfxPrefs +#include "mozilla/AutoRestore.h" // for AutoRestore +#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown +#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/gfx/2D.h" // for DrawTarget +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/gfx/Rect.h" // for IntSize +#include "VRManager.h" // for VRManager +#include "mozilla/ipc/Transport.h" // for Transport +#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager +#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent +#include "mozilla/layers/APZThreadUtils.h" // for APZCTreeManager +#include "mozilla/layers/AsyncCompositionManager.h" +#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor +#include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/CrossProcessCompositorBridgeParent.h" +#include "mozilla/layers/FrameUniformityData.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/PLayerTransactionParent.h" +#include "mozilla/layers/RemoteContentController.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService +#include "mozilla/mozalloc.h" // for operator new, etc +#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 +#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop +#ifdef XP_WIN +#include "mozilla/layers/CompositorD3D11.h" +#include "mozilla/layers/CompositorD3D9.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" +#ifdef MOZ_ENABLE_PROFILER_SPS +#include "ProfilerMarkers.h" +#endif +#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 + +#include "LayerScope.h" + +namespace mozilla { + +namespace layers { + +using namespace mozilla::ipc; +using namespace mozilla::gfx; +using namespace std; + +using base::ProcessId; +using base::Thread; + +ProcessId +CompositorBridgeParentBase::GetChildProcessId() +{ + return OtherPid(); +} + +void +CompositorBridgeParentBase::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) +{ + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); + if (!texture) { + return; + } + + if (!(texture->GetFlags() & TextureFlags::RECYCLE)) { + return; + } + + uint64_t textureId = TextureHost::GetTextureSerial(aTexture); + mPendingAsyncMessage.push_back( + OpNotifyNotUsed(textureId, aTransactionId)); +} + +void +CompositorBridgeParentBase::SendAsyncMessage(const InfallibleTArray<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); +} + +void +CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem) +{ + PCompositorBridgeParent::DeallocShmem(aShmem); +} + +base::ProcessId +CompositorBridgeParentBase::RemotePid() +{ + return OtherPid(); +} + +bool +CompositorBridgeParentBase::StartSharingMetrics(ipc::SharedMemoryBasic::Handle aHandle, + CrossProcessMutexHandle aMutexHandle, + uint64_t aLayersId, + uint32_t aApzcId) +{ + return PCompositorBridgeParent::SendSharedCompositorFrameMetrics( + aHandle, aMutexHandle, aLayersId, aApzcId); +} + +bool +CompositorBridgeParentBase::StopSharingMetrics(FrameMetrics::ViewID aScrollId, + uint32_t aApzcId) +{ + return PCompositorBridgeParent::SendReleaseSharedCompositorFrameMetrics( + aScrollId, aApzcId); +} + +CompositorBridgeParent::LayerTreeState::LayerTreeState() + : mApzcTreeManagerParent(nullptr) + , mParent(nullptr) + , mLayerManager(nullptr) + , mCrossProcessParent(nullptr) + , mLayerTree(nullptr) + , mUpdatedPluginDataAvailable(false) + , mPendingCompositorUpdates(0) +{ +} + +CompositorBridgeParent::LayerTreeState::~LayerTreeState() +{ + if (mController) { + mController->Destroy(); + } +} + +typedef map<uint64_t, CompositorBridgeParent::LayerTreeState> LayerTreeMap; +static LayerTreeMap sIndirectLayerTrees; +static 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); + } + } +} + +/** + * 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 map<uint64_t,CompositorBridgeParent*> CompositorMap; +static StaticAutoPtr<CompositorMap> sCompositorMap; + +void +CompositorBridgeParent::Setup() +{ + EnsureLayerTreeMapReady(); + + MOZ_ASSERT(!sCompositorMap); + sCompositorMap = new CompositorMap; +} + +void +CompositorBridgeParent::Shutdown() +{ + MOZ_ASSERT(sCompositorMap); + MOZ_ASSERT(sCompositorMap->empty()); + sCompositorMap = nullptr; +} + +void +CompositorBridgeParent::FinishShutdown() +{ + // TODO: this should be empty by now... + sIndirectLayerTrees.clear(); +} + +static void SetThreadPriority() +{ + hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR); +} + +#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 = gfxPrefs::LayersCompositionFrameRate(); + if (compositionFrameRatePref < 0) { + // Use the same frame rate for composition as for layout. + int32_t layoutFrameRatePref = gfxPrefs::LayoutFrameRate(); + 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 + +CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner) + : mMutex("CompositorVsyncScheduler.Observer.Mutex") + , mOwner(aOwner) +{ +} + +CompositorVsyncScheduler::Observer::~Observer() +{ + MOZ_ASSERT(!mOwner); +} + +bool +CompositorVsyncScheduler::Observer::NotifyVsync(TimeStamp aVsyncTimestamp) +{ + MutexAutoLock lock(mMutex); + if (!mOwner) { + return false; + } + return mOwner->NotifyVsync(aVsyncTimestamp); +} + +void +CompositorVsyncScheduler::Observer::Destroy() +{ + MutexAutoLock lock(mMutex); + mOwner = nullptr; +} + +CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent, + widget::CompositorWidget* aWidget) + : mCompositorBridgeParent(aCompositorBridgeParent) + , mLastCompose(TimeStamp::Now()) + , mIsObservingVsync(false) + , mNeedsComposite(0) + , mVsyncNotificationsSkipped(0) + , mWidget(aWidget) + , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor") + , mCurrentCompositeTask(nullptr) + , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor") + , mSetNeedsCompositeTask(nullptr) +{ + MOZ_ASSERT(NS_IsMainThread() || XRE_GetProcessType() == GeckoProcessType_GPU); + mVsyncObserver = new Observer(this); + + // mAsapScheduling is set on the main thread during init, + // but is only accessed after on the compositor thread. + mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 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 + mCompositorBridgeParent = nullptr; +} + +void +CompositorVsyncScheduler::Destroy() +{ + if (!mVsyncObserver) { + // Destroy was already called on this object. + return; + } + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + UnobserveVsync(); + mVsyncObserver->Destroy(); + mVsyncObserver = nullptr; + + CancelCurrentSetNeedsCompositeTask(); + CancelCurrentCompositeTask(); +} + +void +CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp) +{ + // can be called from the compositor or vsync thread + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + if (mCurrentCompositeTask == nullptr && CompositorThreadHolder::Loop()) { + RefPtr<CancelableRunnable> task = + NewCancelableRunnableMethod<TimeStamp>(this, &CompositorVsyncScheduler::Composite, + aCompositeTimestamp); + mCurrentCompositeTask = task; + ScheduleTask(task.forget(), 0); + } +} + +void +CompositorVsyncScheduler::ScheduleComposition() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mAsapScheduling) { + // Used only for performance testing purposes + PostCompositeTask(TimeStamp::Now()); +#ifdef MOZ_WIDGET_ANDROID + } else if (mNeedsComposite >= 2 && mIsObservingVsync) { + // uh-oh, we already requested a composite at least twice so far, and a + // composite hasn't happened yet. It is possible that the vsync observation + // is blocked on the main thread, so let's just composite ASAP and not + // wait for the vsync. Note that this should only ever happen on Fennec + // because there content runs in the same process as the compositor, and so + // content can actually block the main thread in this process. + PostCompositeTask(TimeStamp::Now()); +#endif + } else { + SetNeedsComposite(); + } +} + +void +CompositorVsyncScheduler::CancelCurrentSetNeedsCompositeTask() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MonitorAutoLock lock(mSetNeedsCompositeMonitor); + if (mSetNeedsCompositeTask) { + mSetNeedsCompositeTask->Cancel(); + mSetNeedsCompositeTask = nullptr; + } + mNeedsComposite = 0; +} + +/** + * TODO Potential performance heuristics: + * If a composite takes 17 ms, do we composite ASAP or wait until next vsync? + * If a layer transaction comes after vsync, do we composite ASAP or wait until + * next vsync? + * How many skipped vsync events until we stop listening to vsync events? + */ +void +CompositorVsyncScheduler::SetNeedsComposite() +{ + if (!CompositorThreadHolder::IsInCompositorThread()) { + MonitorAutoLock lock(mSetNeedsCompositeMonitor); + RefPtr<CancelableRunnable> task = + NewCancelableRunnableMethod(this, &CompositorVsyncScheduler::SetNeedsComposite); + mSetNeedsCompositeTask = task; + ScheduleTask(task.forget(), 0); + return; + } else { + MonitorAutoLock lock(mSetNeedsCompositeMonitor); + mSetNeedsCompositeTask = nullptr; + } + + mNeedsComposite++; + if (!mIsObservingVsync && mNeedsComposite) { + ObserveVsync(); + } +} + +bool +CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp) +{ + // Called from the vsync dispatch thread. When in the GPU Process, that's + // the same as the compositor thread. + MOZ_ASSERT_IF(XRE_IsParentProcess(), !CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(!NS_IsMainThread()); + PostCompositeTask(aVsyncTimestamp); + return true; +} + +void +CompositorVsyncScheduler::CancelCurrentCompositeTask() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread()); + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + if (mCurrentCompositeTask) { + mCurrentCompositeTask->Cancel(); + mCurrentCompositeTask = nullptr; + } +} + +void +CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + { + MonitorAutoLock lock(mCurrentCompositeTaskMonitor); + mCurrentCompositeTask = nullptr; + } + + if ((aVsyncTimestamp < mLastCompose) && !mAsapScheduling) { + // 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; + } + + MOZ_ASSERT(mCompositorBridgeParent); + if (!mAsapScheduling && mCompositorBridgeParent->IsPendingComposite()) { + // If previous composite is still on going, finish it and does a next + // composite in a next vsync. + mCompositorBridgeParent->FinishPendingComposite(); + return; + } + + DispatchTouchEvents(aVsyncTimestamp); + DispatchVREvents(aVsyncTimestamp); + + if (mNeedsComposite || mAsapScheduling) { + mNeedsComposite = 0; + mLastCompose = aVsyncTimestamp; + ComposeToTarget(nullptr); + mVsyncNotificationsSkipped = 0; + + TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp; + mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME, + compositeFrameTotal.ToMilliseconds()); + } else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) { + UnobserveVsync(); + } +} + +void +CompositorVsyncScheduler::OnForceComposeToTarget() +{ + /** + * bug 1138502 - There are cases such as during long-running window resizing events + * where we receive many sync RecvFlushComposites. We also get vsync notifications which + * will increment mVsyncNotificationsSkipped because a composite just occurred. After + * enough vsyncs and RecvFlushComposites occurred, we will disable vsync. Then at the next + * ScheduleComposite, we will enable vsync, then get a RecvFlushComposite, which will + * force us to unobserve vsync again. 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. + */ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mVsyncNotificationsSkipped = 0; +} + +void +CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + OnForceComposeToTarget(); + mLastCompose = TimeStamp::Now(); + ComposeToTarget(aTarget, aRect); +} + +bool +CompositorVsyncScheduler::NeedsComposite() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return mNeedsComposite; +} + +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::DispatchTouchEvents(TimeStamp aVsyncTimestamp) +{ +} + +void +CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + VRManager* vm = VRManager::Get(); + vm->NotifyVsync(aVsyncTimestamp); +} + +void +CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask, + int aTime) +{ + MOZ_ASSERT(CompositorThreadHolder::Loop()); + MOZ_ASSERT(aTime >= 0); + CompositorThreadHolder::Loop()->PostDelayedTask(Move(aTask), aTime); +} + +void +CompositorVsyncScheduler::ResumeComposition() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mLastCompose = TimeStamp::Now(); + ComposeToTarget(nullptr); +} + +void +CompositorVsyncScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(mCompositorBridgeParent); + mCompositorBridgeParent->CompositeToTarget(aTarget, aRect); +} + +static inline MessageLoop* +CompositorLoop() +{ + return CompositorThreadHolder::Loop(); +} + +CompositorBridgeParent::CompositorBridgeParent(CSSToLayoutDeviceScale aScale, + const TimeDuration& aVsyncRate, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize) + : mWidget(nullptr) + , mScale(aScale) + , mVsyncRate(aVsyncRate) + , mIsTesting(false) + , mPendingTransaction(0) + , mPaused(false) + , mUseExternalSurfaceSize(aUseExternalSurfaceSize) + , mEGLSurfaceSize(aSurfaceSize) + , mPauseCompositionMonitor("PauseCompositionMonitor") + , mResumeCompositionMonitor("ResumeCompositionMonitor") + , mResetCompositorMonitor("ResetCompositorMonitor") + , mRootLayerTreeID(0) + , mOverrideComposeReadiness(false) + , mForceCompositionTask(nullptr) + , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) + , mCompositorScheduler(nullptr) + , mPaintTime(TimeDuration::Forever()) +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + , mLastPluginUpdateLayerTreeId(0) + , mDeferPluginWindows(false) + , mPluginWindowsHidden(false) +#endif +{ + // Always run destructor on the main thread + MOZ_ASSERT(NS_IsMainThread()); +} + +void +CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget, + const uint64_t& aLayerTreeId, + bool aUseAPZ) +{ + mWidget = aWidget; + mRootLayerTreeID = aLayerTreeId; + if (aUseAPZ) { + mApzcTreeManager = new APZCTreeManager(); + } + + // IPDL initialization. mSelfRef is cleared in DeferredDestroy. + SetOtherProcessId(base::GetCurrentProcId()); + mSelfRef = this; + + Initialize(); +} + +bool +CompositorBridgeParent::Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint) +{ + if (!aEndpoint.Bind(this)) { + return false; + } + mSelfRef = this; + return true; +} + +bool +CompositorBridgeParent::RecvInitialize(const uint64_t& aRootLayerTreeId) +{ + mRootLayerTreeID = aRootLayerTreeId; + + Initialize(); + return true; +} + +void +CompositorBridgeParent::Initialize() +{ + MOZ_ASSERT(CompositorThread(), + "The compositor thread must be Initialized before instanciating a CompositorBridgeParent."); + + mCompositorID = 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(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableFunction(&AddCompositor, + this, &mCompositorID)); + + CompositorLoop()->PostTask(NewRunnableFunction(SetThreadPriority)); + + + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[mRootLayerTreeID].mParent = this; + } + + LayerScope::SetPixelScale(mScale.scale); + + mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget); +} + +bool +CompositorBridgeParent::RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) +{ + Maybe<TextureFactoryIdentifier> newIdentifier; + ResetCompositorTask(aBackendHints, &newIdentifier); + + if (newIdentifier) { + *aResult = true; + *aOutIdentifier = newIdentifier.value(); + } else { + *aResult = false; + } + + return true; +} + +uint64_t +CompositorBridgeParent::RootLayerTreeId() +{ + MOZ_ASSERT(mRootLayerTreeID); + return mRootLayerTreeID; +} + +CompositorBridgeParent::~CompositorBridgeParent() +{ + InfallibleTArray<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() +{ + mCompositionManager->ForceIsFirstPaint(); +} + +void +CompositorBridgeParent::StopAndClearResources() +{ + if (mForceCompositionTask) { + mForceCompositionTask->Cancel(); + mForceCompositionTask = nullptr; + } + + mPaused = true; + + // Ensure that the layer manager is destroyed before CompositorBridgeChild. + if (mLayerManager) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([this] (LayerTreeState* lts, uint64_t) -> void { + mLayerManager->ClearCachedResources(lts->mRoot); + lts->mLayerManager = nullptr; + lts->mParent = nullptr; + }); + mLayerManager->Destroy(); + mLayerManager = nullptr; + mCompositionManager = nullptr; + } + + if (mCompositor) { + mCompositor->DetachWidget(); + mCompositor->Destroy(); + mCompositor = nullptr; + } + + // This must be destroyed now since it accesses the widget. + if (mCompositorScheduler) { + mCompositorScheduler->Destroy(); + mCompositorScheduler = nullptr; + } + + // After this point, it is no longer legal to access the widget. + mWidget = nullptr; +} + +bool +CompositorBridgeParent::RecvWillClose() +{ + StopAndClearResources(); + return true; +} + +void CompositorBridgeParent::DeferredDestroy() +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(mCompositorThreadHolder); + mCompositorThreadHolder = nullptr; + mSelfRef = nullptr; +} + +bool +CompositorBridgeParent::RecvPause() +{ + PauseComposition(); + return true; +} + +bool +CompositorBridgeParent::RecvResume() +{ + ResumeComposition(); + return true; +} + +bool +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 false; + } + ForceComposeToTarget(target, &aRect); + return true; +} + +bool +CompositorBridgeParent::RecvFlushRendering() +{ + if (mCompositorScheduler->NeedsComposite()) + { + CancelCurrentCompositeTask(); + ForceComposeToTarget(nullptr); + } + return true; +} + +bool +CompositorBridgeParent::RecvForcePresent() +{ + // During the shutdown sequence mLayerManager may be null + if (mLayerManager) { + mLayerManager->ForcePresent(); + } + return true; +} + +bool +CompositorBridgeParent::RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) +{ + if (mLayerManager) { + mLayerManager->AddInvalidRegion(aRegion); + } + return true; +} + +void +CompositorBridgeParent::Invalidate() +{ + if (mLayerManager && mLayerManager->GetRoot()) { + mLayerManager->AddInvalidRegion( + mLayerManager->GetRoot()->GetLocalVisibleRegion().ToUnknownRegion().GetBounds()); + } +} + +bool +CompositorBridgeParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) +{ + if (mLayerManager) { + *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize); + } else { + *aOutStartIndex = 0; + } + return true; +} + +bool +CompositorBridgeParent::RecvStopFrameTimeRecording(const uint32_t& aStartIndex, + InfallibleTArray<float>* intervals) +{ + if (mLayerManager) { + mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals); + } + return true; +} + +bool +CompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId, + const uint32_t& aPresShellId) +{ + ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId)); + return true; +} + +void +CompositorBridgeParent::ClearApproximatelyVisibleRegions(const uint64_t& aLayersId, + const Maybe<uint32_t>& aPresShellId) +{ + if (mLayerManager) { + mLayerManager->ClearApproximatelyVisibleRegions(aLayersId, aPresShellId); + + // We need to recomposite to update the minimap. + ScheduleComposition(); + } +} + +bool +CompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) +{ + if (mLayerManager) { + mLayerManager->UpdateApproximatelyVisibleRegion(aGuid, aRegion); + + // We need to recomposite to update the minimap. + ScheduleComposition(); + } + return true; +} + +void +CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) +{ + StopAndClearResources(); + + RemoveCompositor(mCompositorID); + + mCompositionManager = nullptr; + + if (mApzcTreeManager) { + mApzcTreeManager->ClearTree(); + mApzcTreeManager = 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; + MessageLoop::current()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::DeferredDestroy)); +} + +void +CompositorBridgeParent::ScheduleRenderOnCompositorThread() +{ + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ScheduleComposition)); +} + +void +CompositorBridgeParent::InvalidateOnCompositorThread() +{ + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::Invalidate)); +} + +void +CompositorBridgeParent::PauseComposition() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "PauseComposition() can only be called on the compositor thread"); + + MonitorAutoLock lock(mPauseCompositionMonitor); + + if (!mPaused) { + mPaused = true; + + mCompositor->Pause(); + + TimeStamp now = TimeStamp::Now(); + DidComposite(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); + + if (!mCompositor->Resume()) { +#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->ResumeComposition(); + + // 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::SetEGLSurfaceSize(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)); + } +} + +void +CompositorBridgeParent::ResumeCompositionAndResize(int width, int height) +{ + SetEGLSurfaceSize(width, height); + ResumeComposition(); +} + +/* + * This will execute a pause synchronously, waiting to make sure that the compositor + * really is paused. + */ +void +CompositorBridgeParent::SchedulePauseOnCompositorThread() +{ + MonitorAutoLock lock(mPauseCompositionMonitor); + + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::PauseComposition)); + + // Wait until the pause has actually been processed by the compositor thread + lock.Wait(); +} + +bool +CompositorBridgeParent::ScheduleResumeOnCompositorThread() +{ + MonitorAutoLock lock(mResumeCompositionMonitor); + + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ResumeComposition)); + + // Wait until the resume has actually been processed by the compositor thread + lock.Wait(); + + return !mPaused; +} + +bool +CompositorBridgeParent::ScheduleResumeOnCompositorThread(int width, int height) +{ + MonitorAutoLock lock(mResumeCompositionMonitor); + + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod + <int, int>(this, + &CompositorBridgeParent::ResumeCompositionAndResize, + width, height)); + + // Wait until the resume has actually been processed by the compositor thread + lock.Wait(); + + return !mPaused; +} + +void +CompositorBridgeParent::ScheduleTask(already_AddRefed<CancelableRunnable> task, int time) +{ + if (time == 0) { + MessageLoop::current()->PostTask(Move(task)); + } else { + MessageLoop::current()->PostDelayedTask(Move(task), time); + } +} + +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::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint, + 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 (mApzcTreeManager && aHitTestUpdate) { + mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID, + mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber); + } + + mLayerManager->NotifyShadowTreeTransaction(); + } + if (aScheduleComposite) { + ScheduleComposition(); + } +} + +void +CompositorBridgeParent::ScheduleComposition() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + if (mPaused) { + return; + } + + 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. + LayerComposite* layerComposite = layer->AsLayerComposite(); + // Set the layerComposite's base transform to the layer's base transform. + layerComposite->SetShadowBaseTransform(layer->GetBaseTransform()); + layerComposite->SetShadowTransformSetByAnimation(false); + layerComposite->SetShadowVisibleRegion(layer->GetVisibleRegion()); + layerComposite->SetShadowClipRect(layer->GetClipRect()); + layerComposite->SetShadowOpacity(layer->GetOpacity()); + layerComposite->SetShadowOpacitySetByAnimation(false); + } + ); +} + +void +CompositorBridgeParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect) +{ + profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START); + PROFILER_LABEL("CompositorBridgeParent", "Composite", + js::ProfileEntry::Category::GRAPHICS); + + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(), + "Composite can only be called on the compositor thread"); + TimeStamp start = TimeStamp::Now(); + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + TimeDuration scheduleDelta = TimeStamp::Now() - mCompositorScheduler->GetExpectedComposeStartTime(); + if (scheduleDelta > TimeDuration::FromMilliseconds(2) || + scheduleDelta < TimeDuration::FromMilliseconds(-2)) { + printf_stderr("Compositor: Compose starting off schedule by %4.1f ms\n", + scheduleDelta.ToMilliseconds()); + } +#endif + + if (!CanComposite()) { + TimeStamp end = TimeStamp::Now(); + DidComposite(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 + + if (aTarget) { + mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect); + } else { + mLayerManager->BeginTransaction(); + } + + SetShadowProperties(mLayerManager->GetRoot()); + + if (mForceCompositionTask && !mOverrideComposeReadiness) { + if (mCompositionManager->ReadyForCompose()) { + mForceCompositionTask->Cancel(); + mForceCompositionTask = nullptr; + } else { + return; + } + } + + mCompositionManager->ComputeRotation(); + + TimeStamp time = mIsTesting ? 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() + (mVsyncRate * 2); + } +#endif + } + + RenderTraceLayers(mLayerManager->GetRoot(), "0000"); + +#ifdef MOZ_DUMP_PAINTING + if (gfxPrefs::DumpHostLayers()) { + printf_stderr("Painting --- compositing layer tree:\n"); + mLayerManager->Dump(/* aSorted = */ true); + } +#endif + mLayerManager->SetDebugOverlayWantsNextFrame(false); + mLayerManager->EndTransaction(time); + + if (!aTarget) { + TimeStamp end = TimeStamp::Now(); + DidComposite(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". + if (!mCompositor->GetCompositeUntilTime().IsNull() || + mLayerManager->DebugOverlayWantsNextFrame()) { + ScheduleComposition(); + } + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + TimeDuration executionTime = TimeStamp::Now() - mCompositorScheduler->GetLastComposeTime(); + 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 (gfxPrefs::LayersCompositionFrameRate() == 0 + || mLayerManager->GetCompositor()->GetDiagnosticTypes() & DiagnosticTypes::FLASH_BORDERS) { + // Special full-tilt composite mode for performance testing + ScheduleComposition(); + } + mCompositor->SetCompositionTime(TimeStamp()); + + mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, start); + profiler_tracing("Paint", "Composite", TRACING_INTERVAL_END); +} + +bool +CompositorBridgeParent::RecvRemotePluginsReady() +{ +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + mWaitForPluginsUntil = TimeStamp(); + if (mHaveBlockedForPlugins) { + mHaveBlockedForPlugins = false; + ForceComposeToTarget(nullptr); + } else { + ScheduleComposition(); + } + return true; +#else + NS_NOTREACHED("CompositorBridgeParent::RecvRemotePluginsReady calls " + "unexpected on this platform."); + return false; +#endif +} + +void +CompositorBridgeParent::ForceComposeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect) +{ + PROFILER_LABEL("CompositorBridgeParent", "ForceComposeToTarget", + js::ProfileEntry::Category::GRAPHICS); + + AutoRestore<bool> override(mOverrideComposeReadiness); + mOverrideComposeReadiness = true; + mCompositorScheduler->ForceComposeToTarget(aTarget, aRect); +} + +PAPZCTreeManagerParent* +CompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) +{ + // The main process should pass in 0 because we assume mRootLayerTreeID + MOZ_ASSERT(aLayersId == 0); + + // This message doubles as initialization + MOZ_ASSERT(!mApzcTreeManager); + mApzcTreeManager = new APZCTreeManager(); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID]; + MOZ_ASSERT(state.mParent); + MOZ_ASSERT(!state.mApzcTreeManagerParent); + state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, state.mParent->GetAPZCTreeManager()); + + return state.mApzcTreeManagerParent; +} + +bool +CompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) +{ + delete aActor; + return true; +} + +PAPZParent* +CompositorBridgeParent::AllocPAPZParent(const uint64_t& aLayersId) +{ + // The main process should pass in 0 because we assume mRootLayerTreeID + MOZ_ASSERT(aLayersId == 0); + + 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_ASSERT(!state.mController); + state.mController = controller; + + return controller; +} + +bool +CompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor) +{ + RemoteContentController* controller = static_cast<RemoteContentController*>(aActor); + controller->Release(); + return true; +} + +bool +CompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ) +{ + // The main process should pass in 0 because we assume mRootLayerTreeID + MOZ_ASSERT(aLayersId == 0); + *aHasAPZ = AsyncPanZoomEnabled(); + return true; +} + +RefPtr<APZCTreeManager> +CompositorBridgeParent::GetAPZCTreeManager() +{ + return mApzcTreeManager; +} + +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(this, &CompositorBridgeParent::ForceComposition); + mForceCompositionTask = task; + ScheduleTask(task.forget(), gfxPrefs::OrientationSyncMillis()); + } +} + +void +CompositorBridgeParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const uint64_t& aTransactionId, + const TargetConfig& aTargetConfig, + const InfallibleTArray<PluginWindowData>& aUnused, + bool aIsFirstPaint, + bool aScheduleComposite, + uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + int32_t aPaintSyncId, + bool aHitTestUpdate) +{ + ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint); + + // 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(aTargetConfig.naturalBounds()); + mLayerManager->SetRegionToClear(aTargetConfig.clearRegion()); + mLayerManager->GetCompositor()->SetScreenRotation(aTargetConfig.rotation()); + + mCompositionManager->Updated(aIsFirstPaint, aTargetConfig, aPaintSyncId); + Layer* root = aLayerTree->GetRoot(); + mLayerManager->SetRoot(root); + + if (mApzcTreeManager && !aIsRepeatTransaction && aHitTestUpdate) { + AutoResolveRefLayers resolve(mCompositionManager); + + mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID, root, aIsFirstPaint, + mRootLayerTreeID, aPaintSequenceNumber); + } + + // 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(aTransactionId == 1 || aTransactionId > mPendingTransaction); + mPendingTransaction = aTransactionId; + + if (root) { + SetShadowProperties(root); + } + if (aScheduleComposite) { + ScheduleComposition(); + if (mPaused) { + TimeStamp now = TimeStamp::Now(); + DidComposite(now, now); + } + } + mLayerManager->NotifyShadowTreeTransaction(); +} + +void +CompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree) +{ + ScheduleComposition(); +} + +bool +CompositorBridgeParent::SetTestSampleTime(LayerTransactionParent* aLayerTree, + const TimeStamp& aTime) +{ + if (aTime.IsNull()) { + return false; + } + + mIsTesting = true; + mTestTime = aTime; + + bool testComposite = mCompositionManager && + mCompositorScheduler->NeedsComposite(); + + // Update but only if we were already scheduled to animate + if (testComposite) { + AutoResolveRefLayers resolve(mCompositionManager); + bool requestNextFrame = mCompositionManager->TransformShadowTree(aTime, mVsyncRate); + if (!requestNextFrame) { + CancelCurrentCompositeTask(); + // Pretend we composited in case someone is wating for this event. + TimeStamp now = TimeStamp::Now(); + DidComposite(now, now); + } + } + + return true; +} + +void +CompositorBridgeParent::LeaveTestMode(LayerTransactionParent* aLayerTree) +{ + mIsTesting = false; +} + +void +CompositorBridgeParent::ApplyAsyncProperties(LayerTransactionParent* aLayerTree) +{ + // NOTE: This should only be used for testing. For example, when mIsTesting is + // true or when called from test-only methods like + // LayerTransactionParent::RecvGetAnimationTransform. + + // Synchronously update the layer tree + if (aLayerTree->GetRoot()) { + AutoResolveRefLayers resolve(mCompositionManager); + SetShadowProperties(mLayerManager->GetRoot()); + + TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime(); + bool requestNextFrame = + mCompositionManager->TransformShadowTree(time, mVsyncRate, + AsyncCompositionManager::TransformsToSkip::APZ); + if (!requestNextFrame) { + CancelCurrentCompositeTask(); + // Pretend we composited in case someone is waiting for this event. + TimeStamp now = TimeStamp::Now(); + DidComposite(now, now); + } + } +} + +bool +CompositorBridgeParent::RecvGetFrameUniformity(FrameUniformityData* aOutData) +{ + mCompositionManager->GetFrameUniformity(aOutData); + return true; +} + +bool +CompositorBridgeParent::RecvRequestOverfill() +{ + uint32_t overfillRatio = mCompositor->GetFillRatio(); + Unused << SendOverfill(overfillRatio); + return true; +} + +void +CompositorBridgeParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree) +{ + MOZ_ASSERT(mApzcTreeManager); + uint64_t layersId = aLayerTree->GetId(); + if (layersId == 0) { + // The request is coming from the parent-process layer tree, so we should + // use the compositor's root layer tree id. + layersId = mRootLayerTreeID; + } + RefPtr<CompositorBridgeParent> self = this; + APZThreadUtils::RunOnControllerThread(NS_NewRunnableFunction([=] () { + self->mApzcTreeManager->FlushApzRepaints(layersId); + })); +} + +void +CompositorBridgeParent::GetAPZTestData(const LayerTransactionParent* aLayerTree, + APZTestData* aOutData) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + *aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData; +} + +void +CompositorBridgeParent::SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree, + const uint64_t& aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) +{ + if (!mApzcTreeManager) { + 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, StoreCopyPassByConstLRef<nsTArray<ScrollableLayerGuid>>> + (mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, aTargets); + APZThreadUtils::RunOnControllerThread(task.forget()); + +} + +void +CompositorBridgeParent::InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints) +{ + NS_ASSERTION(!mLayerManager, "Already initialised mLayerManager"); + NS_ASSERTION(!mCompositor, "Already initialised mCompositor"); + + mCompositor = NewCompositor(aBackendHints); + if (!mCompositor) { + return; + } + + mLayerManager = new LayerManagerComposite(mCompositor); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = mLayerManager; +} + +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); + } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9) { + compositor = new CompositorD3D9(this, mWidget); +#endif + } + nsCString failureReason; + 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_D3D9){ + Telemetry::Accumulate(Telemetry::D3D9_COMPOSITING_FAILURE_ID, failureReason); + } + else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11){ + Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID, failureReason); + } +#endif + + compositor->SetCompositorID(mCompositorID); + 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_D3D9){ + gfxCriticalNote << "[D3D9] Failed to init compositor with reason: " + << failureReason.get(); + Telemetry::Accumulate(Telemetry::D3D9_COMPOSITING_FAILURE_ID, failureReason); + } + 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 uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool *aSuccess) +{ + MOZ_ASSERT(aId == 0); + + InitializeLayerManager(aBackendHints); + + if (!mLayerManager) { + NS_WARNING("Failed to initialise Compositor"); + *aSuccess = false; + LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, 0); + p->AddIPDLReference(); + return p; + } + + mCompositionManager = new AsyncCompositionManager(mLayerManager); + *aSuccess = true; + + *aTextureFactoryIdentifier = mCompositor->GetTextureFactoryIdentifier(); + LayerTransactionParent* p = new LayerTransactionParent(mLayerManager, this, 0); + p->AddIPDLReference(); + return p; +} + +bool +CompositorBridgeParent::DeallocPLayerTransactionParent(PLayerTransactionParent* actor) +{ + static_cast<LayerTransactionParent*>(actor)->ReleaseIPDLReference(); + return true; +} + +CompositorBridgeParent* CompositorBridgeParent::GetCompositorBridgeParent(uint64_t id) +{ + CompositorMap::iterator it = sCompositorMap->find(id); + return it != sCompositorMap->end() ? it->second : nullptr; +} + +void CompositorBridgeParent::AddCompositor(CompositorBridgeParent* compositor, uint64_t* outID) +{ + static uint64_t sNextID = 1; + + ++sNextID; + (*sCompositorMap)[sNextID] = compositor; + *outID = sNextID; +} + +CompositorBridgeParent* CompositorBridgeParent::RemoveCompositor(uint64_t id) +{ + 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 TimeStamp& aTimeStamp, const uint64_t& 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(aTimeStamp); +} + +bool +CompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + NotifyChildCreated(child); + return true; +} + +bool +CompositorBridgeParent::RecvNotifyChildRecreated(const uint64_t& aChild) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + + if (sIndirectLayerTrees.find(aChild) != sIndirectLayerTrees.end()) { + // Invalid to register the same layer tree twice. + return false; + } + + NotifyChildCreated(aChild); + return true; +} + +void +CompositorBridgeParent::NotifyChildCreated(uint64_t aChild) +{ + sIndirectLayerTreesLock->AssertCurrentThreadOwns(); + sIndirectLayerTrees[aChild].mParent = this; + sIndirectLayerTrees[aChild].mLayerManager = mLayerManager; +} + +bool +CompositorBridgeParent::RecvAdoptChild(const uint64_t& child) +{ + APZCTreeManagerParent* parent; + { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + NotifyChildCreated(child); + if (sIndirectLayerTrees[child].mLayerTree) { + sIndirectLayerTrees[child].mLayerTree->SetLayerManager(mLayerManager); + } + parent = sIndirectLayerTrees[child].mApzcTreeManagerParent; + } + + if (mApzcTreeManager && parent) { + parent->ChildAdopted(mApzcTreeManager); + } + return true; +} + +static void +EraseLayerState(uint64_t aId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + + auto iter = sIndirectLayerTrees.find(aId); + if (iter != sIndirectLayerTrees.end()) { + CompositorBridgeParent* parent = iter->second.mParent; + if (parent) { + parent->ClearApproximatelyVisibleRegions(aId, Nothing()); + } + + sIndirectLayerTrees.erase(iter); + } +} + +/*static*/ void +CompositorBridgeParent::DeallocateLayerTreeId(uint64_t 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 (!CompositorLoop()) { + gfxCriticalError() << "Attempting to post to a invalid Compositor Loop"; + return; + } + CompositorLoop()->PostTask(NewRunnableFunction(&EraseLayerState, aId)); +} + +static void +UpdateControllerForLayersId(uint64_t aLayersId, + GeckoContentController* aController) +{ + // Adopt ref given to us by SetControllerForLayerTree() + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[aLayersId].mController = + already_AddRefed<GeckoContentController>(aController); +} + +ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(APZCTreeManager* aApzctm, + uint64_t 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(uint64_t aLayersId, + GeckoContentController* aController) +{ + // This ref is adopted by UpdateControllerForLayersId(). + aController->AddRef(); + CompositorLoop()->PostTask(NewRunnableFunction(&UpdateControllerForLayersId, + aLayersId, + aController)); +} + +/*static*/ already_AddRefed<APZCTreeManager> +CompositorBridgeParent::GetAPZCTreeManager(uint64_t aLayersId) +{ + EnsureLayerTreeMapReady(); + MonitorAutoLock lock(*sIndirectLayerTreesLock); + LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aLayersId); + if (sIndirectLayerTrees.end() == cit) { + return nullptr; + } + LayerTreeState* lts = &cit->second; + + RefPtr<APZCTreeManager> apzctm = lts->mParent + ? lts->mParent->mApzcTreeManager.get() + : nullptr; + return apzctm.forget(); +} + +static void +InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) +{ +#ifdef MOZ_ENABLE_PROFILER_SPS + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + VsyncPayload* payload = new VsyncPayload(aVsyncTimestamp); + PROFILER_MARKER_PAYLOAD("VsyncTimestamp", payload); +#endif +} + +/*static */ void +CompositorBridgeParent::PostInsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) +{ + // Called in the vsync thread + if (profiler_is_active() && CompositorThreadHolder::IsActive()) { + CompositorLoop()->PostTask( + NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp)); + } +} + +widget::PCompositorWidgetParent* +CompositorBridgeParent::AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) +{ +#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING) + if (mWidget) { + // Should not create two widgets on the same compositor. + return nullptr; + } + + widget::CompositorWidgetParent* widget = + new widget::CompositorWidgetParent(aInitData); + 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 mCrossProcessParent; +} + +MetricsSharingController* +CompositorBridgeParent::LayerTreeState::InProcessSharingController() const +{ + return mParent; +} + +void +CompositorBridgeParent::DidComposite(TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd) +{ + Unused << SendDidComposite(0, mPendingTransaction, aCompositeStart, aCompositeEnd); + mPendingTransaction = 0; + + if (mLayerManager) { + nsTArray<ImageCompositeNotification> notifications; + mLayerManager->ExtractImageCompositeNotifications(¬ifications); + if (!notifications.IsEmpty()) { + Unused << ImageBridgeParent::NotifyImageComposites(notifications); + } + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([&] (LayerTreeState* lts, const uint64_t& aLayersId) -> void { + if (lts->mCrossProcessParent && lts->mParent == this) { + CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent; + cpcp->DidComposite(aLayersId, aCompositeStart, aCompositeEnd); + } + }); +} + +void +CompositorBridgeParent::InvalidateRemoteLayers() +{ + MOZ_ASSERT(CompositorLoop() == MessageLoop::current()); + + Unused << PCompositorBridgeParent::SendInvalidateLayers(0); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([] (LayerTreeState* lts, const uint64_t& aLayersId) -> void { + if (lts->mCrossProcessParent) { + CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent; + Unused << cpcp->SendInvalidateLayers(aLayersId); + } + }); +} + +bool +CompositorBridgeParent::ResetCompositor(const nsTArray<LayersBackend>& aBackendHints, + TextureFactoryIdentifier* aOutIdentifier) +{ + Maybe<TextureFactoryIdentifier> newIdentifier; + { + MonitorAutoLock lock(mResetCompositorMonitor); + + CompositorLoop()->PostTask(NewRunnableMethod + <StoreCopyPassByConstLRef<nsTArray<LayersBackend>>, + Maybe<TextureFactoryIdentifier>*>(this, + &CompositorBridgeParent::ResetCompositorTask, + aBackendHints, + &newIdentifier)); + + mResetCompositorMonitor.Wait(); + } + + if (!newIdentifier) { + return false; + } + + *aOutIdentifier = newIdentifier.value(); + return true; +} + +// Invoked on the compositor thread. The main thread is waiting on the given +// monitor. +void +CompositorBridgeParent::ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints, + Maybe<TextureFactoryIdentifier>* aOutNewIdentifier) +{ + // Perform the reset inside a lock, so the main thread can wake up as soon as + // possible. We notify child processes (if necessary) outside the lock. + Maybe<TextureFactoryIdentifier> newIdentifier; + { + MonitorAutoLock lock(mResetCompositorMonitor); + + newIdentifier = ResetCompositorImpl(aBackendHints); + *aOutNewIdentifier = newIdentifier; + + mResetCompositorMonitor.NotifyAll(); + } + + // NOTE: |aBackendHints|, and |aOutNewIdentifier| are now all invalid since + // they are allocated on ResetCompositor's stack on the main thread, which + // is no longer waiting on the lock. + + if (!newIdentifier) { + // No compositor change; nothing to do. + return; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + ForEachIndirectLayerTree([&] (LayerTreeState* lts, uint64_t layersId) -> void { + if (CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent) { + Unused << cpcp->SendCompositorUpdated(layersId, newIdentifier.value()); + + if (LayerTransactionParent* ltp = lts->mLayerTree) { + ltp->AddPendingCompositorUpdate(); + } + lts->mPendingCompositorUpdates++; + } + }); +} + +Maybe<TextureFactoryIdentifier> +CompositorBridgeParent::ResetCompositorImpl(const nsTArray<LayersBackend>& aBackendHints) +{ + if (!mLayerManager) { + return Nothing(); + } + + RefPtr<Compositor> compositor = NewCompositor(aBackendHints); + if (!compositor) { + MOZ_RELEASE_ASSERT(compositor, "Failed to reset compositor."); + } + + // Don't bother changing from basic->basic. + if (mCompositor && + mCompositor->GetBackendType() == LayersBackend::LAYERS_BASIC && + compositor->GetBackendType() == LayersBackend::LAYERS_BASIC) + { + return Nothing(); + } + + if (mCompositor) { + mCompositor->SetInvalid(); + } + mCompositor = compositor; + mLayerManager->ChangeCompositor(compositor); + + return Some(compositor->GetTextureFactoryIdentifier()); +} + +static void +OpenCompositor(RefPtr<CrossProcessCompositorBridgeParent> aCompositor, + Endpoint<PCompositorBridgeParent>&& aEndpoint) +{ + aCompositor->Bind(Move(aEndpoint)); +} + +/* static */ bool +CompositorBridgeParent::CreateForContent(Endpoint<PCompositorBridgeParent>&& aEndpoint) +{ + gfxPlatform::InitLayersIPC(); + + RefPtr<CrossProcessCompositorBridgeParent> cpcp = + new CrossProcessCompositorBridgeParent(); + + CompositorLoop()->PostTask(NewRunnableFunction(OpenCompositor, cpcp, Move(aEndpoint))); + return true; +} + +static void +UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + sIndirectLayerTrees[aId].mRoot = aRoot; + sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig; +} + +/* static */ CompositorBridgeParent::LayerTreeState* +CompositorBridgeParent::GetIndirectShadowTree(uint64_t aId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId); + if (sIndirectLayerTrees.end() == cit) { + return nullptr; + } + return &cit->second; +} + +static CompositorBridgeParent::LayerTreeState* +GetStateForRoot(uint64_t 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) { + uint64_t rootLayersId = state->mParent->RootLayerTreeId(); + itr = sIndirectLayerTrees.find(rootLayersId); + state = (sIndirectLayerTrees.end() != itr) ? &itr->second : nullptr; + } + + return state; +} + +/* static */ APZCTreeManagerParent* +CompositorBridgeParent::GetApzcTreeManagerParentForRoot(uint64_t aContentLayersId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState* state = + GetStateForRoot(aContentLayersId, lock); + return state ? state->mApzcTreeManagerParent : nullptr; +} + +/* static */ GeckoContentController* +CompositorBridgeParent::GetGeckoContentControllerForRoot(uint64_t aContentLayersId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState* state = + GetStateForRoot(aContentLayersId, lock); + return state ? state->mController.get() : nullptr; +} + +PTextureParent* +CompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId, + const uint64_t& aSerial) +{ + return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial); +} + +bool +CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) +{ + return TextureHost::DestroyIPDLActor(actor); +} + +bool +CompositorBridgeParent::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + +#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(uint64_t 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; + return true; +} + +void +CompositorBridgeParent::ScheduleShowAllPluginWindows() +{ + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ShowAllPluginWindows)); +} + +void +CompositorBridgeParent::ShowAllPluginWindows() +{ + MOZ_ASSERT(!NS_IsMainThread()); + mDeferPluginWindows = false; + ScheduleComposition(); +} + +void +CompositorBridgeParent::ScheduleHideAllPluginWindows() +{ + MOZ_ASSERT(CompositorLoop()); + CompositorLoop()->PostTask(NewRunnableMethod(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) + +bool +CompositorBridgeParent::RecvAllPluginsCaptured() +{ +#if defined(XP_WIN) + mWaitForPluginsUntil = TimeStamp(); + mHaveBlockedForPlugins = false; + ForceComposeToTarget(nullptr); + Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey()); + return true; +#else + MOZ_ASSERT_UNREACHABLE( + "CompositorBridgeParent::RecvAllPluginsCaptured calls unexpected."); + return false; +#endif +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h new file mode 100644 index 000000000..58052003f --- /dev/null +++ b/gfx/layers/ipc/CompositorBridgeParent.h @@ -0,0 +1,688 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=4 ts=8 et 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 "Layers.h" // for Layer +#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 +#include "mozilla/Attributes.h" // for override +#include "mozilla/Maybe.h" +#include "mozilla/Monitor.h" // for Monitor +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/TimeStamp.h" // for TimeStamp +#include "mozilla/dom/ipc/IdType.h" +#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/GeckoContentController.h" +#include "mozilla/layers/ISurfaceAllocator.h" // for ShmemAllocator +#include "mozilla/layers/LayersMessages.h" // for TargetConfig +#include "mozilla/layers/MetricsSharingController.h" +#include "mozilla/layers/PCompositorBridgeParent.h" +#include "mozilla/layers/APZTestData.h" +#include "mozilla/widget/CompositorWidget.h" +#include "nsISupportsImpl.h" +#include "ThreadSafeRefcountingWithMainThreadDestruction.h" +#include "mozilla/VsyncDispatcher.h" + +class MessageLoop; +class nsIWidget; + +namespace mozilla { + +class CancelableRunnable; + +namespace gfx { +class DrawTarget; +class GPUProcessManager; +class GPUParent; +} // namespace gfx + +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layers { + +class APZCTreeManager; +class APZCTreeManagerParent; +class AsyncCompositionManager; +class Compositor; +class CompositorBridgeParent; +class LayerManagerComposite; +class LayerTransactionParent; +class PAPZParent; +class CrossProcessCompositorBridgeParent; +class CompositorThreadHolder; +class InProcessCompositorSession; + +struct ScopedLayerTreeRegistration +{ + ScopedLayerTreeRegistration(APZCTreeManager* aApzctm, + uint64_t aLayersId, + Layer* aRoot, + GeckoContentController* aController); + ~ScopedLayerTreeRegistration(); + +private: + uint64_t mLayersId; +}; + +/** + * 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: + explicit CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent, + widget::CompositorWidget* aWidget); + + bool NotifyVsync(TimeStamp aVsyncTimestamp); + void SetNeedsComposite(); + void OnForceComposeToTarget(); + + void ScheduleTask(already_AddRefed<CancelableRunnable>, int); + void ResumeComposition(); + void ComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr); + void PostCompositeTask(TimeStamp aCompositeTimestamp); + void Destroy(); + void ScheduleComposition(); + void CancelCurrentCompositeTask(); + bool NeedsComposite(); + void Composite(TimeStamp aVsyncTimestamp); + void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect); + + const TimeStamp& GetLastComposeTime() + { + return mLastCompose; + } + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + const TimeStamp& GetExpectedComposeStartTime() + { + return mExpectedComposeStartTime; + } +#endif + +private: + virtual ~CompositorVsyncScheduler(); + + void NotifyCompositeTaskExecuted(); + void ObserveVsync(); + void UnobserveVsync(); + void DispatchTouchEvents(TimeStamp aVsyncTimestamp); + void DispatchVREvents(TimeStamp aVsyncTimestamp); + void CancelCurrentSetNeedsCompositeTask(); + + class Observer final : public VsyncObserver + { + public: + explicit Observer(CompositorVsyncScheduler* aOwner); + virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override; + void Destroy(); + private: + virtual ~Observer(); + + Mutex mMutex; + // Hold raw pointer to avoid mutual reference. + CompositorVsyncScheduler* mOwner; + }; + + CompositorBridgeParent* mCompositorBridgeParent; + TimeStamp mLastCompose; + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + TimeStamp mExpectedComposeStartTime; +#endif + + bool mAsapScheduling; + bool mIsObservingVsync; + uint32_t mNeedsComposite; + int32_t mVsyncNotificationsSkipped; + widget::CompositorWidget* mWidget; + RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver; + + mozilla::Monitor mCurrentCompositeTaskMonitor; + RefPtr<CancelableRunnable> mCurrentCompositeTask; + + mozilla::Monitor mSetNeedsCompositeMonitor; + RefPtr<CancelableRunnable> mSetNeedsCompositeTask; +}; + +class CompositorBridgeParentBase : public PCompositorBridgeParent, + public HostIPCAllocator, + public ShmemAllocator, + public MetricsSharingController +{ +public: + virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const uint64_t& aTransactionId, + const TargetConfig& aTargetConfig, + const InfallibleTArray<PluginWindowData>& aPlugins, + bool aIsFirstPaint, + bool aScheduleComposite, + uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + int32_t aPaintSyncId, + bool aHitTestUpdate) = 0; + + virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) { return nullptr; } + + virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) { } + + virtual void ForceComposite(LayerTransactionParent* aLayerTree) { } + virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree, + const TimeStamp& aTime) { return true; } + virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) { } + virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0; + virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) = 0; + virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree, + APZTestData* aOutData) { } + virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree, + const uint64_t& aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) = 0; + virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) {} + + virtual ShmemAllocator* AsShmemAllocator() override { return this; } + + virtual bool RecvSyncWithCompositor() override { return true; } + + // HostIPCAllocator + virtual base::ProcessId GetChildProcessId() override; + virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override; + virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override; + + // ShmemAllocator + virtual bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) override; + virtual bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aType, + mozilla::ipc::Shmem* aShmem) override; + virtual void 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, + uint64_t aLayersId, + uint32_t aApzcId) override; + bool StopSharingMetrics(FrameMetrics::ViewID aScrollId, + uint32_t aApzcId) override; +}; + +class CompositorBridgeParent final : public CompositorBridgeParentBase + , public CompositorController +{ + friend class CompositorVsyncScheduler; + friend class CompositorThreadHolder; + friend class InProcessCompositorSession; + friend class gfx::GPUProcessManager; + friend class gfx::GPUParent; + +public: + NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return CompositorBridgeParentBase::AddRef(); } + NS_IMETHOD_(MozExternalRefCountType) Release() override { return CompositorBridgeParentBase::Release(); } + + explicit CompositorBridgeParent(CSSToLayoutDeviceScale aScale, + const TimeDuration& aVsyncRate, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize); + + // Must only be called by CompositorBridgeChild. After invoking this, the + // IPC channel is active and RecvWillStop/ActorDestroy must be called to + // free the compositor. + void InitSameProcess(widget::CompositorWidget* aWidget, + const uint64_t& aLayerTreeId, + bool aUseAPZ); + + // Must only be called by GPUParent. After invoking this, the IPC channel + // is active and RecvWillStop/ActorDestroy must be called to free the + // compositor. + bool Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint); + + virtual bool RecvInitialize(const uint64_t& aRootLayerTreeId) override; + virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) override; + virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override; + virtual bool RecvRequestOverfill() override; + virtual bool RecvWillClose() override; + virtual bool RecvPause() override; + virtual bool RecvResume() override; + virtual bool RecvNotifyChildCreated(const uint64_t& child) override; + virtual bool RecvNotifyChildRecreated(const uint64_t& child) override; + virtual bool RecvAdoptChild(const uint64_t& child) override; + virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, + const gfx::IntRect& aRect) override; + virtual bool RecvFlushRendering() override; + virtual bool RecvForcePresent() override; + + virtual bool RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) override { + MOZ_ASSERT_UNREACHABLE("This message is only sent cross-process"); + return true; + } + + virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override; + virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override; + virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override; + + // Unused for chrome <-> compositor communication (which this class does). + // @see CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint + virtual bool RecvRequestNotifyAfterRemotePaint() override { return true; }; + + virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId, + const uint32_t& aPresShellId) override; + void ClearApproximatelyVisibleRegions(const uint64_t& aLayersId, + const Maybe<uint32_t>& aPresShellId); + virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) override; + + virtual bool RecvAllPluginsCaptured() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const uint64_t& aTransactionId, + const TargetConfig& aTargetConfig, + const InfallibleTArray<PluginWindowData>& aPlugins, + bool aIsFirstPaint, + bool aScheduleComposite, + uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + int32_t aPaintSyncId, + bool aHitTestUpdate) override; + virtual void ForceComposite(LayerTransactionParent* aLayerTree) override; + virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree, + const TimeStamp& aTime) override; + virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) override; + virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) + override; + virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) override; + virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree, + APZTestData* aOutData) override; + virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree, + const uint64_t& aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) override; + virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) override { return mCompositionManager; } + + virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId, + const uint64_t& aSerial) override; + virtual bool DeallocPTextureParent(PTextureParent* actor) override; + + virtual bool IsSameProcess() const override; + + + PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override; + bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override; + + /** + * Request that the compositor be recreated due to a shared device reset. + * This must be called on the main thread, and blocks until a task posted + * to the compositor thread has completed. + * + * Note that this posts a task directly, rather than using synchronous + * IPDL, and waits on a monitor notification from the compositor thread. + * We do this as a best-effort attempt to jump any IPDL messages that + * have not yet been posted (and are sitting around in the IO pipe), to + * minimize the amount of time the main thread is blocked. + */ + bool ResetCompositor(const nsTArray<LayersBackend>& aBackendHints, + TextureFactoryIdentifier* aOutIdentifier); + + /** + * 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(uint64_t aChild); + + void AsyncRender(); + + // Can be called from any thread + void ScheduleRenderOnCompositorThread() override; + void SchedulePauseOnCompositorThread(); + void InvalidateOnCompositorThread(); + /** + * Returns true if a surface was obtained and the resume succeeded; false + * otherwise. + */ + bool ScheduleResumeOnCompositorThread(); + bool ScheduleResumeOnCompositorThread(int width, int height); + + virtual void ScheduleComposition(); + void NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint, + bool aScheduleComposite, uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, bool aHitTestUpdate); + + void UpdatePaintTime(LayerTransactionParent* aLayerTree, + const TimeDuration& aPaintTime) 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); + + /** + * Returns the unique layer tree identifier that corresponds to the root + * tree of this compositor. + */ + uint64_t 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(); + + /** + * 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 TimeStamp& aTimeStamp, const uint64_t& aLayersId); + + /** + * Set aController as the pan/zoom callback for the subtree referred + * to by aLayersId. + * + * Must run on content main thread. + */ + static void SetControllerForLayerTree(uint64_t aLayersId, + GeckoContentController* aController); + + /** + * A new child process has been configured to push transactions + * directly to us. Transport is to its thread context. + */ + static bool + CreateForContent(Endpoint<PCompositorBridgeParent>&& aEndpoint); + + struct LayerTreeState { + LayerTreeState(); + ~LayerTreeState(); + RefPtr<Layer> mRoot; + RefPtr<GeckoContentController> mController; + APZCTreeManagerParent* mApzcTreeManagerParent; + CompositorBridgeParent* mParent; + LayerManagerComposite* mLayerManager; + // Pointer to the CrossProcessCompositorBridgeParent. Used by APZCs to share + // their FrameMetrics with the corresponding child process that holds + // the PCompositorBridgeChild + CrossProcessCompositorBridgeParent* mCrossProcessParent; + TargetConfig mTargetConfig; + APZTestData mApzTestData; + LayerTransactionParent* mLayerTree; + nsTArray<PluginWindowData> mPluginData; + bool mUpdatedPluginDataAvailable; + + // Number of times the compositor has been reset without having been + // acknowledged by the child. + uint32_t mPendingCompositorUpdates; + + CompositorController* GetCompositorController() const; + MetricsSharingController* CrossProcessSharingController() const; + MetricsSharingController* InProcessSharingController() const; + }; + + /** + * 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(uint64_t aId); + + /** + * 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( + uint64_t aContentLayersId); + /** + * Same as the GetApzcTreeManagerParentForRoot function, but returns + * the GeckoContentController for the parent process. + */ + static GeckoContentController* GetGeckoContentControllerForRoot( + uint64_t 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(uint64_t 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. + */ + virtual bool RecvRemotePluginsReady() override; + + /** + * Used by the profiler to denote when a vsync occured + */ + static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp); + + widget::CompositorWidget* GetWidget() { return mWidget; } + + void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr); + + PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) override; + bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override; + + PAPZParent* AllocPAPZParent(const uint64_t& aLayersId) override; + bool DeallocPAPZParent(PAPZParent* aActor) override; + + bool RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ) override; + + RefPtr<APZCTreeManager> GetAPZCTreeManager(); + + bool AsyncPanZoomEnabled() const { + return !!mApzcTreeManager; + } + +private: + + void Initialize(); + + /** + * Called during destruction in order to release resources as early as possible. + */ + void StopAndClearResources(); + + /** + * This returns a reference to the APZCTreeManager to which + * pan/zoom-related events can be sent. + */ + static already_AddRefed<APZCTreeManager> GetAPZCTreeManager(uint64_t aLayersId); + + /** + * Release compositor-thread resources referred to by |aID|. + * + * Must run on the content main thread. + */ + static void DeallocateLayerTreeId(uint64_t aId); + +protected: + // Protected destructor, to discourage deletion outside of Release(): + virtual ~CompositorBridgeParent(); + + void DeferredDestroy(); + + virtual PLayerTransactionParent* + AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints, + const uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool* aSuccess) override; + virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override; + virtual void ScheduleTask(already_AddRefed<CancelableRunnable>, int); + void CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr); + + void SetEGLSurfaceSize(int width, int height); + + void InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints); + void PauseComposition(); + void ResumeComposition(); + void ResumeCompositionAndResize(int width, int height); + void ForceComposition(); + void CancelCurrentCompositeTask(); + void Invalidate(); + bool IsPendingComposite(); + void FinishPendingComposite(); + + RefPtr<Compositor> NewCompositor(const nsTArray<LayersBackend>& aBackendHints); + void ResetCompositorTask(const nsTArray<LayersBackend>& aBackendHints, + Maybe<TextureFactoryIdentifier>* aOutNewIdentifier); + Maybe<TextureFactoryIdentifier> ResetCompositorImpl(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(); + + /** + * Destroys the compositor thread and global compositor map. + */ + static void Shutdown(); + + /** + * Finish the shutdown operation on the compositor thread. + */ + static void FinishShutdown(); + + /** + * Return true if current state allows compositing, that is + * finishing a layers transaction. + */ + bool CanComposite(); + + void DidComposite(TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd); + + // The indirect layer tree lock must be held before calling this function. + // Callback should take (LayerTreeState* aState, const uint64_t& aLayersId) + template <typename Lambda> + inline void ForEachIndirectLayerTree(const Lambda& aCallback); + + RefPtr<LayerManagerComposite> mLayerManager; + RefPtr<Compositor> mCompositor; + RefPtr<AsyncCompositionManager> mCompositionManager; + widget::CompositorWidget* mWidget; + TimeStamp mTestTime; + CSSToLayoutDeviceScale mScale; + TimeDuration mVsyncRate; + bool mIsTesting; + + uint64_t mPendingTransaction; + + bool mPaused; + + bool mUseExternalSurfaceSize; + gfx::IntSize mEGLSurfaceSize; + + mozilla::Monitor mPauseCompositionMonitor; + mozilla::Monitor mResumeCompositionMonitor; + mozilla::Monitor mResetCompositorMonitor; + + uint64_t mCompositorID; + uint64_t mRootLayerTreeID; + + bool mOverrideComposeReadiness; + RefPtr<CancelableRunnable> mForceCompositionTask; + + RefPtr<APZCTreeManager> mApzcTreeManager; + + RefPtr<CompositorThreadHolder> mCompositorThreadHolder; + 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; + + TimeDuration mPaintTime; + +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + // cached plugin data used to reduce the number of updates we request. + uint64_t 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); +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CompositorBridgeParent_h diff --git a/gfx/layers/ipc/CompositorThread.cpp b/gfx/layers/ipc/CompositorThread.cpp new file mode 100644 index 000000000..789a5d5d3 --- /dev/null +++ b/gfx/layers/ipc/CompositorThread.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 sts=2 ts=8 et tw=99 : */ +/* 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 "MainThreadUtils.h" +#include "nsThreadUtils.h" +#include "CompositorBridgeParent.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/media/MediaSystemResourceService.h" + +namespace mozilla { + +namespace gfx { +// See VRManagerChild.cpp +void ReleaseVRManagerParentSingleton(); +} // namespace gfx + +namespace layers { + +static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder; +static bool sFinishedCompositorShutDown = false; + +// See ImageBridgeChild.cpp +void ReleaseImageBridgeParentSingleton(); + +CompositorThreadHolder* GetCompositorThreadHolder() +{ + return sCompositorThreadHolder; +} + +base::Thread* +CompositorThread() +{ + return sCompositorThreadHolder + ? sCompositorThreadHolder->GetCompositorThread() + : nullptr; +} + +/* static */ MessageLoop* +CompositorThreadHolder::Loop() +{ + return CompositorThread() ? CompositorThread()->message_loop() : nullptr; +} + +CompositorThreadHolder* +CompositorThreadHolder::GetSingleton() +{ + return sCompositorThreadHolder; +} + +CompositorThreadHolder::CompositorThreadHolder() + : mCompositorThread(CreateCompositorThread()) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_COUNT_CTOR(CompositorThreadHolder); +} + +CompositorThreadHolder::~CompositorThreadHolder() +{ + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_COUNT_DTOR(CompositorThreadHolder); + + DestroyCompositorThread(mCompositorThread); +} + +/* static */ void +CompositorThreadHolder::DestroyCompositorThread(base::Thread* aCompositorThread) +{ + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet."); + + CompositorBridgeParent::Shutdown(); + delete aCompositorThread; + sFinishedCompositorShutDown = true; +} + +/* static */ base::Thread* +CompositorThreadHolder::CreateCompositorThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!"); + + base::Thread* compositorThread = new base::Thread("Compositor"); + + base::Thread::Options options; + /* 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). */ + options.transient_hang_timeout = 128; // milliseconds + /* 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. */ + options.permanent_hang_timeout = 2048; // milliseconds +#if defined(_WIN32) + /* With d3d9 the compositor thread creates native ui, see DeviceManagerD3D9. As + * such the thread is a gui thread, and must process a windows message queue or + * risk deadlocks. Chromium message loop TYPE_UI does exactly what we need. */ + options.message_loop_type = MessageLoop::TYPE_UI; +#endif + + if (!compositorThread->StartWithOptions(options)) { + delete compositorThread; + return nullptr; + } + + CompositorBridgeParent::Setup(); + ImageBridgeParent::Setup(); + + return compositorThread; +} + +void +CompositorThreadHolder::Start() +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); + MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!"); + + sCompositorThreadHolder = new CompositorThreadHolder(); +} + +void +CompositorThreadHolder::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!"); + MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!"); + + ReleaseImageBridgeParentSingleton(); + gfx::ReleaseVRManagerParentSingleton(); + MediaSystemResourceService::Shutdown(); + + sCompositorThreadHolder = nullptr; + + // No locking is needed around sFinishedCompositorShutDown because it is only + // ever accessed on the main thread. + while (!sFinishedCompositorShutDown) { + NS_ProcessNextEvent(nullptr, true); + } + + CompositorBridgeParent::FinishShutdown(); +} + +/* static */ bool +CompositorThreadHolder::IsInCompositorThread() +{ + return CompositorThread() && + CompositorThread()->thread_id() == PlatformThread::CurrentId(); +} + +} // namespace mozilla +} // namespace layers diff --git a/gfx/layers/ipc/CompositorThread.h b/gfx/layers/ipc/CompositorThread.h new file mode 100644 index 000000000..cc47f5fa2 --- /dev/null +++ b/gfx/layers/ipc/CompositorThread.h @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 sts=2 ts=8 et tw=99 : */ +/* 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 "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS +#include "base/platform_thread.h" // for PlatformThreadId +#include "base/thread.h" // for Thread +#include "base/message_loop.h" +#include "nsISupportsImpl.h" +#include "ThreadSafeRefcountingWithMainThreadDestruction.h" + +namespace mozilla { +namespace layers { + +class CompositorThreadHolder final +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorThreadHolder) + +public: + CompositorThreadHolder(); + + base::Thread* 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(); + + static MessageLoop* Loop(); + + // Returns true if the calling thread is the compositor thread. + static bool IsInCompositorThread(); + +private: + ~CompositorThreadHolder(); + + base::Thread* const mCompositorThread; + + static base::Thread* CreateCompositorThread(); + static void DestroyCompositorThread(base::Thread* aCompositorThread); + + friend class CompositorBridgeParent; +}; + +base::Thread* CompositorThread(); + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CompositorThread_h diff --git a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp new file mode 100644 index 000000000..1bb6d046b --- /dev/null +++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp @@ -0,0 +1,578 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 et 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/CrossProcessCompositorBridgeParent.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 "LayerTransactionParent.h" // for LayerTransactionParent +#include "RenderTrace.h" // for RenderTraceLayers +#include "base/message_loop.h" // for MessageLoop +#include "base/process.h" // for ProcessId +#include "base/task.h" // for CancelableTask, etc +#include "base/thread.h" // for Thread +#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 "gfxPrefs.h" // for gfxPrefs +#include "mozilla/AutoRestore.h" // for AutoRestore +#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown +#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/gfx/2D.h" // for DrawTarget +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/gfx/Rect.h" // for IntSize +#include "VRManager.h" // for VRManager +#include "mozilla/ipc/Transport.h" // for Transport +#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager +#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent +#include "mozilla/layers/APZThreadUtils.h" // for APZCTreeManager +#include "mozilla/layers/AsyncCompositionManager.h" +#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor +#include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/FrameUniformityData.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/PLayerTransactionParent.h" +#include "mozilla/layers/RemoteContentController.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService +#include "mozilla/mozalloc.h" // for operator new, etc +#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 +#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop +#ifdef XP_WIN +#include "mozilla/layers/CompositorD3D11.h" +#include "mozilla/layers/CompositorD3D9.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" +#ifdef MOZ_ENABLE_PROFILER_SPS +#include "ProfilerMarkers.h" +#endif +#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 + +#include "LayerScope.h" + +namespace mozilla { + +namespace layers { + +// defined in CompositorBridgeParent.cpp +typedef map<uint64_t, CompositorBridgeParent::LayerTreeState> LayerTreeMap; +extern LayerTreeMap sIndirectLayerTrees; +extern StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock; + +bool +CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint() +{ + mNotifyAfterRemotePaint = true; + return true; +} + +void +CrossProcessCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy) +{ + // We must keep this object alive untill the code handling message + // reception is finished on this thread. + MessageLoop::current()->PostTask(NewRunnableMethod(this, &CrossProcessCompositorBridgeParent::DeferredDestroy)); +} + +PLayerTransactionParent* +CrossProcessCompositorBridgeParent::AllocPLayerTransactionParent( + const nsTArray<LayersBackend>&, + const uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool *aSuccess) +{ + MOZ_ASSERT(aId != 0); + + // 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->mCrossProcessParent = this; + LayerManagerComposite* lm = state->mLayerManager; + *aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier(); + *aSuccess = true; + LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId); + p->AddIPDLReference(); + sIndirectLayerTrees[aId].mLayerTree = p; + p->SetPendingCompositorUpdates(state->mPendingCompositorUpdates); + return p; + } + + NS_WARNING("Created child without a matching parent?"); + *aSuccess = false; + LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, aId); + p->AddIPDLReference(); + return p; +} + +bool +CrossProcessCompositorBridgeParent::DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) +{ + LayerTransactionParent* slp = static_cast<LayerTransactionParent*>(aLayers); + EraseLayerState(slp->GetId()); + static_cast<LayerTransactionParent*>(aLayers)->ReleaseIPDLReference(); + return true; +} + +bool +CrossProcessCompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ) +{ + // 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 RecvAsyncPanZoomEnabled; dropping message..."); + return false; + } + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId]; + + *aHasAPZ = state.mParent ? state.mParent->AsyncPanZoomEnabled() : false; + return true; +} + +PAPZCTreeManagerParent* +CrossProcessCompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& 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]; + MOZ_ASSERT(state.mParent); + MOZ_ASSERT(!state.mApzcTreeManagerParent); + state.mApzcTreeManagerParent = new APZCTreeManagerParent(aLayersId, state.mParent->GetAPZCTreeManager()); + + return state.mApzcTreeManagerParent; +} +bool +CrossProcessCompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) +{ + APZCTreeManagerParent* parent = static_cast<APZCTreeManagerParent*>(aActor); + + MonitorAutoLock lock(*sIndirectLayerTreesLock); + auto iter = sIndirectLayerTrees.find(parent->LayersId()); + if (iter != sIndirectLayerTrees.end()) { + CompositorBridgeParent::LayerTreeState& state = iter->second; + MOZ_ASSERT(state.mApzcTreeManagerParent == parent); + state.mApzcTreeManagerParent = nullptr; + } + + delete parent; + + return true; +} + +PAPZParent* +CrossProcessCompositorBridgeParent::AllocPAPZParent(const uint64_t& 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 +CrossProcessCompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor) +{ + RemoteContentController* controller = static_cast<RemoteContentController*>(aActor); + controller->Release(); + return true; +} + +bool +CrossProcessCompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin(); + it != sIndirectLayerTrees.end(); it++) { + CompositorBridgeParent::LayerTreeState* lts = &it->second; + if (lts->mParent && lts->mCrossProcessParent == this) { + lts->mParent->NotifyChildCreated(child); + return true; + } + } + return false; +} + +void +CrossProcessCompositorBridgeParent::ShadowLayersUpdated( + LayerTransactionParent* aLayerTree, + const uint64_t& aTransactionId, + const TargetConfig& aTargetConfig, + const InfallibleTArray<PluginWindowData>& aPlugins, + bool aIsFirstPaint, + bool aScheduleComposite, + uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + int32_t /*aPaintSyncId: unused*/, + bool aHitTestUpdate) +{ + uint64_t id = aLayerTree->GetId(); + + MOZ_ASSERT(id != 0); + + CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state) { + return; + } + MOZ_ASSERT(state->mParent); + state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint); + + Layer* shadowRoot = aLayerTree->GetRoot(); + if (shadowRoot) { + CompositorBridgeParent::SetShadowProperties(shadowRoot); + } + UpdateIndirectTree(id, shadowRoot, aTargetConfig); + + // Cache the plugin data for this remote layer tree + state->mPluginData = aPlugins; + state->mUpdatedPluginDataAvailable = true; + + state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite, + aPaintSequenceNumber, aIsRepeatTransaction, 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->SendObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), true); + } + + aLayerTree->SetPendingTransactionId(aTransactionId); +} + +void +CrossProcessCompositorBridgeParent::DidComposite( + uint64_t aId, + TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd) +{ + sIndirectLayerTreesLock->AssertCurrentThreadOwns(); + if (LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree) { + Unused << SendDidComposite(aId, layerTree->GetPendingTransactionId(), aCompositeStart, aCompositeEnd); + layerTree->SetPendingTransactionId(0); + } +} + +void +CrossProcessCompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + CompositorBridgeParent* parent; + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + parent = sIndirectLayerTrees[id].mParent; + } + if (parent) { + parent->ForceComposite(aLayerTree); + } +} + +void +CrossProcessCompositorBridgeParent::NotifyClearCachedResources(LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + + 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->SendObserveLayerUpdate(id, aLayerTree->GetChildEpoch(), false); + } +} + +bool +CrossProcessCompositorBridgeParent::SetTestSampleTime( + LayerTransactionParent* aLayerTree, const TimeStamp& aTime) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state) { + return false; + } + + MOZ_ASSERT(state->mParent); + return state->mParent->SetTestSampleTime(aLayerTree, aTime); +} + +void +CrossProcessCompositorBridgeParent::LeaveTestMode(LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state) { + return; + } + + MOZ_ASSERT(state->mParent); + state->mParent->LeaveTestMode(aLayerTree); +} + +void +CrossProcessCompositorBridgeParent::ApplyAsyncProperties( + LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state) { + return; + } + + MOZ_ASSERT(state->mParent); + state->mParent->ApplyAsyncProperties(aLayerTree); +} + +void +CrossProcessCompositorBridgeParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state) { + return; + } + + MOZ_ASSERT(state->mParent); + state->mParent->FlushApzRepaints(aLayerTree); +} + +void +CrossProcessCompositorBridgeParent::GetAPZTestData( + const LayerTransactionParent* aLayerTree, + APZTestData* aOutData) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + MonitorAutoLock lock(*sIndirectLayerTreesLock); + *aOutData = sIndirectLayerTrees[id].mApzTestData; +} + +void +CrossProcessCompositorBridgeParent::SetConfirmedTargetAPZC( + const LayerTransactionParent* aLayerTree, + const uint64_t& aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state || !state->mParent) { + return; + } + + state->mParent->SetConfirmedTargetAPZC(aLayerTree, aInputBlockId, aTargets); +} + +AsyncCompositionManager* +CrossProcessCompositorBridgeParent::GetCompositionManager(LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + const CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state) { + return nullptr; + } + + MOZ_ASSERT(state->mParent); + return state->mParent->GetCompositionManager(aLayerTree); +} + +bool +CrossProcessCompositorBridgeParent::RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) +{ + MonitorAutoLock lock(*sIndirectLayerTreesLock); + CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId]; + + if (LayerTransactionParent* ltp = state.mLayerTree) { + ltp->AcknowledgeCompositorUpdate(); + } + MOZ_ASSERT(state.mPendingCompositorUpdates > 0); + state.mPendingCompositorUpdates--; + return true; +} + +void +CrossProcessCompositorBridgeParent::DeferredDestroy() +{ + mCompositorThreadHolder = nullptr; + mSelfRef = nullptr; +} + +CrossProcessCompositorBridgeParent::~CrossProcessCompositorBridgeParent() +{ + MOZ_ASSERT(XRE_GetIOMessageLoop()); + MOZ_ASSERT(IToplevelProtocol::GetTransport()); +} + +PTextureParent* +CrossProcessCompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId, + const uint64_t& aSerial) +{ + CompositorBridgeParent::LayerTreeState* state = nullptr; + + LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId); + if (sIndirectLayerTrees.end() != itr) { + state = &itr->second; + } + + TextureFlags flags = aFlags; + + if (!state || state->mPendingCompositorUpdates) { + // 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 (state->mLayerManager && state->mLayerManager->GetCompositor() && + aLayersBackend != state->mLayerManager->GetCompositor()->GetBackendType()) { + gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) << "Texture backend is wrong"; + } + + return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial); +} + +bool +CrossProcessCompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) +{ + return TextureHost::DestroyIPDLActor(actor); +} + +bool +CrossProcessCompositorBridgeParent::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + +bool +CrossProcessCompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId, + const uint32_t& aPresShellId) +{ + CompositorBridgeParent* parent; + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + parent = sIndirectLayerTrees[aLayersId].mParent; + } + if (parent) { + parent->ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId)); + } + return true; +} + +bool +CrossProcessCompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) +{ + CompositorBridgeParent* parent; + { // scope lock + MonitorAutoLock lock(*sIndirectLayerTreesLock); + parent = sIndirectLayerTrees[aGuid.mLayersId].mParent; + } + if (parent) { + return parent->RecvNotifyApproximatelyVisibleRegion(aGuid, aRegion); + } + return true; +} + +void +CrossProcessCompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + + CompositorBridgeParent::LayerTreeState* state = + CompositorBridgeParent::GetIndirectShadowTree(id); + if (!state || !state->mParent) { + return; + } + + state->mParent->UpdatePaintTime(aLayerTree, aPaintTime); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h new file mode 100644 index 000000000..399969950 --- /dev/null +++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 et 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_CrossProcessCompositorBridgeParent_h +#define mozilla_layers_CrossProcessCompositorBridgeParent_h + +#include "mozilla/layers/CompositorBridgeParent.h" + +namespace mozilla { +namespace layers { + +/** + * 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 CrossProcessCompositorBridgeParent final : public CompositorBridgeParentBase +{ + friend class CompositorBridgeParent; + +public: + explicit CrossProcessCompositorBridgeParent() + : mNotifyAfterRemotePaint(false) + , mDestroyCalled(false) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + void Bind(Endpoint<PCompositorBridgeParent>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + return; + } + mSelfRef = this; + } + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + // FIXME/bug 774388: work out what shutdown protocol we need. + virtual bool RecvInitialize(const uint64_t& aRootLayerTreeId) override { return false; } + virtual bool RecvReset(nsTArray<LayersBackend>&& aBackendHints, bool* aResult, TextureFactoryIdentifier* aOutIdentifier) override { return false; } + virtual bool RecvRequestOverfill() override { return true; } + virtual bool RecvWillClose() override { return true; } + virtual bool RecvPause() override { return true; } + virtual bool RecvResume() override { return true; } + virtual bool RecvNotifyChildCreated(const uint64_t& child) override; + virtual bool RecvNotifyChildRecreated(const uint64_t& child) override { return false; } + virtual bool RecvAdoptChild(const uint64_t& child) override { return false; } + virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, + const gfx::IntRect& aRect) override + { return true; } + virtual bool RecvFlushRendering() override { return true; } + virtual bool RecvForcePresent() override { return true; } + virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override { return true; } + virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { return true; } + virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override { return true; } + + virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId, + const uint32_t& aPresShellId) override; + + virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid, + const CSSIntRegion& aRegion) override; + + virtual bool RecvAllPluginsCaptured() override { return true; } + + virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override + { + // Don't support calculating frame uniformity on the child process and + // this is just a stub for now. + MOZ_ASSERT(false); + return true; + } + + /** + * Tells this CompositorBridgeParent to send a message when the compositor has received the transaction. + */ + virtual bool RecvRequestNotifyAfterRemotePaint() override; + + virtual PLayerTransactionParent* + AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints, + const uint64_t& aId, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + bool *aSuccess) override; + + virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override; + + virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, + const uint64_t& aTransactionId, + const TargetConfig& aTargetConfig, + const InfallibleTArray<PluginWindowData>& aPlugins, + bool aIsFirstPaint, + bool aScheduleComposite, + uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + int32_t /*aPaintSyncId: unused*/, + bool aHitTestUpdate) override; + virtual void ForceComposite(LayerTransactionParent* aLayerTree) override; + virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) override; + virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree, + const TimeStamp& aTime) override; + virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) override; + virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) + override; + virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) override; + virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree, + APZTestData* aOutData) override; + virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree, + const uint64_t& aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) override; + + virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) override; + virtual bool RecvRemotePluginsReady() override { return false; } + virtual bool RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) override; + + void DidComposite(uint64_t aId, + TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd); + + virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aId, + const uint64_t& aSerial) override; + + virtual bool DeallocPTextureParent(PTextureParent* actor) override; + + virtual bool IsSameProcess() const override; + + PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override { + // Not allowed. + return nullptr; + } + bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override { + // Not allowed. + return false; + } + + virtual bool RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ) override; + + virtual PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) override; + virtual bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override; + + virtual PAPZParent* AllocPAPZParent(const uint64_t& aLayersId) override; + virtual bool DeallocPAPZParent(PAPZParent* aActor) override; + + virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) override; + +protected: + void OnChannelConnected(int32_t pid) override { + mCompositorThreadHolder = CompositorThreadHolder::GetSingleton(); + } +private: + // Private destructor, to discourage deletion outside of Release(): + virtual ~CrossProcessCompositorBridgeParent(); + + 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<CrossProcessCompositorBridgeParent> mSelfRef; + + RefPtr<CompositorThreadHolder> mCompositorThreadHolder; + // If true, we should send a RemotePaintIsReady message when the layer transaction + // is received + bool mNotifyAfterRemotePaint; + bool mDestroyCalled; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_CrossProcessCompositorBridgeParent_h diff --git a/gfx/layers/ipc/GonkNativeHandle.cpp b/gfx/layers/ipc/GonkNativeHandle.cpp new file mode 100644 index 000000000..8f808e623 --- /dev/null +++ b/gfx/layers/ipc/GonkNativeHandle.cpp @@ -0,0 +1,80 @@ +/* -*- 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 <unistd.h> + +#include "GonkNativeHandle.h" + +using namespace mozilla::layers; + +namespace mozilla { +namespace layers { + +GonkNativeHandle::GonkNativeHandle() + : mNhObj(new NhObj()) +{ +} + +GonkNativeHandle::GonkNativeHandle(NhObj* aNhObj) + : mNhObj(aNhObj) +{ + MOZ_ASSERT(aNhObj); +} + + +void +GonkNativeHandle::TransferToAnother(GonkNativeHandle& aHandle) +{ + aHandle.mNhObj = this->GetAndResetNhObj(); +} + +already_AddRefed<GonkNativeHandle::NhObj> +GonkNativeHandle::GetAndResetNhObj() +{ + RefPtr<NhObj> nhObj = mNhObj; + mNhObj = new NhObj(); + return nhObj.forget(); +} + +already_AddRefed<GonkNativeHandle::NhObj> +GonkNativeHandle::GetDupNhObj() +{ + if (!IsValid()) { + return GonkNativeHandle::CreateDupNhObj(nullptr); + } + return GonkNativeHandle::CreateDupNhObj(mNhObj->mHandle); +} + +/* static */ already_AddRefed<GonkNativeHandle::NhObj> +GonkNativeHandle::CreateDupNhObj(native_handle_t* aHandle) +{ + RefPtr<NhObj> nhObj; + if (aHandle) { + native_handle* nativeHandle = + native_handle_create(aHandle->numFds, aHandle->numInts); + if (!nativeHandle) { + nhObj = new GonkNativeHandle::NhObj(); + return nhObj.forget(); + } + + for (int i = 0; i < aHandle->numFds; ++i) { + nativeHandle->data[i] = dup(aHandle->data[i]); + } + + memcpy(nativeHandle->data + nativeHandle->numFds, + aHandle->data + aHandle->numFds, + sizeof(int) * aHandle->numInts); + + nhObj = new GonkNativeHandle::NhObj(nativeHandle); + } else { + nhObj = new GonkNativeHandle::NhObj(); + } + return nhObj.forget(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/GonkNativeHandle.h b/gfx/layers/ipc/GonkNativeHandle.h new file mode 100644 index 000000000..6afaea540 --- /dev/null +++ b/gfx/layers/ipc/GonkNativeHandle.h @@ -0,0 +1,24 @@ +/* -*- 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/. */ + +#ifndef IPC_GonkNativeHandle_h +#define IPC_GonkNativeHandle_h + +#include "mozilla/RefPtr.h" // for RefPtr +#include "nsISupportsImpl.h" + +namespace mozilla { +namespace layers { + +struct GonkNativeHandle { + bool operator==(const GonkNativeHandle&) const { return false; } +}; + +} // namespace layers +} // namespace mozilla + +#endif // IPC_GonkNativeHandle_h diff --git a/gfx/layers/ipc/GonkNativeHandleUtils.cpp b/gfx/layers/ipc/GonkNativeHandleUtils.cpp new file mode 100644 index 000000000..fb2efecb3 --- /dev/null +++ b/gfx/layers/ipc/GonkNativeHandleUtils.cpp @@ -0,0 +1,93 @@ +/* -*- 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 "GonkNativeHandleUtils.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" + +using namespace mozilla::layers; + +namespace IPC { + +namespace { + +class native_handle_Delete +{ +public: + void operator()(native_handle* aNativeHandle) const + { + native_handle_close(aNativeHandle); // closes file descriptors + native_handle_delete(aNativeHandle); + } +}; + +} // anonymous namespace + +void +ParamTraits<GonkNativeHandle>::Write(Message* aMsg, + const paramType& aParam) +{ + GonkNativeHandle handle = aParam; + MOZ_ASSERT(handle.IsValid()); + + RefPtr<GonkNativeHandle::NhObj> nhObj = handle.GetAndResetNhObj(); + native_handle_t* nativeHandle = nhObj->GetAndResetNativeHandle(); + + size_t nbytes = nativeHandle->numInts * sizeof(int); + aMsg->WriteSize(nbytes); + aMsg->WriteBytes((nativeHandle->data + nativeHandle->numFds), nbytes); + + for (size_t i = 0; i < static_cast<size_t>(nativeHandle->numFds); ++i) { + aMsg->WriteFileDescriptor(base::FileDescriptor(nativeHandle->data[i], true)); + } +} + +bool +ParamTraits<GonkNativeHandle>::Read(const Message* aMsg, + PickleIterator* aIter, paramType* aResult) +{ + size_t nbytes; + if (!aMsg->ReadSize(aIter, &nbytes)) { + return false; + } + + if (nbytes % sizeof(int) != 0) { + return false; + } + + size_t numInts = nbytes / sizeof(int); + size_t numFds = aMsg->num_fds(); + mozilla::UniquePtr<native_handle, native_handle_Delete> nativeHandle( + native_handle_create(numFds, numInts)); + if (!nativeHandle) { + return false; + } + + auto data = + reinterpret_cast<char*>(nativeHandle->data + nativeHandle->numFds); + if (!aMsg->ReadBytesInto(aIter, data, nbytes)) { + return false; + } + + for (size_t i = 0; i < numFds; ++i) { + base::FileDescriptor fd; + if (!aMsg->ReadFileDescriptor(aIter, &fd)) { + return false; + } + nativeHandle->data[i] = fd.fd; + nativeHandle->numFds = i + 1; // set number of valid file descriptors + } + + GonkNativeHandle handle(new GonkNativeHandle::NhObj(nativeHandle.get())); + handle.TransferToAnother(*aResult); + + mozilla::Unused << nativeHandle.release(); + + return true; +} + +} // namespace IPC diff --git a/gfx/layers/ipc/GonkNativeHandleUtils.h b/gfx/layers/ipc/GonkNativeHandleUtils.h new file mode 100644 index 000000000..d91792c95 --- /dev/null +++ b/gfx/layers/ipc/GonkNativeHandleUtils.h @@ -0,0 +1,26 @@ +/* -*- 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/. */ + +#ifndef IPC_GonkNativeHandleUtils_h +#define IPC_GonkNativeHandleUtils_h + +#include "ipc/IPCMessageUtils.h" + +#include "GonkNativeHandle.h" + +namespace IPC { + +template <> +struct ParamTraits<mozilla::layers::GonkNativeHandle> { + typedef mozilla::layers::GonkNativeHandle paramType; + static void Write(Message*, const paramType&) {} + static bool Read(const Message*, PickleIterator*, paramType*) { return false; } +}; + +} // namespace IPC + +#endif // IPC_GonkNativeHandleUtils_h diff --git a/gfx/layers/ipc/ISurfaceAllocator.cpp b/gfx/layers/ipc/ISurfaceAllocator.cpp new file mode 100644 index 000000000..57da4d9cd --- /dev/null +++ b/gfx/layers/ipc/ISurfaceAllocator.cpp @@ -0,0 +1,235 @@ +/* -*- 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 "ISurfaceAllocator.h" + +#include "gfxPrefs.h" +#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent +#include "mozilla/layers/TextureHost.h" // for TextureHost +#include "mozilla/layers/TextureForwarder.h" + +namespace mozilla { +namespace layers { + +NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter) + +mozilla::Atomic<ptrdiff_t> GfxMemoryImageReporter::sAmount(0); + +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 + + InfallibleTArray<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++; + } + } +} + +int32_t +ClientIPCAllocator::GetMaxTextureSize() const +{ + return gfxPrefs::MaxTextureSize(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ISurfaceAllocator.h b/gfx/layers/ipc/ISurfaceAllocator.h new file mode 100644 index 000000000..cd38b7e8b --- /dev/null +++ b/gfx/layers/ipc/ISurfaceAllocator.h @@ -0,0 +1,318 @@ +/* -*- 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/. */ + +#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 +#include "LayersTypes.h" + +namespace mozilla { +namespace ipc { +class Shmem; +class IShmemAllocator; +} // namespace ipc +namespace gfx { +class DataSourceSurface; +} // namespace gfx + +namespace layers { + +class CompositableForwarder; +class TextureForwarder; + +class ShmemAllocator; +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 gralloc, 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() {} + + // down-casting + + virtual ShmemAllocator* 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; } + + // ipc info + + virtual bool IPCOpen() const { return true; } + + virtual bool IsSameProcess() const = 0; + + virtual bool UsesImageBridge() const { return false; } + +protected: + void Finalize() {} + + virtual ~ISurfaceAllocator() {} +}; + +/// Methods that are specific to the client/child side. +class ClientIPCAllocator : public ISurfaceAllocator +{ +public: + ClientIPCAllocator() {} + + virtual ClientIPCAllocator* AsClientAllocator() override { return this; } + + virtual base::ProcessId GetParentPid() const = 0; + + virtual MessageLoop * GetMessageLoop() const = 0; + + virtual int32_t GetMaxTextureSize() const; + + virtual void CancelWaitForRecycle(uint64_t aTextureId) = 0; +}; + +/// Methods that are specific to the host/parent side. +class HostIPCAllocator : public ISurfaceAllocator +{ +public: + HostIPCAllocator() {} + + virtual 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 InfallibleTArray<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 can provide shared memory. +/// +/// The allocated shmems can be deallocated on either process, as long as they +/// belong to the same channel. +class ShmemAllocator +{ +public: + virtual bool AllocShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) = 0; + virtual bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) = 0; + virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) = 0; +}; + +/// 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() {} + +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(); + + virtual bool AllocShmemSection(uint32_t aSize, ShmemSection* aShmemSection) override; + + virtual void DeallocShmemSection(ShmemSection& aShmemSection) override; + + virtual 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 000000000..0466a1031 --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -0,0 +1,1239 @@ +/* -*- 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 "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 "base/message_loop.h" // for MessageLoop +#include "base/platform_thread.h" // for PlatformThread +#include "base/process.h" // for ProcessId +#include "base/task.h" // for NewRunnableFunction, etc +#include "base/thread.h" // for Thread +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock +#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc +#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc +#include "mozilla/ipc/Transport.h" // for Transport +#include "mozilla/gfx/Point.h" // for IntSize +#include "mozilla/layers/AsyncCanvasRenderer.h" +#include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager +#include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild +#include "mozilla/layers/CompositableChild.h" +#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/ImageContainerChild.h" +#include "mozilla/layers/LayersMessages.h" // for CompositableOperation +#include "mozilla/layers/PCompositableChild.h" // for PCompositableChild +#include "mozilla/layers/TextureClient.h" // for TextureClient +#include "mozilla/dom/ContentChild.h" +#include "mozilla/mozalloc.h" // for operator new, etc +#include "mtransport/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 +#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop +#include "mozilla/StaticMutex.h" +#include "mozilla/StaticPtr.h" // for StaticRefPtr +#include "mozilla/layers/TextureClient.h" +#include "SynchronousTask.h" + +namespace mozilla { +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layers { + +using base::Thread; +using base::ProcessId; +using namespace mozilla::ipc; +using namespace mozilla::gfx; +using namespace mozilla::media; + +typedef std::vector<CompositableOperation> OpVector; +typedef nsTArray<OpDestroy> OpDestroyVector; + +namespace { +class ImageBridgeThread : public Thread { +public: + + ImageBridgeThread() : Thread("ImageBridgeChild") { + } + +protected: + + MOZ_IS_CLASS_INIT + void Init() { +#ifdef MOZ_ENABLE_PROFILER_SPS + mPseudoStackHack = mozilla_get_pseudo_stack(); +#endif + } + + void CleanUp() { +#ifdef MOZ_ENABLE_PROFILER_SPS + mPseudoStackHack = nullptr; +#endif + } + +private: + +#ifdef MOZ_ENABLE_PROFILER_SPS + // This is needed to avoid a spurious leak report. There's no other + // use for it. See bug 1239504 and bug 1215265. + MOZ_INIT_OUTSIDE_CTOR PseudoStack* mPseudoStackHack; +#endif +}; +} + +struct CompositableTransaction +{ + CompositableTransaction() + : mSwapRequired(false) + , mFinished(true) + {} + ~CompositableTransaction() + { + End(); + } + bool Finished() const + { + return mFinished; + } + void Begin() + { + MOZ_ASSERT(mFinished); + mFinished = false; + } + void End() + { + mFinished = true; + mSwapRequired = false; + 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); + } + void AddEdit(const CompositableOperation& op) + { + AddNoSwapEdit(op); + MarkSyncTransaction(); + } + void MarkSyncTransaction() + { + mSwapRequired = true; + } + + OpVector mOperations; + OpDestroyVector mDestroyedActors; + bool mSwapRequired; + bool mFinished; +}; + +struct AutoEndTransaction { + 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->GetIPDLActor()); + 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; + } + + ReadLockDescriptor readLock; + t.mTextureClient->SerializeReadLock(readLock); + + textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), + readLock, + t.mTimeStamp, t.mPictureRect, + t.mFrameID, t.mProducerID)); + + // Wait end of usage on host side if TextureFlags::RECYCLE is set or GrallocTextureData case + HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient); + } + mTxn->AddNoSwapEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(), + OpUseTexture(textures))); +} + +void +ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable, + TextureClient* aTextureOnBlack, + TextureClient* aTextureOnWhite) +{ + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(aTextureOnWhite); + MOZ_ASSERT(aTextureOnBlack); + MOZ_ASSERT(aCompositable->IsConnected()); + MOZ_ASSERT(aTextureOnWhite->GetIPDLActor()); + MOZ_ASSERT(aTextureOnBlack->GetIPDLActor()); + MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize()); + + ReadLockDescriptor readLockW; + ReadLockDescriptor readLockB; + aTextureOnBlack->SerializeReadLock(readLockB); + aTextureOnWhite->SerializeReadLock(readLockW); + + HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack); + HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite); + + mTxn->AddNoSwapEdit( + CompositableOperation( + nullptr, + aCompositable->GetIPDLActor(), + OpUseComponentAlphaTextures( + nullptr, aTextureOnBlack->GetIPDLActor(), + nullptr, aTextureOnWhite->GetIPDLActor(), + readLockB, readLockW + ) + ) + ); +} + +void +ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient) +{ + // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE is set on ImageBridge. + if (!aClient || + !(aClient->GetFlags() & TextureFlags::RECYCLE)) { + return; + } + aClient->SetLastFwdTransactionId(GetFwdTransactionId()); + mTexturesWaitingRecycled.Put(aClient->GetSerial(), aClient); +} + +void +ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId) +{ + RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); + if (!client) { + return; + } + if (aFwdTransactionId < client->GetLastFwdTransactionId()) { + // Released on host side, but client already requested newer use texture. + return; + } + mTexturesWaitingRecycled.Remove(aTextureId); +} + +void +ImageBridgeChild::CancelWaitForRecycle(uint64_t aTextureId) +{ + MOZ_ASSERT(InImageBridgeChildThread()); + + RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId); + if (!client) { + return; + } + mTexturesWaitingRecycled.Remove(aTextureId); +} + +// Singleton +static StaticMutex sImageBridgeSingletonLock; +static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton; +static Thread *sImageBridgeChildThread = nullptr; + +// 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 + InfallibleTArray<PCompositableChild*> compositables; + ManagedPCompositableChild(compositables); + for (int i = compositables.Length() - 1; i >= 0; --i) { + auto compositable = CompositableClient::FromIPDLActor(compositables[i]); + if (compositable) { + compositable->Destroy(); + } + } + InfallibleTArray<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 (!mCalledClose) { + Close(); + mCalledClose = true; + } +} + +void +ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy) +{ + mCanSend = false; + mCalledClose = true; +} + +void +ImageBridgeChild::DeallocPImageBridgeChild() +{ + this->Release(); +} + +void +ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask, + RefPtr<ImageClient>* result, + CompositableType aType, + ImageContainer* aImageContainer, + ImageContainerChild* aContainerChild) +{ + AutoCompleteTask complete(aTask); + *result = CreateImageClientNow(aType, aImageContainer, aContainerChild); +} + +// dispatched function +void +ImageBridgeChild::CreateCanvasClientSync(SynchronousTask* aTask, + CanvasClient::CanvasClientType aType, + TextureFlags aFlags, + RefPtr<CanvasClient>* const outResult) +{ + AutoCompleteTask complete(aTask); + *outResult = CreateCanvasClientNow(aType, aFlags); +} + +ImageBridgeChild::ImageBridgeChild() + : mCanSend(false) + , mCalledClose(false) + , mFwdTransactionId(0) +{ + MOZ_ASSERT(NS_IsMainThread()); + + mTxn = new CompositableTransaction(); +} + +ImageBridgeChild::~ImageBridgeChild() +{ + delete mTxn; +} + +void +ImageBridgeChild::MarkShutDown() +{ + mTexturesWaitingRecycled.Clear(); + + mCanSend = false; +} + +void +ImageBridgeChild::Connect(CompositableClient* aCompositable, + ImageContainer* aImageContainer) +{ + MOZ_ASSERT(aCompositable); + MOZ_ASSERT(InImageBridgeChildThread()); + MOZ_ASSERT(CanSend()); + + uint64_t id = 0; + + PImageContainerChild* imageContainerChild = nullptr; + if (aImageContainer) + imageContainerChild = aImageContainer->GetPImageContainerChild(); + + PCompositableChild* child = + SendPCompositableConstructor(aCompositable->GetTextureInfo(), + imageContainerChild, &id); + if (!child) { + return; + } + aCompositable->InitIPDLActor(child, id); +} + +PCompositableChild* +ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, + PImageContainerChild* aChild, uint64_t* aID) +{ + MOZ_ASSERT(CanSend()); + return AsyncCompositableChild::CreateActor(); +} + +bool +ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor) +{ + AsyncCompositableChild::DestroyActor(aActor); + return true; +} + + +Thread* ImageBridgeChild::GetThread() const +{ + return sImageBridgeChildThread; +} + +/* static */ RefPtr<ImageBridgeChild> +ImageBridgeChild::GetSingleton() +{ + StaticMutexAutoLock lock(sImageBridgeSingletonLock); + return sImageBridgeChildSingleton; +} + +void +ImageBridgeChild::ReleaseImageContainer(RefPtr<ImageContainerChild> aChild) +{ + if (!aChild) { + return; + } + + if (!InImageBridgeChildThread()) { + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::ReleaseImageContainer, + aChild); + GetMessageLoop()->PostTask(runnable.forget()); + return; + } + + aChild->SendAsyncDelete(); +} + +void +ImageBridgeChild::ReleaseTextureClientNow(TextureClient* aClient) +{ + MOZ_ASSERT(InImageBridgeChildThread()); + RELEASE_MANUALLY(aClient); +} + +/* static */ void +ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient) +{ + if (!aClient) { + return; + } + + RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton(); + if (!imageBridge) { + // TextureClient::Release should normally happen in the ImageBridgeChild + // thread because it usually generate some IPDL messages. + // However, if we take this branch it means that the ImageBridgeChild + // has already shut down, along with the TextureChild, which means no + // message will be sent and it is safe to run this code from any thread. + MOZ_ASSERT(aClient->GetIPDLActor() == nullptr); + RELEASE_MANUALLY(aClient); + return; + } + + RefPtr<Runnable> runnable = WrapRunnable( + imageBridge, + &ImageBridgeChild::ReleaseTextureClientNow, + aClient); + imageBridge->GetMessageLoop()->PostTask(runnable.forget()); +} + +void +ImageBridgeChild::UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer) +{ + if (!aClient || !aContainer) { + return; + } + + if (!InImageBridgeChildThread()) { + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::UpdateImageClient, + aClient, + aContainer); + GetMessageLoop()->PostTask(runnable.forget()); + return; + } + + if (!CanSend()) { + return; + } + + // If the client has become disconnected before this event was dispatched, + // early return now. + if (!aClient->IsConnected()) { + return; + } + + BeginTransaction(); + aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE); + EndTransaction(); +} + +void +ImageBridgeChild::UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper) +{ + AutoCompleteTask complete(aTask); + + UpdateAsyncCanvasRendererNow(aWrapper); +} + +void +ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper) +{ + aWrapper->GetCanvasClient()->UpdateAsync(aWrapper); + + if (InImageBridgeChildThread()) { + UpdateAsyncCanvasRendererNow(aWrapper); + return; + } + + SynchronousTask task("UpdateAsyncCanvasRenderer Lock"); + + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::UpdateAsyncCanvasRendererSync, + &task, + aWrapper); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); +} + +void +ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper) +{ + MOZ_ASSERT(aWrapper); + + if (!CanSend()) { + return; + } + + BeginTransaction(); + aWrapper->GetCanvasClient()->Updated(); + 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); + GetMessageLoop()->PostTask(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(); + } + + AutoTArray<EditReply, 10> replies; + + if (mTxn->mSwapRequired) { + if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId(), &replies)) { + NS_WARNING("could not send async texture transaction"); + return; + } + } else { + // If we don't require a swap we can call SendUpdateNoSwap which + // assumes that aReplies is empty (DEBUG assertion) + if (!SendUpdateNoSwap(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) { + NS_WARNING("could not send async texture transaction (no swap)"); + return; + } + } + for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) { + NS_RUNTIMEABORT("not reached"); + } +} + +void +ImageBridgeChild::SendImageBridgeThreadId() +{ +} + +bool +ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint) +{ + MOZ_ASSERT(NS_IsMainThread()); + + gfxPlatform::GetPlatform(); + + if (!sImageBridgeChildThread) { + sImageBridgeChildThread = new ImageBridgeThread(); + if (!sImageBridgeChildThread->Start()) { + return false; + } + } + + RefPtr<ImageBridgeChild> child = new ImageBridgeChild(); + + RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>( + child, + &ImageBridgeChild::Bind, + Move(aEndpoint)); + child->GetMessageLoop()->PostTask(runnable.forget()); + + // 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) +{ + 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(Move(aEndpoint)); +} + +void +ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint) +{ + if (!aEndpoint.Bind(this)) { + return; + } + + // This reference is dropped in DeallocPImageBridgeChild. + this->AddRef(); + + mCanSend = true; + SendImageBridgeThreadId(); +} + +void +ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) +{ + MessageLoop *parentMsgLoop = aParent->GetMessageLoop(); + ipc::MessageChannel *parentChannel = aParent->GetIPCChannel(); + Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide); + + // This reference is dropped in DeallocPImageBridgeChild. + this->AddRef(); + + mCanSend = true; + SendImageBridgeThreadId(); +} + +/* static */ void +ImageBridgeChild::ShutDown() +{ + MOZ_ASSERT(NS_IsMainThread()); + + ShutdownSingleton(); + + delete sImageBridgeChildThread; + 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); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); + } + + { + SynchronousTask task("ImageBridge ShutdownStep2 lock"); + + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::ShutdownStep2, + &task); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); + } +} + +void +ImageBridgeChild::InitSameProcess() +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); + + MOZ_ASSERT(!sImageBridgeChildSingleton); + MOZ_ASSERT(!sImageBridgeChildThread); + + sImageBridgeChildThread = new ImageBridgeThread(); + if (!sImageBridgeChildThread->IsRunning()) { + sImageBridgeChildThread->Start(); + } + + RefPtr<ImageBridgeChild> child = new ImageBridgeChild(); + RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess(); + + RefPtr<Runnable> runnable = WrapRunnable( + child, + &ImageBridgeChild::BindSameProcess, + parent); + child->GetMessageLoop()->PostTask(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) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sImageBridgeChildSingleton); + MOZ_ASSERT(!sImageBridgeChildThread); + + sImageBridgeChildThread = new ImageBridgeThread(); + if (!sImageBridgeChildThread->IsRunning()) { + sImageBridgeChildThread->Start(); + } + + RefPtr<ImageBridgeChild> child = new ImageBridgeChild(); + + MessageLoop* loop = child->GetMessageLoop(); + loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>( + child, &ImageBridgeChild::Bind, Move(aEndpoint))); + + // 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->thread_id() == PlatformThread::CurrentId(); +} + +MessageLoop * ImageBridgeChild::GetMessageLoop() const +{ + return sImageBridgeChildThread ? sImageBridgeChildThread->message_loop() : nullptr; +} + +/* static */ void +ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier) +{ + if (RefPtr<ImageBridgeChild> child = GetSingleton()) { + child->IdentifyTextureHost(aIdentifier); + } +} + +RefPtr<ImageClient> +ImageBridgeChild::CreateImageClient(CompositableType aType, + ImageContainer* aImageContainer, + ImageContainerChild* aContainerChild) +{ + if (InImageBridgeChildThread()) { + return CreateImageClientNow(aType, aImageContainer, aContainerChild); + } + + SynchronousTask task("CreateImageClient Lock"); + + RefPtr<ImageClient> result = nullptr; + + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::CreateImageClientSync, + &task, + &result, + aType, + aImageContainer, + aContainerChild); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); + + return result; +} + +RefPtr<ImageClient> +ImageBridgeChild::CreateImageClientNow(CompositableType aType, + ImageContainer* aImageContainer, + ImageContainerChild* aContainerChild) +{ + MOZ_ASSERT(InImageBridgeChildThread()); + if (!CanSend()) { + return nullptr; + } + + if (aImageContainer) { + aContainerChild->RegisterWithIPDL(); + if (!SendPImageContainerConstructor(aContainerChild)) { + 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; +} + +already_AddRefed<CanvasClient> +ImageBridgeChild::CreateCanvasClient(CanvasClient::CanvasClientType aType, + TextureFlags aFlag) +{ + if (InImageBridgeChildThread()) { + return CreateCanvasClientNow(aType, aFlag); + } + + SynchronousTask task("CreateCanvasClient Lock"); + + // RefPtrs on arguments are not needed since this dispatches synchronously. + RefPtr<CanvasClient> result = nullptr; + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::CreateCanvasClientSync, + &task, + aType, + aFlag, + &result); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); + + return result.forget(); +} + +already_AddRefed<CanvasClient> +ImageBridgeChild::CreateCanvasClientNow(CanvasClient::CanvasClientType aType, + TextureFlags aFlag) +{ + RefPtr<CanvasClient> client + = CanvasClient::CreateCanvasClient(aType, this, aFlag); + MOZ_ASSERT(client, "failed to create CanvasClient"); + if (client) { + client->Connect(); + } + return client.forget(); +} + +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); +} + +// NewRunnableFunction accepts a limited number of parameters so we need a +// struct here +struct AllocShmemParams { + size_t mSize; + ipc::SharedMemory::SharedMemoryType mType; + ipc::Shmem* mShmem; + bool mUnsafe; + bool mSuccess; +}; + +void +ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams) +{ + AutoCompleteTask complete(aTask); + + if (!CanSend()) { + return; + } + + bool ok = false; + if (aParams->mUnsafe) { + ok = AllocUnsafeShmem(aParams->mSize, aParams->mType, aParams->mShmem); + } else { + ok = AllocShmem(aParams->mSize, aParams->mType, aParams->mShmem); + } + aParams->mSuccess = ok; +} + +bool +ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize, + SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem, + bool aUnsafe) +{ + SynchronousTask task("AllocatorProxy alloc"); + + AllocShmemParams params = { + aSize, aType, aShmem, aUnsafe, false + }; + + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::ProxyAllocShmemNow, + &task, + ¶ms); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); + + return params.mSuccess; +} + +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); + } + + SynchronousTask task("AllocatorProxy Dealloc"); + bool result = false; + + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::ProxyDeallocShmemNow, + &task, + &aShmem, + &result); + GetMessageLoop()->PostTask(runnable.forget()); + + task.Wait(); + return result; +} + +PTextureChild* +ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&, + const LayersBackend&, + const TextureFlags&, + const uint64_t& aSerial) +{ + 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; +} + +PImageContainerChild* +ImageBridgeChild::AllocPImageContainerChild() +{ + // we always use the "power-user" ctor + NS_RUNTIMEABORT("not reached"); + return nullptr; +} + +bool +ImageBridgeChild::DeallocPImageContainerChild(PImageContainerChild* actor) +{ + static_cast<ImageContainerChild*>(actor)->UnregisterFromIPDL(); + return true; +} + +bool +ImageBridgeChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) +{ + for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) { + const AsyncParentMessageData& message = aMessages[i]; + + switch (message.type()) { + case AsyncParentMessageData::TOpNotifyNotUsed: { + const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed(); + NotifyNotUsed(op.TextureId(), op.fwdTransactionId()); + break; + } + default: + NS_ERROR("unknown AsyncParentMessageData type"); + return false; + } + } + return true; +} + +bool +ImageBridgeChild::RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications) +{ + for (auto& n : aNotifications) { + ImageContainerChild* child = + static_cast<ImageContainerChild*>(n.imageContainerChild()); + if (child) { + child->NotifyComposite(n); + } + } + return true; +} + +PTextureChild* +ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData, + LayersBackend aLayersBackend, + TextureFlags aFlags, + uint64_t aSerial) +{ + MOZ_ASSERT(CanSend()); + return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial); +} + +static bool +IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op, bool synchronously) +{ + if (aTxn->Finished()) { + return false; + } + + aTxn->mDestroyedActors.AppendElement(op); + + if (synchronously) { + aTxn->MarkSyncTransaction(); + } + + return true; +} + +bool +ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture, bool synchronously) +{ + return IBCAddOpDestroy(mTxn, OpDestroy(aTexture), synchronously); +} + +bool +ImageBridgeChild::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) +{ + return IBCAddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously); +} + + +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; + } + + CompositableOperation op( + nullptr, aCompositable->GetIPDLActor(), + OpRemoveTexture(nullptr, aTexture->GetIPDLActor())); + + if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) { + mTxn->AddEdit(op); + } else { + mTxn->AddNoSwapEdit(op); + } +} + +bool ImageBridgeChild::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + +void +ImageBridgeChild::Destroy(CompositableChild* aCompositable) +{ + if (!InImageBridgeChildThread()) { + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<ImageBridgeChild>(this), + &ImageBridgeChild::Destroy, + RefPtr<CompositableChild>(aCompositable)); + GetMessageLoop()->PostTask(runnable.forget()); + return; + } + CompositableForwarder::Destroy(aCompositable); +} + +bool +ImageBridgeChild::CanSend() const +{ + MOZ_ASSERT(InImageBridgeChildThread()); + return mCanSend; +} + +void +ImageBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const +{ + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid()); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ImageBridgeChild.h b/gfx/layers/ipc/ImageBridgeChild.h new file mode 100644 index 000000000..f068149f7 --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeChild.h @@ -0,0 +1,400 @@ +/* -*- 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/. */ + +#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 "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/CanvasClient.h" +#include "mozilla/layers/CompositableForwarder.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/PImageBridgeChild.h" +#include "mozilla/Mutex.h" +#include "nsDebug.h" // for NS_RUNTIMEABORT +#include "nsIObserver.h" +#include "nsRegion.h" // for nsIntRegion +#include "mozilla/gfx/Rect.h" +#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc + +class MessageLoop; + +namespace base { +class Thread; +} // namespace base + +namespace mozilla { +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layers { + +class AsyncCanvasRenderer; +class ImageClient; +class ImageContainer; +class ImageContainerChild; +class ImageBridgeParent; +class CompositableClient; +struct CompositableTransaction; +class Image; +class TextureClient; +class SynchronousTask; +struct AllocShmemParams; + +/** + * 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 InfallibleTArray<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(); + + static void InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint); + static bool InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint); + static bool ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint); + + /** + * 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. + */ + base::Thread * GetThread() const; + + /** + * Returns the ImageBridgeChild's message loop. + * + * Can be called from any thread. + */ + virtual MessageLoop * GetMessageLoop() const override; + + virtual base::ProcessId GetParentPid() const override { return OtherPid(); } + + PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo, + PImageContainerChild* aChild, uint64_t* aID) override; + bool DeallocPCompositableChild(PCompositableChild* aActor) override; + + virtual PTextureChild* + AllocPTextureChild(const SurfaceDescriptor& aSharedData, const LayersBackend& aLayersBackend, const TextureFlags& aFlags, const uint64_t& aSerial) override; + + virtual bool + DeallocPTextureChild(PTextureChild* actor) override; + + PMediaSystemResourceManagerChild* + AllocPMediaSystemResourceManagerChild() override; + bool + DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor) override; + + virtual PImageContainerChild* + AllocPImageContainerChild() override; + virtual bool + DeallocPImageContainerChild(PImageContainerChild* actor) override; + + virtual bool + RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) override; + + virtual bool + RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications) override; + + // Create an ImageClient from any thread. + RefPtr<ImageClient> CreateImageClient( + CompositableType aType, + ImageContainer* aImageContainer, + ImageContainerChild* aContainerChild); + + // Create an ImageClient from the ImageBridge thread. + RefPtr<ImageClient> CreateImageClientNow( + CompositableType aType, + ImageContainer* aImageContainer, + ImageContainerChild* aContainerChild); + + already_AddRefed<CanvasClient> CreateCanvasClient(CanvasClient::CanvasClientType aType, + TextureFlags aFlag); + void ReleaseImageContainer(RefPtr<ImageContainerChild> aChild); + void UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aClient); + void UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer); + static void DispatchReleaseTextureClient(TextureClient* aClient); + + /** + * Flush all Images sent to CompositableHost. + */ + void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer); + + virtual bool IPCOpen() const override { return mCanSend; } + +private: + + /** + * This must be called by the static function DeleteImageBridgeSync defined + * in ImageBridgeChild.cpp ONLY. + */ + ~ImageBridgeChild(); + + // Helpers for dispatching. + already_AddRefed<CanvasClient> CreateCanvasClientNow( + CanvasClient::CanvasClientType aType, + TextureFlags aFlags); + void CreateCanvasClientSync( + SynchronousTask* aTask, + CanvasClient::CanvasClientType aType, + TextureFlags aFlags, + RefPtr<CanvasClient>* const outResult); + + void CreateImageClientSync( + SynchronousTask* aTask, + RefPtr<ImageClient>* result, + CompositableType aType, + ImageContainer* aImageContainer, + ImageContainerChild* aContainerChild); + + void ReleaseTextureClientNow(TextureClient* aClient); + + void UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aClient); + void UpdateAsyncCanvasRendererSync( + SynchronousTask* aTask, + AsyncCanvasRenderer* aWrapper); + + void FlushAllImagesSync( + SynchronousTask* aTask, + ImageClient* aClient, + ImageContainer* aContainer); + + void ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams); + void ProxyDeallocShmemNow(SynchronousTask* aTask, Shmem* aShmem, bool* aResult); + +public: + // CompositableForwarder + + virtual void Connect(CompositableClient* aCompositable, + ImageContainer* aImageContainer) override; + + virtual bool UsesImageBridge() const override { return true; } + + /** + * See CompositableForwarder::UseTextures + */ + virtual void UseTextures(CompositableClient* aCompositable, + const nsTArray<TimedTextureClient>& aTextures) override; + virtual void UseComponentAlphaTextures(CompositableClient* aCompositable, + TextureClient* aClientOnBlack, + TextureClient* aClientOnWhite) override; + + void Destroy(CompositableChild* aCompositable) override; + + /** + * 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); + + virtual void CancelWaitForRecycle(uint64_t aTextureId) override; + + virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override; + virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override; + + virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable, + TextureClient* aTexture) override; + + virtual void UseTiledLayerBuffer(CompositableClient* aCompositable, + const SurfaceDescriptorTiles& aTileLayerDescriptor) override + { + NS_RUNTIMEABORT("should not be called"); + } + + virtual void UpdateTextureRegion(CompositableClient* aCompositable, + const ThebesBufferData& aThebesBufferData, + const nsIntRegion& aUpdatedRegion) override { + NS_RUNTIMEABORT("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. + */ + virtual bool AllocUnsafeShmem(size_t aSize, + mozilla::ipc::SharedMemory::SharedMemoryType aShmType, + mozilla::ipc::Shmem* aShmem) override; + virtual 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. + */ + virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override; + + virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData, + LayersBackend aLayersBackend, + TextureFlags aFlags, + uint64_t aSerial) override; + + virtual bool IsSameProcess() const override; + + virtual void UpdateFwdTransactionId() override { ++mFwdTransactionId; } + virtual uint64_t GetFwdTransactionId() override { return mFwdTransactionId; } + + bool InForwarderThread() override { + return InImageBridgeChildThread(); + } + + virtual void HandleFatalError(const char* aName, const char* aMsg) const override; + +protected: + ImageBridgeChild(); + 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 DeallocPImageBridgeChild() override; + + bool CanSend() const; + + static void ShutdownSingleton(); + +private: + CompositableTransaction* mTxn; + + bool mCanSend; + bool mCalledClose; + + /** + * 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. + */ + nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingRecycled; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp new file mode 100644 index 000000000..7b116f520 --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -0,0 +1,449 @@ +/* vim: set ts=2 sw=2 et tw=80: */ +/* -*- 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 "ImageBridgeParent.h" +#include <stdint.h> // for uint64_t, uint32_t +#include "CompositableHost.h" // for CompositableParent, Create +#include "base/message_loop.h" // for MessageLoop +#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/MessageChannel.h" // for MessageChannel, etc +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/Transport.h" // for Transport +#include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent +#include "mozilla/layers/CompositableTransactionParent.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayersMessages.h" // for EditReply +#include "mozilla/layers/LayersSurfaces.h" // for PGrallocBufferParent +#include "mozilla/layers/PCompositableParent.h" +#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_RUNTIMEABORT, etc +#include "nsISupportsImpl.h" // for ImageBridgeParent::Release, etc +#include "nsTArray.h" // for nsTArray, nsTArray_Impl +#include "nsTArrayForwardDeclare.h" // for InfallibleTArray +#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop +#include "mozilla/layers/TextureHost.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::ipc; +using namespace mozilla::gfx; +using namespace mozilla::media; + +std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeParent::sImageBridges; + +StaticAutoPtr<mozilla::Monitor> sImageBridgesLock; + +// defined in CompositorBridgeParent.cpp +CompositorThreadHolder* GetCompositorThreadHolder(); + +/* static */ void +ImageBridgeParent::Setup() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!sImageBridgesLock) { + sImageBridgesLock = new Monitor("ImageBridges"); + mozilla::ClearOnShutdown(&sImageBridgesLock); + } +} + +ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop, + ProcessId aChildProcessId) + : mMessageLoop(aLoop) + , mSetChildThreadPriority(false) + , mClosed(false) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // creates the map only if it has not been created already, so it is safe + // with several bridges + CompositableMap::Create(); + { + MonitorAutoLock lock(*sImageBridgesLock); + sImageBridges[aChildProcessId] = this; + } + SetOtherProcessId(aChildProcessId); +} + +ImageBridgeParent::~ImageBridgeParent() +{ + nsTArray<PImageContainerParent*> parents; + ManagedPImageContainerParent(parents); + for (PImageContainerParent* p : parents) { + delete p; + } +} + +static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton; + +void ReleaseImageBridgeParentSingleton() { + sImageBridgeParentSingleton = nullptr; +} + +/* static */ ImageBridgeParent* +ImageBridgeParent::CreateSameProcess() +{ + RefPtr<ImageBridgeParent> parent = + new ImageBridgeParent(CompositorThreadHolder::Loop(), base::GetCurrentProcId()); + parent->mSelfRef = parent; + + sImageBridgeParentSingleton = parent; + return parent; +} + +/* static */ bool +ImageBridgeParent::CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU); + + MessageLoop* loop = CompositorThreadHolder::Loop(); + RefPtr<ImageBridgeParent> parent = new ImageBridgeParent(loop, aEndpoint.OtherPid()); + + loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>( + parent, &ImageBridgeParent::Bind, Move(aEndpoint))); + + sImageBridgeParentSingleton = parent; + return true; +} + +void +ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy) +{ + // Can't alloc/dealloc shmems from now on. + mClosed = true; + { + MonitorAutoLock lock(*sImageBridgesLock); + sImageBridges.erase(OtherPid()); + } + MessageLoop::current()->PostTask(NewRunnableMethod(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. +} + +bool +ImageBridgeParent::RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) +{ + MOZ_ASSERT(!mSetChildThreadPriority); + if (mSetChildThreadPriority) { + return false; + } + mSetChildThreadPriority = true; + return true; +} + +class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender +{ +public: + explicit AutoImageBridgeParentAsyncMessageSender(ImageBridgeParent* aImageBridge, + InfallibleTArray<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; + InfallibleTArray<OpDestroy>* mToDestroy; +}; + +bool +ImageBridgeParent::RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy, + const uint64_t& aFwdTransactionId, + EditReplyArray* aReply) +{ + // 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); + + EditReplyVector replyv; + for (EditArray::index_type i = 0; i < aEdits.Length(); ++i) { + if (!ReceiveCompositableUpdate(aEdits[i], replyv)) { + return false; + } + } + + aReply->SetCapacity(replyv.size()); + if (replyv.size() > 0) { + aReply->AppendElements(&replyv.front(), replyv.size()); + } + + 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 true; +} + +bool +ImageBridgeParent::RecvUpdateNoSwap(EditArray&& aEdits, OpDestroyArray&& aToDestroy, + const uint64_t& aFwdTransactionId) +{ + InfallibleTArray<EditReply> noReplies; + bool success = RecvUpdate(Move(aEdits), Move(aToDestroy), aFwdTransactionId, &noReplies); + MOZ_ASSERT(noReplies.Length() == 0, "RecvUpdateNoSwap requires a sync Update to carry Edits"); + return success; +} + +/* static */ bool +ImageBridgeParent::CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint) +{ + MessageLoop* loop = CompositorThreadHolder::Loop(); + + RefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aEndpoint.OtherPid()); + loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>( + bridge, &ImageBridgeParent::Bind, Move(aEndpoint))); + + return true; +} + +void +ImageBridgeParent::Bind(Endpoint<PImageBridgeParent>&& aEndpoint) +{ + if (!aEndpoint.Bind(this)) + return; + mSelfRef = this; +} + +bool 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. + InfallibleTArray<PTextureParent*> textures; + ManagedPTextureParent(textures); + for (unsigned int i = 0; i < textures.Length(); ++i) { + RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]); + tex->DeallocateDeviceData(); + } + return true; +} + +static uint64_t GenImageContainerID() { + static uint64_t sNextImageID = 1; + + ++sNextImageID; + return sNextImageID; +} + +PCompositableParent* +ImageBridgeParent::AllocPCompositableParent(const TextureInfo& aInfo, + PImageContainerParent* aImageContainer, + uint64_t* aID) +{ + uint64_t id = GenImageContainerID(); + *aID = id; + return CompositableHost::CreateIPDLActor(this, aInfo, id, aImageContainer); +} + +bool ImageBridgeParent::DeallocPCompositableParent(PCompositableParent* aActor) +{ + return CompositableHost::DestroyIPDLActor(aActor); +} + +PTextureParent* +ImageBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aSerial) +{ + return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial); +} + +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; +} + +PImageContainerParent* +ImageBridgeParent::AllocPImageContainerParent() +{ + return new ImageContainerParent(); +} + +bool +ImageBridgeParent::DeallocPImageContainerParent(PImageContainerParent* actor) +{ + delete actor; + return true; +} + +void +ImageBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) +{ + mozilla::Unused << SendParentAsyncMessages(aMessage); +} + +class ProcessIdComparator +{ +public: + bool Equals(const ImageCompositeNotification& aA, + const ImageCompositeNotification& aB) const + { + return aA.imageContainerParent()->OtherPid() == aB.imageContainerParent()->OtherPid(); + } + bool LessThan(const ImageCompositeNotification& aA, + const ImageCompositeNotification& aB) const + { + return aA.imageContainerParent()->OtherPid() < aB.imageContainerParent()->OtherPid(); + } +}; + +/* static */ bool +ImageBridgeParent::NotifyImageComposites(nsTArray<ImageCompositeNotification>& 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]); + uint32_t end = i + 1; + MOZ_ASSERT(aNotifications[i].imageContainerParent()); + ProcessId pid = aNotifications[i].imageContainerParent()->OtherPid(); + while (end < aNotifications.Length() && + aNotifications[end].imageContainerParent()->OtherPid() == pid) { + notifications.AppendElement(aNotifications[end]); + ++end; + } + GetInstance(pid)->SendPendingAsyncMessages(); + if (!GetInstance(pid)->SendDidComposite(notifications)) { + ok = false; + } + i = end; + } + return ok; +} + +void +ImageBridgeParent::DeferredDestroy() +{ + mCompositorThreadHolder = nullptr; + mSelfRef = nullptr; // "this" ImageBridge may get deleted here. +} + +RefPtr<ImageBridgeParent> +ImageBridgeParent::GetInstance(ProcessId aId) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MonitorAutoLock lock(*sImageBridgesLock); + NS_ASSERTION(sImageBridges.count(aId) == 1, "ImageBridgeParent for the process"); + return sImageBridges[aId]; +} + +void +ImageBridgeParent::OnChannelConnected(int32_t aPid) +{ + mCompositorThreadHolder = GetCompositorThreadHolder(); +} + + +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); +} + +void +ImageBridgeParent::DeallocShmem(ipc::Shmem& aShmem) +{ + if (mClosed) { + return; + } + PImageBridgeParent::DeallocShmem(aShmem); +} + +bool ImageBridgeParent::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + +void +ImageBridgeParent::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) +{ + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); + if (!texture) { + return; + } + + if (!(texture->GetFlags() & TextureFlags::RECYCLE)) { + return; + } + + uint64_t textureId = TextureHost::GetTextureSerial(aTexture); + mPendingAsyncMessage.push_back( + OpNotifyNotUsed(textureId, aTransactionId)); + + if (!IsAboutToSendAsyncMessages()) { + SendPendingAsyncMessages(); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h new file mode 100644 index 000000000..2dc705691 --- /dev/null +++ b/gfx/layers/ipc/ImageBridgeParent.h @@ -0,0 +1,155 @@ +/* -*- 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/. */ + +#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 "ImageContainerParent.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 InfallibleTArray + +class MessageLoop; + +namespace base { +class Thread; +} // namespace base + +namespace mozilla { +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layers { + +/** + * ImageBridgeParent is the manager Protocol of ImageContainerParent. + * It's purpose is mainly to setup the IPDL connection. Most of the + * interesting stuff is in ImageContainerParent. + */ +class ImageBridgeParent final : public PImageBridgeParent, + public CompositableParentManager, + public ShmemAllocator +{ +public: + typedef InfallibleTArray<CompositableOperation> EditArray; + typedef InfallibleTArray<OpDestroy> OpDestroyArray; + typedef InfallibleTArray<EditReply> EditReplyArray; + +protected: + ImageBridgeParent(MessageLoop* aLoop, ProcessId aChildProcessId); + +public: + ~ImageBridgeParent(); + + /** + * Creates the globals of ImageBridgeParent. + */ + static void Setup(); + + static ImageBridgeParent* CreateSameProcess(); + static bool CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint); + static bool CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint); + + virtual ShmemAllocator* AsShmemAllocator() override { return this; } + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + // CompositableParentManager + virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override; + + virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override; + + virtual base::ProcessId GetChildProcessId() override + { + return OtherPid(); + } + + // PImageBridge + virtual bool RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) override; + virtual bool RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy, + const uint64_t& aFwdTransactionId, + EditReplyArray* aReply) override; + virtual bool RecvUpdateNoSwap(EditArray&& aEdits, OpDestroyArray&& aToDestroy, + const uint64_t& aFwdTransactionId) override; + + PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo, + PImageContainerParent* aImageContainer, + uint64_t*) override; + bool DeallocPCompositableParent(PCompositableParent* aActor) override; + + virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aSerial) override; + virtual bool DeallocPTextureParent(PTextureParent* actor) override; + + PMediaSystemResourceManagerParent* AllocPMediaSystemResourceManagerParent() override; + bool DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor) override; + virtual PImageContainerParent* AllocPImageContainerParent() override; + virtual bool DeallocPImageContainerParent(PImageContainerParent* actor) override; + + // Shutdown step 1 + virtual bool RecvWillClose() override; + + MessageLoop* GetMessageLoop() const { return mMessageLoop; } + + // ShmemAllocator + + virtual bool AllocShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) override; + + virtual bool AllocUnsafeShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) override; + + virtual void DeallocShmem(ipc::Shmem& aShmem) override; + + virtual bool IsSameProcess() const override; + + static RefPtr<ImageBridgeParent> GetInstance(ProcessId aId); + + static bool NotifyImageComposites(nsTArray<ImageCompositeNotification>& aNotifications); + + virtual bool UsesImageBridge() const override { return true; } + + virtual bool IPCOpen() const override { return !mClosed; } + +protected: + void OnChannelConnected(int32_t pid) override; + + void Bind(Endpoint<PImageBridgeParent>&& aEndpoint); + +private: + void DeferredDestroy(); + MessageLoop* mMessageLoop; + // This keeps us alive until ActorDestroy(), at which point we do a + // deferred destruction of ourselves. + RefPtr<ImageBridgeParent> mSelfRef; + + bool mSetChildThreadPriority; + bool mClosed; + + /** + * Map of all living ImageBridgeParent instances + */ + static std::map<base::ProcessId, ImageBridgeParent*> sImageBridges; + + RefPtr<CompositorThreadHolder> mCompositorThreadHolder; +}; + +} // namespace layers +} // namespace mozilla + +#endif // gfx_layers_ipc_ImageBridgeParent_h_ diff --git a/gfx/layers/ipc/ImageContainerChild.cpp b/gfx/layers/ipc/ImageContainerChild.cpp new file mode 100644 index 000000000..c54eb2c41 --- /dev/null +++ b/gfx/layers/ipc/ImageContainerChild.cpp @@ -0,0 +1,70 @@ +/* -*- 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 "ImageContainerChild.h" +#include "ImageContainer.h" +#include "mozilla/Assertions.h" +#include "mozilla/layers/ImageBridgeChild.h" + +namespace mozilla { +namespace layers { + +ImageContainerChild::ImageContainerChild(ImageContainer* aImageContainer) + : mLock("ImageContainerChild") + , mImageContainer(aImageContainer) + , mIPCOpen(false) +{ +} + +void +ImageContainerChild::ForgetImageContainer() +{ + MutexAutoLock lock(mLock); + mImageContainer = nullptr; +} + +void +ImageContainerChild::NotifyComposite(const ImageCompositeNotification& aNotification) +{ + MOZ_ASSERT(InImageBridgeChildThread()); + + MutexAutoLock lock(mLock); + if (mImageContainer) { + mImageContainer->NotifyCompositeInternal(aNotification); + } +} + +void +ImageContainerChild::RegisterWithIPDL() +{ + MOZ_ASSERT(!mIPCOpen); + MOZ_ASSERT(InImageBridgeChildThread()); + + AddRef(); + mIPCOpen = true; +} + +void +ImageContainerChild::UnregisterFromIPDL() +{ + MOZ_ASSERT(mIPCOpen); + MOZ_ASSERT(InImageBridgeChildThread()); + + mIPCOpen = false; + Release(); +} + +void +ImageContainerChild::SendAsyncDelete() +{ + MOZ_ASSERT(InImageBridgeChildThread()); + + if (mIPCOpen) { + PImageContainerChild::SendAsyncDelete(); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ImageContainerChild.h b/gfx/layers/ipc/ImageContainerChild.h new file mode 100644 index 000000000..839540411 --- /dev/null +++ b/gfx/layers/ipc/ImageContainerChild.h @@ -0,0 +1,61 @@ +/* -*- 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/. */ + +#ifndef mozilla_gfx_layers_ImageContainerChild_h +#define mozilla_gfx_layers_ImageContainerChild_h + +#include "mozilla/Mutex.h" +#include "mozilla/layers/PImageContainerChild.h" + +namespace mozilla { +namespace layers { + +class ImageContainer; +class ImageCompositeNotification; + +/** + * The child side of PImageContainer. It's best to avoid ImageContainer filling + * this role since IPDL objects should be associated with a single thread and + * ImageContainer definitely isn't. This object belongs to (and is always + * destroyed on) the ImageBridge thread, except when we need to destroy it + * during shutdown. + * An ImageContainer owns one of these; we have a weak reference to our + * ImageContainer. + */ +class ImageContainerChild final : public PImageContainerChild +{ +public: + explicit ImageContainerChild(ImageContainer* aImageContainer); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainerChild) + + void RegisterWithIPDL(); + void UnregisterFromIPDL(); + void SendAsyncDelete(); + + void NotifyComposite(const ImageCompositeNotification& aNotification); + void ForgetImageContainer(); + +private: + ~ImageContainerChild() + {} + +private: + Mutex mLock; + ImageContainer* mImageContainer; + + // If mIPCOpen is false, it means the IPDL code tried to deallocate the actor + // before the ImageContainer released it. When this happens we don't actually + // delete the actor right away because the ImageContainer has a reference to + // it. In this case the actor will be deleted when the ImageContainer lets go + // of it. + // mIPCOpen must not be accessed off the ImageBridgeChild thread. + bool mIPCOpen; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_gfx_layers_ImageContainerChild_h diff --git a/gfx/layers/ipc/ImageContainerParent.cpp b/gfx/layers/ipc/ImageContainerParent.cpp new file mode 100644 index 000000000..0dc0d6d32 --- /dev/null +++ b/gfx/layers/ipc/ImageContainerParent.cpp @@ -0,0 +1,31 @@ +/* -*- 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 "ImageContainerParent.h" + +#include "nsThreadUtils.h" +#include "mozilla/layers/ImageHost.h" +#include "mozilla/Unused.h" + +namespace mozilla { +namespace layers { + +ImageContainerParent::~ImageContainerParent() +{ + while (!mImageHosts.IsEmpty()) { + mImageHosts[mImageHosts.Length() - 1]->SetImageContainer(nullptr); + } +} + +bool ImageContainerParent::RecvAsyncDelete() +{ + Unused << PImageContainerParent::Send__delete__(this); + return true; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ImageContainerParent.h b/gfx/layers/ipc/ImageContainerParent.h new file mode 100644 index 000000000..849bcb44f --- /dev/null +++ b/gfx/layers/ipc/ImageContainerParent.h @@ -0,0 +1,37 @@ +/* -*- 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/. */ + +#ifndef mozilla_layers_ImageContainerParent_h +#define mozilla_layers_ImageContainerParent_h + +#include "mozilla/Attributes.h" // for override +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/layers/PImageContainerParent.h" + +namespace mozilla { +namespace layers { + +class ImageHost; + +class ImageContainerParent : public PImageContainerParent +{ +public: + ImageContainerParent() {} + ~ImageContainerParent(); + + virtual bool RecvAsyncDelete() override; + + AutoTArray<ImageHost*,1> mImageHosts; + +private: + virtual void ActorDestroy(ActorDestroyReason why) override {} +}; + +} // namespace layers +} // namespace mozilla + +#endif // ifndef mozilla_layers_ImageContainerParent_h diff --git a/gfx/layers/ipc/KnowsCompositor.h b/gfx/layers/ipc/KnowsCompositor.h new file mode 100755 index 000000000..c4cb8092d --- /dev/null +++ b/gfx/layers/ipc/KnowsCompositor.h @@ -0,0 +1,90 @@ +/* -*- 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" + +namespace mozilla { +namespace layers { + +class SyncObject; +class TextureForwarder; +class LayersIPCActor; + +/** + * 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_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0; + NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0; + + KnowsCompositor(); + ~KnowsCompositor(); + + void IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier); + + SyncObject* GetSyncObject() { return mSyncObject; } + + int32_t GetMaxTextureSize() const + { + return 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 + { + return mTextureFactoryIdentifier.mParentBackend; + } + + bool SupportsTextureBlitting() const + { + return mTextureFactoryIdentifier.mSupportsTextureBlitting; + } + + bool SupportsPartialUploads() const + { + return mTextureFactoryIdentifier.mSupportsPartialUploads; + } + + bool SupportsComponentAlpha() const + { + return mTextureFactoryIdentifier.mSupportsComponentAlpha; + } + + const TextureFactoryIdentifier& GetTextureFactoryIdentifier() const + { + return mTextureFactoryIdentifier; + } + + int32_t GetSerial() { return mSerial; } + + /** + * Helpers for finding other related interface. These are infallible. + */ + virtual TextureForwarder* GetTextureForwarder() = 0; + virtual LayersIPCActor* GetLayersIPCActor() = 0; + +protected: + TextureFactoryIdentifier mTextureFactoryIdentifier; + RefPtr<SyncObject> mSyncObject; + + const int32_t mSerial; + static mozilla::Atomic<int32_t> sSerialCounter; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/LayerAnimationUtils.cpp b/gfx/layers/ipc/LayerAnimationUtils.cpp new file mode 100644 index 000000000..60c2791ac --- /dev/null +++ b/gfx/layers/ipc/LayerAnimationUtils.cpp @@ -0,0 +1,45 @@ +/* -*- 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. + +namespace mozilla { +namespace layers { + +/* static */ Maybe<ComputedTimingFunction> +AnimationUtils::TimingFunctionToComputedTimingFunction( + const TimingFunction& aTimingFunction) +{ + switch (aTimingFunction.type()) { + case TimingFunction::Tnull_t: + return Nothing(); + case TimingFunction::TCubicBezierFunction: { + ComputedTimingFunction result; + CubicBezierFunction cbf = aTimingFunction.get_CubicBezierFunction(); + result.Init(nsTimingFunction(cbf.x1(), cbf.y1(), cbf.x2(), cbf.y2())); + return Some(result); + } + case TimingFunction::TStepFunction: { + StepFunction sf = aTimingFunction.get_StepFunction(); + nsTimingFunction::Type type = sf.type() == 1 ? + nsTimingFunction::Type::StepStart : + nsTimingFunction::Type::StepEnd; + ComputedTimingFunction result; + result.Init(nsTimingFunction(type, sf.steps())); + return Some(result); + } + default: + MOZ_ASSERT_UNREACHABLE( + "Function must be null, bezier or step"); + 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 000000000..fc807dbea --- /dev/null +++ b/gfx/layers/ipc/LayerAnimationUtils.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_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 000000000..8b60d3b51 --- /dev/null +++ b/gfx/layers/ipc/LayerTransactionChild.cpp @@ -0,0 +1,87 @@ +/* -*- 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 "LayerTransactionChild.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/layers/CompositableChild.h" +#include "mozilla/layers/PCompositableChild.h" // for PCompositableChild +#include "mozilla/layers/PLayerChild.h" // for PLayerChild +#include "mozilla/layers/PImageContainerChild.h" +#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "nsDebug.h" // for NS_RUNTIMEABORT, 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(); +} + + +PLayerChild* +LayerTransactionChild::AllocPLayerChild() +{ + // we always use the "power-user" ctor + NS_RUNTIMEABORT("not reached"); + return nullptr; +} + +bool +LayerTransactionChild::DeallocPLayerChild(PLayerChild* actor) +{ + delete actor; + return true; +} + +PCompositableChild* +LayerTransactionChild::AllocPCompositableChild(const TextureInfo& aInfo) +{ + MOZ_ASSERT(!mDestroyed); + return CompositableChild::CreateActor(); +} + +bool +LayerTransactionChild::DeallocPCompositableChild(PCompositableChild* actor) +{ + CompositableChild::DestroyActor(actor); + return true; +} + +void +LayerTransactionChild::ActorDestroy(ActorDestroyReason why) +{ + mDestroyed = true; +#ifdef MOZ_B2G + // Due to poor lifetime management of gralloc (and possibly shmems) we will + // crash at some point in the future when we get destroyed due to abnormal + // shutdown. Its better just to crash here. On desktop though, we have a chance + // of recovering. + if (why == AbnormalShutdown) { + NS_RUNTIMEABORT("ActorDestroy by IPC channel failure at LayerTransactionChild"); + } +#endif +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/LayerTransactionChild.h b/gfx/layers/ipc/LayerTransactionChild.h new file mode 100644 index 000000000..3d56399f4 --- /dev/null +++ b/gfx/layers/ipc/LayerTransactionChild.h @@ -0,0 +1,90 @@ +/* -*- 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/. */ + +#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 layout { +class RenderFrameChild; +} // namespace layout + +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; + } + + uint64_t GetId() const { return mId; } + +protected: + explicit LayerTransactionChild(const uint64_t& aId) + : mForwarder(nullptr) + , mIPCOpen(false) + , mDestroyed(false) + , mId(aId) + {} + ~LayerTransactionChild() { } + + virtual PLayerChild* AllocPLayerChild() override; + virtual bool DeallocPLayerChild(PLayerChild* actor) override; + + virtual PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo) override; + virtual bool DeallocPCompositableChild(PCompositableChild* actor) override; + + virtual 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; + friend class layout::RenderFrameChild; + + ShadowLayerForwarder* mForwarder; + bool mIPCOpen; + bool mDestroyed; + uint64_t 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 000000000..c30ccee5b --- /dev/null +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -0,0 +1,1098 @@ +/* -*- 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 "LayerTransactionParent.h" +#include <vector> // for vector +#include "apz/src/AsyncPanZoomController.h" +#include "CompositableHost.h" // for CompositableParent, Get, etc +#include "ImageLayers.h" // for ImageLayer +#include "Layers.h" // for Layer, ContainerLayer, etc +#include "ShadowLayerParent.h" // for ShadowLayerParent +#include "CompositableTransactionParent.h" // for EditReplyVector +#include "CompositorBridgeParent.h" +#include "gfxPrefs.h" +#include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D +#include "mozilla/layers/CanvasLayerComposite.h" +#include "mozilla/layers/ColorLayerComposite.h" +#include "mozilla/layers/Compositor.h" // for Compositor +#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/LayersSurfaces.h" // for PGrallocBufferParent +#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG +#include "mozilla/layers/PCompositableParent.h" +#include "mozilla/layers/PLayerParent.h" // for PLayerParent +#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL +#include "mozilla/layers/PaintedLayerComposite.h" +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "mozilla/Unused.h" +#include "nsCoord.h" // for NSAppUnitsToFloatPixels +#include "nsDebug.h" // for NS_RUNTIMEABORT +#include "nsDeviceContext.h" // for AppUnitsPerCSSPixel +#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" + +typedef std::vector<mozilla::layers::EditReply> EditReplyVector; + +using mozilla::layout::RenderFrameParent; + +namespace mozilla { +namespace layers { + +//-------------------------------------------------- +// Convenience accessors +static ShadowLayerParent* +cast(const PLayerParent* in) +{ + return const_cast<ShadowLayerParent*>( + static_cast<const ShadowLayerParent*>(in)); +} + +template<class OpCreateT> +static ShadowLayerParent* +AsLayerComposite(const OpCreateT& op) +{ + return cast(op.layerParent()); +} + +static ShadowLayerParent* +AsLayerComposite(const OpSetRoot& op) +{ + return cast(op.rootParent()); +} + +static ShadowLayerParent* +ShadowContainer(const OpInsertAfter& op) +{ + return cast(op.containerParent()); +} +static ShadowLayerParent* +ShadowChild(const OpInsertAfter& op) +{ + return cast(op.childLayerParent()); +} +static ShadowLayerParent* +ShadowAfter(const OpInsertAfter& op) +{ + return cast(op.afterParent()); +} + +static ShadowLayerParent* +ShadowContainer(const OpPrependChild& op) +{ + return cast(op.containerParent()); +} +static ShadowLayerParent* +ShadowChild(const OpPrependChild& op) +{ + return cast(op.childLayerParent()); +} + +static ShadowLayerParent* +ShadowContainer(const OpRemoveChild& op) +{ + return cast(op.containerParent()); +} +static ShadowLayerParent* +ShadowChild(const OpRemoveChild& op) +{ + return cast(op.childLayerParent()); +} + +static ShadowLayerParent* +ShadowContainer(const OpRepositionChild& op) +{ + return cast(op.containerParent()); +} +static ShadowLayerParent* +ShadowChild(const OpRepositionChild& op) +{ + return cast(op.childLayerParent()); +} +static ShadowLayerParent* +ShadowAfter(const OpRepositionChild& op) +{ + return cast(op.afterParent()); +} + +static ShadowLayerParent* +ShadowContainer(const OpRaiseToTopChild& op) +{ + return cast(op.containerParent()); +} +static ShadowLayerParent* +ShadowChild(const OpRaiseToTopChild& op) +{ + return cast(op.childLayerParent()); +} + +//-------------------------------------------------- +// LayerTransactionParent +LayerTransactionParent::LayerTransactionParent(LayerManagerComposite* aManager, + CompositorBridgeParentBase* aBridge, + uint64_t aId) + : mLayerManager(aManager) + , mCompositorBridge(aBridge) + , mId(aId) + , mChildEpoch(0) + , mParentEpoch(0) + , mPendingTransaction(0) + , mPendingCompositorUpdates(0) + , mDestroyed(false) + , mIPCOpen(false) +{ +} + +LayerTransactionParent::~LayerTransactionParent() +{ +} + +void +LayerTransactionParent::SetLayerManager(LayerManagerComposite* aLayerManager) +{ + mLayerManager = aLayerManager; + const ManagedContainer<PLayerParent>& layers = ManagedPLayerParent(); + for (auto iter = layers.ConstIter(); !iter.Done(); iter.Next()) { + ShadowLayerParent* slp = + static_cast<ShadowLayerParent*>(iter.Get()->GetKey()); + if (slp->AsLayer() && slp->AsLayer()->AsLayerComposite()) { + slp->AsLayer()->AsLayerComposite()->SetLayerManager(aLayerManager); + } + } +} + +bool +LayerTransactionParent::RecvShutdown() +{ + Destroy(); + return Send__delete__(this); +} + +void +LayerTransactionParent::Destroy() +{ + const ManagedContainer<PLayerParent>& layers = ManagedPLayerParent(); + for (auto iter = layers.ConstIter(); !iter.Done(); iter.Next()) { + ShadowLayerParent* slp = + static_cast<ShadowLayerParent*>(iter.Get()->GetKey()); + slp->Destroy(); + } + mDestroyed = true; +} + +bool +LayerTransactionParent::RecvUpdateNoSwap(InfallibleTArray<Edit>&& cset, + InfallibleTArray<OpDestroy>&& aToDestroy, + const uint64_t& aFwdTransactionId, + const uint64_t& aTransactionId, + const TargetConfig& targetConfig, + PluginsArray&& aPlugins, + const bool& isFirstPaint, + const bool& scheduleComposite, + const uint32_t& paintSequenceNumber, + const bool& isRepeatTransaction, + const mozilla::TimeStamp& aTransactionStart, + const int32_t& aPaintSyncId) +{ + return RecvUpdate(Move(cset), Move(aToDestroy), aFwdTransactionId, + aTransactionId, targetConfig, Move(aPlugins), isFirstPaint, + scheduleComposite, paintSequenceNumber, isRepeatTransaction, + aTransactionStart, aPaintSyncId, nullptr); +} + +class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender +{ +public: + explicit AutoLayerTransactionParentAsyncMessageSender(LayerTransactionParent* aLayerTransaction, + InfallibleTArray<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; + InfallibleTArray<OpDestroy>* mActorsToDestroy; +}; + +bool +LayerTransactionParent::RecvPaintTime(const uint64_t& aTransactionId, + const TimeDuration& aPaintTime) +{ + mCompositorBridge->UpdatePaintTime(this, aPaintTime); + return true; +} + +bool +LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset, + InfallibleTArray<OpDestroy>&& aToDestroy, + const uint64_t& aFwdTransactionId, + const uint64_t& aTransactionId, + const TargetConfig& targetConfig, + PluginsArray&& aPlugins, + const bool& isFirstPaint, + const bool& scheduleComposite, + const uint32_t& paintSequenceNumber, + const bool& isRepeatTransaction, + const mozilla::TimeStamp& aTransactionStart, + const int32_t& aPaintSyncId, + InfallibleTArray<EditReply>* reply) +{ + profiler_tracing("Paint", "LayerTransaction", TRACING_INTERVAL_START); + PROFILER_LABEL("LayerTransactionParent", "RecvUpdate", + js::ProfileEntry::Category::GRAPHICS); + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + TimeStamp updateStart = TimeStamp::Now(); +#endif + + MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length())); + + UpdateFwdTransactionId(aFwdTransactionId); + + if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) { + for (const auto& op : aToDestroy) { + DestroyActor(op); + } + return true; + } + + // This ensures that destroy operations are always processed. It is not safe + // to early-return from RecvUpdate without doing so. + AutoLayerTransactionParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy); + EditReplyVector replyv; + + { + AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this)); + layer_manager()->BeginTransaction(); + } + + // not all edits require an update to the hit testing tree + bool updateHitTestingTree = false; + + for (EditArray::index_type i = 0; i < cset.Length(); ++i) { + const Edit& edit = cset[i]; + + switch (edit.type()) { + // Create* ops + case Edit::TOpCreatePaintedLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreatePaintedLayer")); + + RefPtr<PaintedLayerComposite> layer = + layer_manager()->CreatePaintedLayerComposite(); + AsLayerComposite(edit.get_OpCreatePaintedLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateContainerLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer")); + + RefPtr<ContainerLayer> layer = layer_manager()->CreateContainerLayerComposite(); + AsLayerComposite(edit.get_OpCreateContainerLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateImageLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer")); + + RefPtr<ImageLayerComposite> layer = + layer_manager()->CreateImageLayerComposite(); + AsLayerComposite(edit.get_OpCreateImageLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateColorLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer")); + + RefPtr<ColorLayerComposite> layer = layer_manager()->CreateColorLayerComposite(); + AsLayerComposite(edit.get_OpCreateColorLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateCanvasLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer")); + + RefPtr<CanvasLayerComposite> layer = + layer_manager()->CreateCanvasLayerComposite(); + AsLayerComposite(edit.get_OpCreateCanvasLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + case Edit::TOpCreateRefLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer")); + + RefPtr<RefLayerComposite> layer = + layer_manager()->CreateRefLayerComposite(); + AsLayerComposite(edit.get_OpCreateRefLayer())->Bind(layer); + + updateHitTestingTree = true; + break; + } + + // Attributes + case Edit::TOpSetLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes")); + + const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes(); + ShadowLayerParent* layerParent = AsLayerComposite(osla); + Layer* layer = layerParent->AsLayer(); + if (!layer) { + return false; + } + const LayerAttributes& attrs = osla.attrs(); + + const CommonLayerAttributes& common = attrs.common(); + layer->SetLayerBounds(common.layerBounds()); + layer->SetVisibleRegion(common.visibleRegion()); + layer->SetEventRegions(common.eventRegions()); + layer->SetContentFlags(common.contentFlags()); + layer->SetOpacity(common.opacity()); + layer->SetClipRect(common.useClipRect() ? Some(common.clipRect()) : Nothing()); + layer->SetScrolledClip(common.scrolledClip()); + layer->SetBaseTransform(common.transform().value()); + layer->SetTransformIsPerspective(common.transformIsPerspective()); + layer->SetPostScale(common.postXScale(), common.postYScale()); + layer->SetIsFixedPosition(common.isFixedPosition()); + if (common.isFixedPosition()) { + layer->SetFixedPositionData(common.fixedPositionScrollContainerId(), + common.fixedPositionAnchor(), + common.fixedPositionSides()); + } + if (common.isStickyPosition()) { + layer->SetStickyPositionData(common.stickyScrollContainerId(), + common.stickyScrollRangeOuter(), + common.stickyScrollRangeInner()); + } + layer->SetScrollbarData(common.scrollbarTargetContainerId(), + static_cast<Layer::ScrollDirection>(common.scrollbarDirection()), + common.scrollbarThumbRatio()); + if (common.isScrollbarContainer()) { + layer->SetIsScrollbarContainer(); + } + layer->SetMixBlendMode((gfx::CompositionOp)common.mixBlendMode()); + layer->SetForceIsolatedGroup(common.forceIsolatedGroup()); + if (PLayerParent* maskLayer = common.maskLayerParent()) { + layer->SetMaskLayer(cast(maskLayer)->AsLayer()); + } else { + layer->SetMaskLayer(nullptr); + } + layer->SetAnimations(common.animations()); + 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.ancestorMaskLayersParent().Length(); i++) { + Layer* maskLayer = cast(common.ancestorMaskLayersParent().ElementAt(i))->AsLayer(); + 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")); + + PaintedLayerComposite* paintedLayer = layerParent->AsPaintedLayerComposite(); + if (!paintedLayer) { + return false; + } + const PaintedLayerAttributes& attrs = + specific.get_PaintedLayerAttributes(); + + paintedLayer->SetValidRegion(attrs.validRegion()); + + break; + } + case Specific::TContainerLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] container layer")); + + ContainerLayerComposite* containerLayer = layerParent->AsContainerLayerComposite(); + 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.scaleToResolution(), + attrs.presShellResolution()); + containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride()); + + break; + } + case Specific::TColorLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] color layer")); + + ColorLayerComposite* colorLayer = layerParent->AsColorLayerComposite(); + 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")); + + CanvasLayerComposite* canvasLayer = layerParent->AsCanvasLayerComposite(); + 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")); + + RefLayerComposite* refLayer = layerParent->AsRefLayerComposite(); + if (!refLayer) { + return false; + } + refLayer->SetReferentId(specific.get_RefLayerAttributes().id()); + refLayer->SetEventRegionsOverride(specific.get_RefLayerAttributes().eventRegionsOverride()); + break; + } + case Specific::TImageLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] image layer")); + + ImageLayerComposite* imageLayer = layerParent->AsImageLayerComposite(); + if (!imageLayer) { + return false; + } + const ImageLayerAttributes& attrs = specific.get_ImageLayerAttributes(); + imageLayer->SetSamplingFilter(attrs.samplingFilter()); + imageLayer->SetScaleToSize(attrs.scaleToSize(), attrs.scaleMode()); + break; + } + default: + NS_RUNTIMEABORT("not reached"); + } + + updateHitTestingTree = true; + break; + } + case Edit::TOpSetDiagnosticTypes: { + mLayerManager->GetCompositor()->SetDiagnosticTypes( + edit.get_OpSetDiagnosticTypes().diagnostics()); + break; + } + case Edit::TOpWindowOverlayChanged: { + mLayerManager->SetWindowOverlayChanged(); + break; + } + // Tree ops + case Edit::TOpSetRoot: { + MOZ_LAYERS_LOG(("[ParentSide] SetRoot")); + + Layer* newRoot = AsLayerComposite(edit.get_OpSetRoot())->AsLayer(); + if (!newRoot) { + return false; + } + if (newRoot->GetParent()) { + // newRoot is not a root! + return false; + } + mRoot = newRoot; + + updateHitTestingTree = true; + break; + } + case Edit::TOpInsertAfter: { + MOZ_LAYERS_LOG(("[ParentSide] InsertAfter")); + + const OpInsertAfter& oia = edit.get_OpInsertAfter(); + Layer* child = ShadowChild(oia)->AsLayer(); + if (!child) { + return false; + } + ContainerLayerComposite* container = ShadowContainer(oia)->AsContainerLayerComposite(); + if (!container || + !container->InsertAfter(child, ShadowAfter(oia)->AsLayer())) + { + return false; + } + + updateHitTestingTree = true; + break; + } + case Edit::TOpPrependChild: { + MOZ_LAYERS_LOG(("[ParentSide] PrependChild")); + + const OpPrependChild& oac = edit.get_OpPrependChild(); + Layer* child = ShadowChild(oac)->AsLayer(); + if (!child) { + return false; + } + ContainerLayerComposite* container = ShadowContainer(oac)->AsContainerLayerComposite(); + if (!container || + !container->InsertAfter(child, nullptr)) + { + return false; + } + + updateHitTestingTree = true; + break; + } + case Edit::TOpRemoveChild: { + MOZ_LAYERS_LOG(("[ParentSide] RemoveChild")); + + const OpRemoveChild& orc = edit.get_OpRemoveChild(); + Layer* childLayer = ShadowChild(orc)->AsLayer(); + if (!childLayer) { + return false; + } + ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite(); + if (!container || + !container->RemoveChild(childLayer)) + { + return false; + } + + updateHitTestingTree = true; + break; + } + case Edit::TOpRepositionChild: { + MOZ_LAYERS_LOG(("[ParentSide] RepositionChild")); + + const OpRepositionChild& orc = edit.get_OpRepositionChild(); + Layer* child = ShadowChild(orc)->AsLayer(); + if (!child) { + return false; + } + ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite(); + if (!container || + !container->RepositionChild(child, ShadowAfter(orc)->AsLayer())) + { + return false; + } + + updateHitTestingTree = true; + break; + } + case Edit::TOpRaiseToTopChild: { + MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild")); + + const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild(); + Layer* child = ShadowChild(rtc)->AsLayer(); + if (!child) { + return false; + } + ContainerLayerComposite* container = ShadowContainer(rtc)->AsContainerLayerComposite(); + if (!container || + !container->RepositionChild(child, nullptr)) + { + return false; + } + + updateHitTestingTree = true; + break; + } + case Edit::TCompositableOperation: { + if (!ReceiveCompositableUpdate(edit.get_CompositableOperation(), + replyv)) { + return false; + } + break; + } + case Edit::TOpAttachCompositable: { + const OpAttachCompositable& op = edit.get_OpAttachCompositable(); + CompositableHost* host = CompositableHost::FromIPDLActor(op.compositableParent()); + if (mPendingCompositorUpdates) { + // Do not attach compositables from old layer trees. Return true since + // content cannot handle errors. + return true; + } + if (!Attach(cast(op.layerParent()), host, false)) { + return false; + } + host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID()); + break; + } + case Edit::TOpAttachAsyncCompositable: { + const OpAttachAsyncCompositable& op = edit.get_OpAttachAsyncCompositable(); + PCompositableParent* compositableParent = CompositableMap::Get(op.containerID()); + if (!compositableParent) { + NS_ERROR("CompositableParent not found in the map"); + return false; + } + if (mPendingCompositorUpdates) { + // Do not attach compositables from old layer trees. Return true since + // content cannot handle errors. + return true; + } + CompositableHost* host = CompositableHost::FromIPDLActor(compositableParent); + if (!Attach(cast(op.layerParent()), host, true)) { + return false; + } + host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID()); + break; + } + default: + NS_RUNTIMEABORT("not reached"); + } + } + + mCompositorBridge->ShadowLayersUpdated(this, aTransactionId, targetConfig, + aPlugins, isFirstPaint, scheduleComposite, + paintSequenceNumber, isRepeatTransaction, + aPaintSyncId, updateHitTestingTree); + + { + AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this)); + layer_manager()->EndTransaction(TimeStamp(), LayerManager::END_NO_IMMEDIATE_REDRAW); + } + + if (reply) { + reply->SetCapacity(replyv.size()); + if (replyv.size() > 0) { + reply->AppendElements(&replyv.front(), replyv.size()); + } + } + + 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 = gfxPrefs::LayersDrawFPS(); + if (drawFps) { + uint32_t visualWarningTrigger = gfxPrefs::LayerTransactionWarning(); + // The default theshold is 200ms to trigger, hit red when it take 4 times longer + TimeDuration latency = TimeStamp::Now() - aTransactionStart; + if (latency > TimeDuration::FromMilliseconds(visualWarningTrigger)) { + float severity = (latency - TimeDuration::FromMilliseconds(visualWarningTrigger)).ToMilliseconds() / + (4 * visualWarningTrigger); + if (severity > 1.f) { + severity = 1.f; + } + mLayerManager->VisualFrameWarning(severity); + PR_LogPrint("LayerTransactionParent::RecvUpdate transaction from process %d took %f ms", + OtherPid(), + latency.ToMilliseconds()); + } + } + + profiler_tracing("Paint", "LayerTransaction", TRACING_INTERVAL_END); + return true; +} + +bool +LayerTransactionParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) +{ + mChildEpoch = aLayerObserverEpoch; + return true; +} + +bool +LayerTransactionParent::ShouldParentObserveEpoch() +{ + if (mParentEpoch == mChildEpoch) { + return false; + } + + mParentEpoch = mChildEpoch; + return true; +} + +bool +LayerTransactionParent::RecvSetTestSampleTime(const TimeStamp& aTime) +{ + return mCompositorBridge->SetTestSampleTime(this, aTime); +} + +bool +LayerTransactionParent::RecvLeaveTestMode() +{ + mCompositorBridge->LeaveTestMode(this); + return true; +} + +bool +LayerTransactionParent::RecvGetAnimationOpacity(PLayerParent* aParent, + float* aOpacity, + bool* aHasAnimationOpacity) +{ + *aHasAnimationOpacity = false; + if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) { + return false; + } + + Layer* layer = cast(aParent)->AsLayer(); + if (!layer) { + return false; + } + + mCompositorBridge->ApplyAsyncProperties(this); + + if (!layer->AsLayerComposite()->GetShadowOpacitySetByAnimation()) { + return true; + } + + *aOpacity = layer->GetLocalOpacity(); + *aHasAnimationOpacity = true; + return true; +} + +bool +LayerTransactionParent::RecvGetAnimationTransform(PLayerParent* aParent, + MaybeTransform* aTransform) +{ + if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) { + return false; + } + + Layer* layer = cast(aParent)->AsLayer(); + if (!layer) { + return false; + } + + // 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); + + // This method is specific to transforms applied by animation. + // This is because this method uses the information stored with an animation + // such as the origin of the reference frame corresponding to the layer, to + // recover the untranslated transform from the shadow transform. For + // transforms that are not set by animation we don't have this information + // available. + if (!layer->AsLayerComposite()->GetShadowTransformSetByAnimation()) { + *aTransform = mozilla::void_t(); + return true; + } + + // The following code recovers the untranslated transform + // from the shadow transform by undoing the translations in + // AsyncCompositionManager::SampleValue. + + Matrix4x4 transform = layer->AsLayerComposite()->GetShadowBaseTransform(); + if (ContainerLayer* c = layer->AsContainerLayer()) { + // Undo the scale transform applied by AsyncCompositionManager::SampleValue + transform.PostScale(1.0f/c->GetInheritedXScale(), + 1.0f/c->GetInheritedYScale(), + 1.0f); + } + float scale = 1; + Point3D scaledOrigin; + Point3D transformOrigin; + for (uint32_t i=0; i < layer->GetAnimations().Length(); i++) { + if (layer->GetAnimations()[i].data().type() == AnimationData::TTransformData) { + const TransformData& data = layer->GetAnimations()[i].data().get_TransformData(); + scale = data.appUnitsPerDevPixel(); + scaledOrigin = + Point3D(NS_round(NSAppUnitsToFloatPixels(data.origin().x, scale)), + NS_round(NSAppUnitsToFloatPixels(data.origin().y, scale)), + 0.0f); + transformOrigin = data.transformOrigin(); + break; + } + } + + // 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); + } + + // Undo the rebasing applied by + // nsDisplayTransform::GetResultingTransformMatrixInternal + transform.ChangeBasis(-transformOrigin); + + // Convert to CSS pixels (this undoes the operations performed by + // nsStyleTransformMatrix::ProcessTranslatePart which is called from + // nsDisplayTransform::GetResultingTransformMatrix) + double devPerCss = + double(scale) / double(nsDeviceContext::AppUnitsPerCSSPixel()); + transform._41 *= devPerCss; + transform._42 *= devPerCss; + transform._43 *= devPerCss; + + *aTransform = transform; + return true; +} + +static AsyncPanZoomController* +GetAPZCForViewID(Layer* aLayer, FrameMetrics::ViewID aScrollID) +{ + AsyncPanZoomController* resultApzc = nullptr; + ForEachNode<ForwardIterator>( + aLayer, + [aScrollID, &resultApzc] (Layer* layer) + { + for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) { + if (layer->GetFrameMetrics(i).GetScrollId() == aScrollID) { + resultApzc = layer->GetAsyncPanZoomController(i); + return TraversalFlag::Abort; + } + } + return TraversalFlag::Continue; + }); + return resultApzc; +} + +bool +LayerTransactionParent::RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aScrollID, + const float& aX, const float& aY) +{ + if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) { + return false; + } + + AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID); + if (!controller) { + return false; + } + controller->SetTestAsyncScrollOffset(CSSPoint(aX, aY)); + return true; +} + +bool +LayerTransactionParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollID, + const float& aValue) +{ + if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) { + return false; + } + + AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID); + if (!controller) { + return false; + } + controller->SetTestAsyncZoom(LayerToParentLayerScale(aValue)); + return true; +} + +bool +LayerTransactionParent::RecvFlushApzRepaints() +{ + mCompositorBridge->FlushApzRepaints(this); + return true; +} + +bool +LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData) +{ + mCompositorBridge->GetAPZTestData(this, aOutData); + return true; +} + +bool +LayerTransactionParent::RecvRequestProperty(const nsString& aProperty, float* aValue) +{ + if (aProperty.Equals(NS_LITERAL_STRING("overdraw"))) { + *aValue = layer_manager()->GetCompositor()->GetFillRatio(); + } else if (aProperty.Equals(NS_LITERAL_STRING("missed_hwc"))) { + *aValue = layer_manager()->LastFrameMissedHWC() ? 1 : 0; + } else { + *aValue = -1; + } + return true; +} + +bool +LayerTransactionParent::RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId, + nsTArray<ScrollableLayerGuid>&& aTargets) +{ + mCompositorBridge->SetConfirmedTargetAPZC(this, aBlockId, aTargets); + return true; +} + +bool +LayerTransactionParent::Attach(ShadowLayerParent* aLayerParent, + CompositableHost* aCompositable, + bool aIsAsync) +{ + if (!aCompositable) { + return false; + } + + Layer* baselayer = aLayerParent->AsLayer(); + if (!baselayer) { + return false; + } + LayerComposite* layer = baselayer->AsLayerComposite(); + if (!layer) { + return false; + } + + Compositor* compositor + = static_cast<LayerManagerComposite*>(aLayerParent->AsLayer()->Manager())->GetCompositor(); + + if (!layer->SetCompositableHost(aCompositable)) { + // not all layer types accept a compositable, see bug 967824 + return false; + } + aCompositable->Attach(aLayerParent->AsLayer(), + compositor, + aIsAsync + ? CompositableHost::ALLOW_REATTACH + | CompositableHost::KEEP_ATTACHED + : CompositableHost::NO_FLAGS); + return true; +} + +bool +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 true; +} + +bool +LayerTransactionParent::RecvForceComposite() +{ + mCompositorBridge->ForceComposite(this); + return true; +} + +PLayerParent* +LayerTransactionParent::AllocPLayerParent() +{ + return new ShadowLayerParent(); +} + +bool +LayerTransactionParent::DeallocPLayerParent(PLayerParent* actor) +{ + delete actor; + return true; +} + +PCompositableParent* +LayerTransactionParent::AllocPCompositableParent(const TextureInfo& aInfo) +{ + return CompositableHost::CreateIPDLActor(this, aInfo, 0); +} + +bool +LayerTransactionParent::DeallocPCompositableParent(PCompositableParent* aActor) +{ + return CompositableHost::DestroyIPDLActor(aActor); +} + +void +LayerTransactionParent::ActorDestroy(ActorDestroyReason why) +{ +} + +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); +} + +void +LayerTransactionParent::DeallocShmem(ipc::Shmem& aShmem) +{ + if (!mIPCOpen || mDestroyed) { + return; + } + PLayerTransactionParent::DeallocShmem(aShmem); +} + +bool LayerTransactionParent::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + +void +LayerTransactionParent::SendAsyncMessage(const InfallibleTArray<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"); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/LayerTransactionParent.h b/gfx/layers/ipc/LayerTransactionParent.h new file mode 100644 index 000000000..d92aa0358 --- /dev/null +++ b/gfx/layers/ipc/LayerTransactionParent.h @@ -0,0 +1,240 @@ +/* -*- 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/. */ + +#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 "nsTArrayForwardDeclare.h" // for InfallibleTArray + +namespace mozilla { + +namespace ipc { +class Shmem; +} // namespace ipc + +namespace layout { +class RenderFrameParent; +} // namespace layout + +namespace layers { + +class Layer; +class LayerManagerComposite; +class ShadowLayerParent; +class CompositableParent; +class CompositorBridgeParentBase; + +class LayerTransactionParent final : public PLayerTransactionParent, + public CompositableParentManager, + public ShmemAllocator +{ + typedef mozilla::layout::RenderFrameParent RenderFrameParent; + typedef InfallibleTArray<Edit> EditArray; + typedef InfallibleTArray<OpDestroy> OpDestroyArray; + typedef InfallibleTArray<EditReply> EditReplyArray; + typedef InfallibleTArray<PluginWindowData> PluginsArray; + +public: + LayerTransactionParent(LayerManagerComposite* aManager, + CompositorBridgeParentBase* aBridge, + uint64_t aId); + +protected: + ~LayerTransactionParent(); + +public: + void Destroy(); + + LayerManagerComposite* layer_manager() const { return mLayerManager; } + + void SetLayerManager(LayerManagerComposite* aLayerManager); + + uint64_t GetId() const { return mId; } + Layer* GetRoot() const { return mRoot; } + + uint64_t GetChildEpoch() const { return mChildEpoch; } + bool ShouldParentObserveEpoch(); + + virtual ShmemAllocator* AsShmemAllocator() override { return this; } + + virtual bool AllocShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) override; + + virtual bool AllocUnsafeShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) override; + + virtual void DeallocShmem(ipc::Shmem& aShmem) override; + + virtual bool IsSameProcess() const override; + + const uint64_t& GetPendingTransactionId() { return mPendingTransaction; } + void SetPendingTransactionId(uint64_t aId) { mPendingTransaction = aId; } + + // CompositableParentManager + virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override; + + virtual void SendPendingAsyncMessages() override; + + virtual void SetAboutToSendAsyncMessages() override; + + virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override; + + virtual base::ProcessId GetChildProcessId() override + { + return OtherPid(); + } + + void AddPendingCompositorUpdate() { + mPendingCompositorUpdates++; + } + void SetPendingCompositorUpdates(uint32_t aCount) { + // Only called after construction. + MOZ_ASSERT(mPendingCompositorUpdates == 0); + mPendingCompositorUpdates = aCount; + } + void AcknowledgeCompositorUpdate() { + MOZ_ASSERT(mPendingCompositorUpdates > 0); + mPendingCompositorUpdates--; + } + +protected: + virtual bool RecvShutdown() override; + + virtual bool RecvPaintTime(const uint64_t& aTransactionId, + const TimeDuration& aPaintTime) override; + + virtual bool RecvUpdate(EditArray&& cset, + OpDestroyArray&& aToDestroy, + const uint64_t& aFwdTransactionId, + const uint64_t& aTransactionId, + const TargetConfig& targetConfig, + PluginsArray&& aPlugins, + const bool& isFirstPaint, + const bool& scheduleComposite, + const uint32_t& paintSequenceNumber, + const bool& isRepeatTransaction, + const mozilla::TimeStamp& aTransactionStart, + const int32_t& aPaintSyncId, + EditReplyArray* reply) override; + + virtual bool RecvUpdateNoSwap(EditArray&& cset, + OpDestroyArray&& aToDestroy, + const uint64_t& aFwdTransactionId, + const uint64_t& aTransactionId, + const TargetConfig& targetConfig, + PluginsArray&& aPlugins, + const bool& isFirstPaint, + const bool& scheduleComposite, + const uint32_t& paintSequenceNumber, + const bool& isRepeatTransaction, + const mozilla::TimeStamp& aTransactionStart, + const int32_t& aPaintSyncId) override; + + virtual bool RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override; + + virtual bool RecvClearCachedResources() override; + virtual bool RecvForceComposite() override; + virtual bool RecvSetTestSampleTime(const TimeStamp& aTime) override; + virtual bool RecvLeaveTestMode() override; + virtual bool RecvGetAnimationOpacity(PLayerParent* aParent, + float* aOpacity, + bool* aHasAnimationOpacity) override; + virtual bool RecvGetAnimationTransform(PLayerParent* aParent, + MaybeTransform* aTransform) + override; + virtual bool RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aId, + const float& aX, const float& aY) override; + virtual bool RecvSetAsyncZoom(const FrameMetrics::ViewID& aId, + const float& aValue) override; + virtual bool RecvFlushApzRepaints() override; + virtual bool RecvGetAPZTestData(APZTestData* aOutData) override; + virtual bool RecvRequestProperty(const nsString& aProperty, float* aValue) override; + virtual bool RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId, + nsTArray<ScrollableLayerGuid>&& aTargets) override; + + virtual PLayerParent* AllocPLayerParent() override; + virtual bool DeallocPLayerParent(PLayerParent* actor) override; + + virtual PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo) override; + virtual bool DeallocPCompositableParent(PCompositableParent* actor) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + bool Attach(ShadowLayerParent* aLayerParent, + 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 CrossProcessCompositorBridgeParent; + friend class layout::RenderFrameParent; + +private: + RefPtr<LayerManagerComposite> mLayerManager; + CompositorBridgeParentBase* mCompositorBridge; + // Hold the root because it might be grafted under various + // containers in the "real" layer tree + RefPtr<Layer> mRoot; + // When this is nonzero, it refers to a layer tree owned by the + // compositor thread. It is always true that + // mId != 0 => mRoot == null + // because the "real tree" is owned by the compositor. + uint64_t 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 TabParent about + // (via ObserveLayerUpdate). + uint64_t mChildEpoch; + uint64_t mParentEpoch; + + uint64_t mPendingTransaction; + + // Number of compositor updates we're waiting for the child to + // acknowledge. + uint32_t mPendingCompositorUpdates; + + // 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; +}; + +} // 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 000000000..9dac68688 --- /dev/null +++ b/gfx/layers/ipc/LayerTreeOwnerTracker.cpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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/dom/ContentParent.h" // for ContentParent +#include "mozilla/gfx/GPUChild.h" // for GPUChild +#include "mozilla/gfx/GPUProcessManager.h" // for GPUProcessManager + +#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(uint64_t aLayersId, base::ProcessId aProcessId) +{ + MutexAutoLock lock(mLayerIdsLock); + + // Add the mapping to the list + mLayerIds[aLayersId] = aProcessId; +} + +void +LayerTreeOwnerTracker::Unmap(uint64_t aLayersId, base::ProcessId aProcessId) +{ + MutexAutoLock lock(mLayerIdsLock); + + MOZ_ASSERT(mLayerIds[aLayersId] == aProcessId); + mLayerIds.erase(aLayersId); +} + +bool +LayerTreeOwnerTracker::IsMapped(uint64_t aLayersId, base::ProcessId aProcessId) +{ + MutexAutoLock lock(mLayerIdsLock); + + auto iter = mLayerIds.find(aLayersId); + return iter != mLayerIds.end() && iter->second == aProcessId; +} + +void +LayerTreeOwnerTracker::Iterate(function<void(uint64_t 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 000000000..71f734bbf --- /dev/null +++ b/gfx/layers/ipc/LayerTreeOwnerTracker.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=4 ts=8 et 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 "mozilla/Mutex.h" // for mozilla::Mutex +#include "mozilla/Function.h" + +#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(uint64_t aLayersId, base::ProcessId aProcessId); + + /** + * Remove an existing mapping. + */ + void Unmap(uint64_t aLayersId, base::ProcessId aProcessId); + + /** + * Checks whether it is okay for aProcessId to access aLayersId. + */ + bool IsMapped(uint64_t aLayersId, base::ProcessId aProcessId); + + void Iterate(function<void(uint64_t aLayersId, base::ProcessId aProcessId)> aCallback); + +private: + LayerTreeOwnerTracker(); + + mozilla::Mutex mLayerIdsLock; + std::map<uint64_t, base::ProcessId> mLayerIds; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_LayerTreeOwnerTracker_h diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh new file mode 100644 index 000000000..dbbb3649a --- /dev/null +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -0,0 +1,499 @@ +/* -*- 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 PCompositable; +include protocol PCompositorBridge; +include protocol PLayer; +include protocol PImageContainer; +include protocol PRenderFrame; +include protocol PTexture; + +include "gfxipc/ShadowLayerUtils.h"; +include "mozilla/GfxMessageUtils.h"; +include "ImageLayers.h"; + +using mozilla::gfx::SamplingFilter from "mozilla/gfx/2D.h"; +using struct mozilla::gfx::Color from "mozilla/gfx/2D.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 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 mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h"; +using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h"; +using mozilla::LayerMargin from "Units.h"; +using mozilla::LayerPoint from "Units.h"; +using mozilla::LayerRect from "Units.h"; +using mozilla::LayerIntRegion from "Units.h"; +using mozilla::ParentLayerIntRect from "Units.h"; +using mozilla::LayoutDeviceIntRect 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 struct mozilla::layers::ScrollMetadata from "FrameMetrics.h"; +using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h"; +using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::MaybeLayerClip from "FrameMetrics.h"; + +namespace mozilla { +namespace layers { + +struct TargetConfig { + IntRect naturalBounds; + ScreenRotation rotation; + ScreenOrientationInternal orientation; + nsIntRegion clearRegion; +}; + +// Create a shadow layer for |layer| +struct OpCreatePaintedLayer { PLayer layer; }; +struct OpCreateContainerLayer { PLayer layer; }; +struct OpCreateImageLayer { PLayer layer; }; +struct OpCreateColorLayer { PLayer layer; }; +struct OpCreateCanvasLayer { PLayer layer; }; +struct OpCreateRefLayer { PLayer layer; }; + +struct OpAttachCompositable { + PLayer layer; + PCompositable compositable; +}; + +struct OpAttachAsyncCompositable { + PLayer layer; + uint64_t containerID; +}; + +struct ThebesBufferData { + IntRect rect; + IntPoint rotation; +}; + +struct CubicBezierFunction { + float x1; + float y1; + float x2; + float y2; +}; + +struct StepFunction { + int steps; + // 1 = nsTimingFunction::StepStart, 2 = nsTimingFunction::StepEnd + int type; +}; + +union TimingFunction { + null_t; + CubicBezierFunction; + StepFunction; +}; + +// Send the angle with units rather than sending all angles in radians +// to avoid having floating point error introduced by unit switching. +struct CSSAngle { + float value; + int unit; // an nsCSSUnit that is valid for angles +}; + +struct LayerColor { Color value; }; +struct Perspective { float value; }; +struct RotationX { CSSAngle angle; }; +struct RotationY { CSSAngle angle; }; +struct RotationZ { CSSAngle angle; }; +struct Rotation { CSSAngle angle; }; +struct Rotation3D { + float x; + float y; + float z; + CSSAngle angle; +}; +struct Scale { + float x; + float y; + float z; +}; +struct Skew { CSSAngle x; CSSAngle y; }; +struct SkewX { CSSAngle x; }; +struct SkewY { CSSAngle y; }; +struct TransformMatrix { Matrix4x4 value; }; +struct Translation { + float x; + float y; + float z; +}; + +union TransformFunction { + Perspective; + RotationX; + RotationY; + RotationZ; + Rotation; + Rotation3D; + Scale; + Skew; + SkewX; + SkewY; + Translation; + TransformMatrix; +}; + +union Animatable { + float; + TransformFunction[]; +}; + +struct AnimationSegment { + Animatable startState; + Animatable endState; + float startPortion; + float endPortion; + TimingFunction sampleFn; +}; + +// Transforms need extra information to correctly convert the list of transform +// functions to a Matrix4x4 that can be applied directly to the layer. +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; +}; + +union AnimationData { + null_t; + TransformData; +}; + +struct Animation { + TimeStamp startTime; + TimeDuration delay; + // The value of the animation's current time at the moment it was created. + // For animations that are waiting to start, their startTime will be null. + // Once the animation is ready to start, we calculate an appropriate value + // of startTime such that we begin playback from initialCurrentTime. + TimeDuration initialCurrentTime; + 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; + AnimationData data; + float playbackRate; + // This is used in the transformed progress calculation. + TimingFunction easingFunction; + uint8_t iterationComposite; +}; + +// Change a layer's attributes +struct CommonLayerAttributes { + IntRect layerBounds; + LayerIntRegion visibleRegion; + EventRegions eventRegions; + TransformMatrix transform; + bool transformIsPerspective; + float postXScale; + float postYScale; + uint32_t contentFlags; + float opacity; + bool useClipRect; + ParentLayerIntRect clipRect; + MaybeLayerClip scrolledClip; + bool isFixedPosition; + uint64_t fixedPositionScrollContainerId; + LayerPoint fixedPositionAnchor; + int32_t fixedPositionSides; + bool isStickyPosition; + uint64_t stickyScrollContainerId; + LayerRect stickyScrollRangeOuter; + LayerRect stickyScrollRangeInner; + uint64_t scrollbarTargetContainerId; + uint32_t scrollbarDirection; + float scrollbarThumbRatio; + bool isScrollbarContainer; + int8_t mixBlendMode; + bool forceIsolatedGroup; + nullable PLayer maskLayer; + PLayer[] ancestorMaskLayers; + // Animated colors will only honored for ColorLayers. + Animation[] animations; + nsIntRegion invalidRegion; + ScrollMetadata[] scrollMetadata; + nsCString displayListLog; +}; + +struct PaintedLayerAttributes { + nsIntRegion validRegion; +}; +struct ContainerLayerAttributes { + float preXScale; + float preYScale; + float inheritedXScale; + float inheritedYScale; + float presShellResolution; + bool scaleToResolution; + EventRegionsOverride eventRegionsOverride; +}; +struct ColorLayerAttributes { LayerColor color; IntRect bounds; }; +struct CanvasLayerAttributes { SamplingFilter samplingFilter; IntRect bounds; }; +struct RefLayerAttributes { + int64_t id; + // TODO: Once bug 1132895 is fixed we shouldn't need to propagate the override + // explicitly here. + EventRegionsOverride eventRegionsOverride; +}; +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 +struct PluginWindowData { + uintptr_t windowId; + LayoutDeviceIntRect[] clip; + LayoutDeviceIntRect bounds; + bool visible; +}; + +struct OpSetLayerAttributes { + PLayer layer; + LayerAttributes attrs; +}; + +// Monkey with the tree structure +struct OpSetRoot { PLayer root; }; +struct OpInsertAfter { PLayer container; PLayer childLayer; PLayer after; }; +struct OpPrependChild { PLayer container; PLayer childLayer; }; +struct OpRemoveChild { PLayer container; PLayer childLayer; }; +struct OpRepositionChild { PLayer container; PLayer childLayer; PLayer after; }; +struct OpRaiseToTopChild { PLayer container; PLayer childLayer; }; + +struct OpSetDiagnosticTypes { DiagnosticTypes diagnostics; }; +struct OpWindowOverlayChanged { }; + +struct ShmemSection { + Shmem shmem; + uint32_t offset; + size_t size; +}; + +union ReadLockDescriptor { + ShmemSection; + uintptr_t; + null_t; +}; + +union MaybeTexture { + PTexture; + null_t; +}; + +struct TexturedTileDescriptor { + PTexture texture; + MaybeTexture textureOnWhite; + IntRect updateRect; + ReadLockDescriptor sharedLock; + ReadLockDescriptor sharedLockOnWhite; + 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 OpUseOverlaySource { + OverlaySource overlay; + IntRect picture; +}; + +struct OpPaintTextureRegion { + ThebesBufferData bufferData; + nsIntRegion updatedRegion; +}; + +/** + * Tells the CompositableHost to remove the corresponding TextureHost + */ +struct OpRemoveTexture { + PTexture texture; +}; + +struct TimedTexture { + PTexture texture; + ReadLockDescriptor sharedLock; + TimeStamp timeStamp; + IntRect picture; + uint32_t frameID; + uint32_t producerID; +}; + +/** + * 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; + ReadLockDescriptor sharedLockBlack; + ReadLockDescriptor sharedLockWhite; +}; + +union MaybeRegion { + nsIntRegion; + null_t; +}; + +struct OpNotifyNotUsed { + uint64_t TextureId; + uint64_t fwdTransactionId; +}; + +union CompositableOperationDetail { + OpPaintTextureRegion; + + OpUseTiledLayerBuffer; + + OpRemoveTexture; + + OpUseTexture; + OpUseComponentAlphaTextures; + OpUseOverlaySource; +}; + +struct CompositableOperation { + PCompositable 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; + + OpSetLayerAttributes; + OpSetDiagnosticTypes; + OpWindowOverlayChanged; + + 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; + PCompositable; +}; + +// Replies to operations + +struct OpContentBufferSwap { + PCompositable compositable; + nsIntRegion frontUpdatedRegion; +}; + +/** + * An ImageCompositeNotification is sent the first time a particular + * image is composited by an ImageHost. + */ +struct ImageCompositeNotification { + PImageContainer imageContainer; + TimeStamp imageTimeStamp; + TimeStamp firstCompositeTimeStamp; + uint32_t frameID; + uint32_t producerID; +}; + +// Unit of a "changeset reply". This is a weird abstraction, probably +// only to be used for buffer swapping. +union EditReply { + OpContentBufferSwap; +}; + +union AsyncParentMessageData { + OpNotifyNotUsed; +}; + +} // namespace +} // namespace diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh new file mode 100644 index 000000000..8eeda4ada --- /dev/null +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -0,0 +1,142 @@ +/* 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 struct gfxPoint from "gfxPoint.h"; +using nsIntRegion from "nsRegion.h"; +using struct mozilla::layers::SurfaceDescriptorX11 from "gfxipc/ShadowLayerUtils.h"; +using mozilla::StereoMode from "ImageTypes.h"; +using mozilla::YUVColorSpace from "ImageTypes.h"; +using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; +using mozilla::WindowsHandle from "ipc/IPCMessageUtils.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 gfxImageFormat from "gfxTypes.h"; +using struct mozilla::layers::GonkNativeHandle from "mozilla/layers/GonkNativeHandleUtils.h"; + +namespace mozilla { +namespace layers { + +union OverlayHandle { + int32_t; + GonkNativeHandle; + null_t; +}; + +struct OverlaySource { + OverlayHandle handle; + IntSize size; +}; + +struct SurfaceDescriptorD3D9 { + // IDirect3DTexture9* + uintptr_t texture; +}; + +struct SurfaceDescriptorFileMapping { + WindowsHandle handle; + SurfaceFormat format; + IntSize size; +}; + +struct SurfaceDescriptorDIB { + // gfxWindowsSurface* + uintptr_t surface; +}; + +struct SurfaceDescriptorD3D10 { + WindowsHandle handle; + SurfaceFormat format; + IntSize size; +}; + +struct SurfaceDescriptorDXGIYCbCr { + WindowsHandle handleY; + WindowsHandle handleCb; + WindowsHandle handleCr; + IntSize size; + IntSize sizeY; + IntSize sizeCbCr; +}; + +struct SurfaceDescriptorMacIOSurface { + uint32_t surfaceId; + double scaleFactor; + bool isOpaque; +}; + +struct SurfaceTextureDescriptor { + uintptr_t surfTex; + IntSize size; +}; + +struct EGLImageDescriptor { + uintptr_t image; // `EGLImage` is a `void*`. + uintptr_t fence; + IntSize size; + bool hasAlpha; +}; + +struct SurfaceDescriptorSharedGLTexture { + uint32_t texture; + uint32_t target; + uintptr_t fence; + IntSize size; + bool hasAlpha; +}; + +struct SurfaceDescriptorGPUVideo { + uint64_t handle; +}; + +struct RGBDescriptor { + IntSize size; + SurfaceFormat format; + bool hasIntermediateBuffer; +}; + +struct YCbCrDescriptor { + IntSize ySize; + IntSize cbCrSize; + uint32_t yOffset; + uint32_t cbOffset; + uint32_t crOffset; + StereoMode stereoMode; + YUVColorSpace yUVColorSpace; + bool hasIntermediateBuffer; +}; + +union BufferDescriptor { + RGBDescriptor; + YCbCrDescriptor; +}; + +union MemoryOrShmem { + uintptr_t; + Shmem; +}; + +struct SurfaceDescriptorBuffer { + BufferDescriptor desc; + MemoryOrShmem data; +}; + +union SurfaceDescriptor { + SurfaceDescriptorBuffer; + SurfaceDescriptorD3D9; + SurfaceDescriptorDIB; + SurfaceDescriptorD3D10; + SurfaceDescriptorFileMapping; + SurfaceDescriptorDXGIYCbCr; + SurfaceDescriptorX11; + SurfaceTextureDescriptor; + EGLImageDescriptor; + SurfaceDescriptorMacIOSurface; + SurfaceDescriptorSharedGLTexture; + SurfaceDescriptorGPUVideo; + null_t; +}; + +} // namespace +} // namespace diff --git a/gfx/layers/ipc/PAPZ.ipdl b/gfx/layers/ipc/PAPZ.ipdl new file mode 100644 index 000000000..e321b9ea0 --- /dev/null +++ b/gfx/layers/ipc/PAPZ.ipdl @@ -0,0 +1,71 @@ +/* -*- 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 protocol PCompositorBridge; + +using CSSRect from "Units.h"; +using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; +using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; +using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h"; +using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h"; +using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h"; +using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.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 RequestContentRepaint(FrameMetrics frame); + + async UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent); + + async UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent); + + async SetScrollingRootContent(bool aIsRootContent); + + async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent); + + async NotifyAPZStateChange(ScrollableLayerGuid aGuid, APZStateChange aChange, int aArg); + + async NotifyFlushComplete(); + + async Destroy(); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PAPZCTreeManager.ipdl b/gfx/layers/ipc/PAPZCTreeManager.ipdl new file mode 100644 index 000000000..21d899f91 --- /dev/null +++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl @@ -0,0 +1,139 @@ +/* -*- 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 "ipc/nsGUIEventIPC.h"; + +include protocol PCompositorBridge; + +using CSSRect from "Units.h"; +using LayoutDeviceCoord from "Units.h"; +using LayoutDeviceIntPoint from "Units.h"; +using mozilla::LayoutDevicePoint from "Units.h"; +using ScreenPoint from "Units.h"; +using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; +using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h"; +using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h"; +using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h"; +using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h"; + +using nsEventStatus from "mozilla/EventForwards.h"; +using EventMessage from "mozilla/EventForwards.h"; +using mozilla::Modifiers from "mozilla/EventForwards.h"; +using class mozilla::WidgetInputEvent from "mozilla/BasicEvents.h"; +using class mozilla::WidgetMouseEventBase from "mozilla/MouseEvents.h"; +using mozilla::WidgetMouseEvent::Reason from "mozilla/MouseEvents.h"; +using class mozilla::WidgetTouchEvent from "mozilla/TouchEvents.h"; +using class mozilla::WidgetWheelEvent from "mozilla/MouseEvents.h"; +using class mozilla::InputData from "InputData.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 mozilla::PinchGestureInput::PinchGestureType from "InputData.h"; +using class mozilla::TapGestureInput from "InputData.h"; +using class mozilla::ScrollWheelInput 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. + */ +sync 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 CancelAnimation(ScrollableLayerGuid aGuid); + + async AdjustScrollForSurfaceShift(ScreenPoint aShift); + + async SetDPI(float aDpiValue); + + async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aValues); + + async StartScrollbarDrag(ScrollableLayerGuid aGuid, AsyncDragMetrics aDragMetrics); + + async SetLongTapEnabled(bool aTapGestureEnabled); + + async ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY); + + // The following messages are used to + // implement the ReceiveInputEvent methods + + sync ReceiveMultiTouchInputEvent(MultiTouchInput aEvent) + returns (nsEventStatus aOutStatus, + MultiTouchInput aOutEvent, + ScrollableLayerGuid aOutTargetGuid, + uint64_t aOutInputBlockId); + + sync ReceiveMouseInputEvent(MouseInput aEvent) + returns (nsEventStatus aOutStatus, + MouseInput aOutEvent, + ScrollableLayerGuid aOutTargetGuid, + uint64_t aOutInputBlockId); + + sync ReceivePanGestureInputEvent(PanGestureInput aEvent) + returns (nsEventStatus aOutStatus, + PanGestureInput aOutEvent, + ScrollableLayerGuid aOutTargetGuid, + uint64_t aOutInputBlockId); + + sync ReceivePinchGestureInputEvent(PinchGestureInput aEvent) + returns (nsEventStatus aOutStatus, + PinchGestureInput aOutEvent, + ScrollableLayerGuid aOutTargetGuid, + uint64_t aOutInputBlockId); + + sync ReceiveTapGestureInputEvent(TapGestureInput aEvent) + returns (nsEventStatus aOutStatus, + TapGestureInput aOutEvent, + ScrollableLayerGuid aOutTargetGuid, + uint64_t aOutInputBlockId); + + sync ReceiveScrollWheelInputEvent(ScrollWheelInput aEvent) + returns (nsEventStatus aOutStatus, + ScrollWheelInput aOutEvent, + ScrollableLayerGuid aOutTargetGuid, + uint64_t aOutInputBlockId); + + async UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage); + + sync TransformEventRefPoint(LayoutDeviceIntPoint aRefPoint) + returns (LayoutDeviceIntPoint aOutRefPoint, + ScrollableLayerGuid aOutTargetGuid); + + async __delete__(); + +child: + + async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers, + ScrollableLayerGuid aGuid, uint64_t aInputBlockId); + + async NotifyPinchGesture(PinchGestureType aType, ScrollableLayerGuid aGuid, + LayoutDeviceCoord aSpanChange, Modifiers aModifiers); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/layers/ipc/PCompositable.ipdl b/gfx/layers/ipc/PCompositable.ipdl new file mode 100644 index 000000000..d7754cd95 --- /dev/null +++ b/gfx/layers/ipc/PCompositable.ipdl @@ -0,0 +1,28 @@ +/* -*- 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 PLayerTransaction; +include protocol PImageBridge; +include protocol PCompositorBridge; + +namespace mozilla { +namespace layers { + +async protocol PCompositable +{ + manager PImageBridge or PLayerTransaction; +child: + async __delete__(); +parent: + /** + * Asynchronously tell the compositor side to remove the texture. + */ + async Destroy(); +}; + +} // namespace +} // namespace diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl new file mode 100644 index 000000000..03a353506 --- /dev/null +++ b/gfx/layers/ipc/PCompositorBridge.ipdl @@ -0,0 +1,237 @@ +/* -*- 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 protocol PAPZ; +include protocol PAPZCTreeManager; +include protocol PBrowser; +include protocol PCompositable; +include protocol PCompositorWidget; +include protocol PImageContainer; +include protocol PLayer; +include protocol PLayerTransaction; +include protocol PTexture; +include "mozilla/GfxMessageUtils.h"; + +using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; +using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; +using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; +using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h"; +using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h"; +using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; +using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; +using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.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 class mozilla::TimeStamp from "mozilla/TimeStamp.h"; +using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h"; +using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; + +namespace mozilla { +namespace layers { + + +/** + * The PCompositorBridge protocol is used to manage communication between + * the main thread and the compositor thread context. It's primary + * purpose is to manage the PLayerTransaction sub protocol. + */ +sync protocol PCompositorBridge +{ + manages PAPZ; + manages PAPZCTreeManager; + // A Compositor manages a single Layer Manager (PLayerTransaction) + manages PLayerTransaction; + manages PTexture; + manages PCompositorWidget; + +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(uint64_t layersId); + + // The compositor type or device has changed, and a new texture factory + // identifier is available. Layers must be invalidated and the new identifier + // must be propagated. + async CompositorUpdated(uint64_t layersId, TextureFactoryIdentifier newIdentifier); + + // 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. + async DidComposite(uint64_t id, uint64_t transactionId, + TimeStamp compositeStart, TimeStamp compositeEnd); + + // The parent sends the child the requested fill ratio numbers. + async Overfill(uint32_t aOverfill); + + /** + * 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); + + /** + * Drop any buffers that might be retained on the child compositor + * side. + */ + async ClearCachedResources(uint64_t id); + + async ParentAsyncMessages(AsyncParentMessageData[] aMessages); + + async ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive); + +parent: + // Must be called before Initialize(). + async PCompositorWidget(CompositorWidgetInitData aInitData); + + // When out-of-process, this must be called to finish initialization. + sync Initialize(uint64_t rootLayerTreeId); + sync Reset(LayersBackend[] aBackendHints) returns (bool aResult, TextureFactoryIdentifier aOutIdentifier); + + // Returns whether this Compositor has APZ enabled or not. + sync AsyncPanZoomEnabled(uint64_t layersId) returns (bool aHasAPZ); + + // Must be called after Initialize(), and only succeeds if AsyncPanZoomEnabled() is true. + async PAPZ(uint64_t layersId); + async PAPZCTreeManager(uint64_t layersId); + + /** + * Confirmation callback for UpdatePluginConfigurations and HideAllPlugins. + */ + async RemotePluginsReady(); + + // Confirmation that the child has invalidated all its layers, and will not + // request layers against an old compositor. + async AcknowledgeCompositorUpdate(uint64_t id); + + // Child sends the parent a request for fill ratio numbers. + async RequestOverfill(); + + // Child requests frame uniformity measurements + sync GetFrameUniformity() returns (FrameUniformityData data); + + // 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(); + + // See bug 1316632 comment #33 for why this has to be sync. Otherwise, + // there are ordering issues with SendPLayerTransactionConstructor. + sync NotifyChildCreated(uint64_t id); + + async AdoptChild(uint64_t 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(uint64_t id); + + // 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(); + + // 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. + sync PLayerTransaction(LayersBackend[] layersBackendHints, uint64_t id) + returns (TextureFactoryIdentifier textureFactoryIdentifier, bool success); + + // 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(); + + // The child clears the 'approximately visible' regions associated with the + // provided layers ID and pres shell ID (i.e., the regions for all view IDs + // associated with those IDs). + async ClearApproximatelyVisibleRegions(uint64_t layersId, uint32_t presShellId); + + // The child sends a region containing rects associated with the provided + // scrollable layer GUID that the child considers 'approximately visible'. + // We visualize this information in the APZ minimap. + async NotifyApproximatelyVisibleRegion(ScrollableLayerGuid guid, CSSIntRegion region); + + /** + * Sent when the child has finished CaptureAllPlugins. + */ + async AllPluginsCaptured(); + + async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t id, uint64_t aSerial); + + sync SyncWithCompositor(); + +child: + // Send back Compositor Frame Metrics from APZCs so tiled layers can + // update progressively. + async SharedCompositorFrameMetrics(Handle metrics, CrossProcessMutexHandle mutex, uint64_t aLayersId, uint32_t aAPZCId); + async ReleaseSharedCompositorFrameMetrics(ViewID aId, uint32_t aAPZCId); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PImageBridge.ipdl b/gfx/layers/ipc/PImageBridge.ipdl new file mode 100644 index 000000000..3bed222d0 --- /dev/null +++ b/gfx/layers/ipc/PImageBridge.ipdl @@ -0,0 +1,70 @@ +/* -*- 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 PCompositable; +include protocol PImageContainer; +include protocol PLayer; +include protocol PTexture; +include ProtocolTypes; +include protocol PMediaSystemResourceManager; + +include "mozilla/GfxMessageUtils.h"; + +using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h"; +using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h"; + +using PlatformThreadId from "base/platform_thread.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 PCompositable; + manages PTexture; + manages PMediaSystemResourceManager; + manages PImageContainer; + +child: + async ParentAsyncMessages(AsyncParentMessageData[] aMessages); + + async DidComposite(ImageCompositeNotification[] aNotifications); + +parent: + async ImageBridgeThreadId(PlatformThreadId aTreahdId); + + sync Update(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId) + returns (EditReply[] reply); + + async UpdateNoSwap(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. + sync WillClose(); + + sync PCompositable(TextureInfo aInfo, + nullable PImageContainer aImageContainer) returns (uint64_t id); + async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t aSerial); + async PMediaSystemResourceManager(); + async PImageContainer(); + +}; + + +} // namespace +} // namespace + diff --git a/gfx/layers/ipc/PImageContainer.ipdl b/gfx/layers/ipc/PImageContainer.ipdl new file mode 100644 index 000000000..7ae567a88 --- /dev/null +++ b/gfx/layers/ipc/PImageContainer.ipdl @@ -0,0 +1,33 @@ +/* -*- 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 PImageBridge; + +namespace mozilla { +namespace layers { + +/** + * PImageContainer represents an ImageContainer. + */ + +async protocol PImageContainer { + manager PImageBridge; +parent: + /** + * The child effectively owns the parent. When the child should be + * destroyed, it sends an AsyncDelete to the parent but does not die + * because we could still have messages in flight from the compositor + * mentioning the child. The parent handles AsyncDelete by destroying + * itself and sending __delete__ to the child to clean it up. + */ + async AsyncDelete(); +child: + async __delete__(); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PLayer.ipdl b/gfx/layers/ipc/PLayer.ipdl new file mode 100644 index 000000000..4a46b56d6 --- /dev/null +++ b/gfx/layers/ipc/PLayer.ipdl @@ -0,0 +1,39 @@ +/* -*- 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 PLayerTransaction; + +namespace mozilla { +namespace layers { + +/** + * PLayer represents a layer shared across thread contexts. + */ + +async protocol PLayer { + manager PLayerTransaction; + + /** + * OWNERSHIP MODEL + * + * Roughly speaking, the child side "actually owns" a Layer. This + * is because the parent side is the "shadow"; when the child + * releases a Layer, the parent's shadow is no longer meaningful. + * + * To implement this model, the concrete PLayerParent keeps a + * strong ref to its Layer, so the Layer's lifetime is bound to + * the PLayerParent's. Then, when the Layer's refcount hits 0 on + * the child side, we send __delete__() from the child to parent. + * The parent then releases its Layer, which results in the Layer + * being deleted "soon" (usually immediately). + */ +parent: + async __delete__(); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PLayerTransaction.ipdl b/gfx/layers/ipc/PLayerTransaction.ipdl new file mode 100644 index 000000000..d669b1d65 --- /dev/null +++ b/gfx/layers/ipc/PLayerTransaction.ipdl @@ -0,0 +1,133 @@ +/* -*- 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 PCompositable; +include protocol PCompositorBridge; +include protocol PLayer; +include protocol PRenderFrame; +include protocol PTexture; + +include "mozilla/GfxMessageUtils.h"; + +using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h"; +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; +using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h"; +using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h"; +using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; +using mozilla::layers::LayersBackend 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 { + +union MaybeTransform { + Matrix4x4; + void_t; +}; + +sync protocol PLayerTransaction { + manager PCompositorBridge; + manages PLayer; + manages PCompositable; + +parent: + async PLayer(); + async PCompositable(TextureInfo aTextureInfo); + + // The isFirstPaint flag can be used to indicate that this is the first update + // for a particular document. + sync Update(Edit[] cset, OpDestroy[] toDestroy, + uint64_t fwdTransactionId, + uint64_t id, TargetConfig targetConfig, + PluginWindowData[] plugins, bool isFirstPaint, + bool scheduleComposite, uint32_t paintSequenceNumber, + bool isRepeatTransaction, TimeStamp transactionStart, + int32_t paintSyncId) + returns (EditReply[] reply); + + async PaintTime(uint64_t id, TimeDuration paintTime); + + // We don't need to send a sync transaction if + // no transaction operate require a swap. + async UpdateNoSwap(Edit[] cset, OpDestroy[] toDestroy, + uint64_t fwdTransactionId, + uint64_t id, TargetConfig targetConfig, + PluginWindowData[] plugins, bool isFirstPaint, + bool scheduleComposite, uint32_t paintSequenceNumber, + bool isRepeatTransaction, TimeStamp transactionStart, + int32_t paintSyncId); + + async SetLayerObserverEpoch(uint64_t layerObserverEpoch); + + // Testing APIs + + // Enter test mode, set the sample time to sampleTime, and resample + // animations. sampleTime must not be null. + sync SetTestSampleTime(TimeStamp sampleTime); + // Leave test mode and resume normal compositing + sync LeaveTestMode(); + + // Returns the value of the opacity applied to the layer by animation. + // |hasAnimationOpacity| is true if the layer has an opacity value + // specified by animation. If it's false, |opacity| value is indefinite. + sync GetAnimationOpacity(PLayer layer) returns (float opacity, + bool hasAnimationOpacity); + + // Returns the value of the transform applied to the layer by animation after + // factoring out translation components introduced to account for the offset + // of the corresponding frame and transform origin and after converting to CSS + // pixels. If the layer is not transformed by animation, the return value will + // be void_t. + sync GetAnimationTransform(PLayer layer) returns (MaybeTransform 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 ForceComposite(); + + // Get a copy of the compositor-side APZ test data instance for this + // layers id. + sync GetAPZTestData() returns (APZTestData data); + + // Query a named property from the last frame + sync RequestProperty(nsString property) returns (float value); + + // Tell the compositor to notify APZ that a layer has been confirmed for an + // input event. + async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets); + + async Shutdown(); +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 000000000..bccff8627 --- /dev/null +++ b/gfx/layers/ipc/PTexture.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/. */ + +include LayersSurfaces; +include protocol PLayerTransaction; +include protocol PCompositorBridge; +include protocol PImageBridge; +include protocol PVRManager; +include protocol PVideoBridge; +include "mozilla/GfxMessageUtils.h"; + +using struct mozilla::layers::FrameMetrics from "FrameMetrics.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 PVRManager or PVideoBridge; + +child: + async __delete__(); + +parent: + /** + * Asynchronously tell the compositor side to remove the texture. + */ + async Destroy(); + + /** + * Synchronously tell the compositor side to remove the texture. + */ + sync DestroySync(); + + async RecycleTexture(TextureFlags aTextureFlags); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PVideoBridge.ipdl b/gfx/layers/ipc/PVideoBridge.ipdl new file mode 100644 index 000000000..3fca7ac75 --- /dev/null +++ b/gfx/layers/ipc/PVideoBridge.ipdl @@ -0,0 +1,31 @@ +/* -*- 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"; +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, LayersBackend aBackend, + TextureFlags aTextureFlags, uint64_t aSerial); +}; + +} // namespace +} // namespace + diff --git a/gfx/layers/ipc/RemoteContentController.cpp b/gfx/layers/ipc/RemoteContentController.cpp new file mode 100644 index 000000000..54a08eed3 --- /dev/null +++ b/gfx/layers/ipc/RemoteContentController.cpp @@ -0,0 +1,272 @@ +/* -*- 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/RemoteContentController.h" + +#include "base/message_loop.h" +#include "base/task.h" +#include "MainThreadUtils.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/Unused.h" +#include "Units.h" +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#endif + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +RemoteContentController::RemoteContentController() + : mCompositorThread(MessageLoop::current()) + , mCanSend(true) +{ +} + +RemoteContentController::~RemoteContentController() +{ +} + +void +RemoteContentController::RequestContentRepaint(const FrameMetrics& aFrameMetrics) +{ + MOZ_ASSERT(IsRepaintThread()); + + if (mCanSend) { + Unused << SendRequestContentRepaint(aFrameMetrics); + } +} + +void +RemoteContentController::HandleTapOnMainThread(TapType aTapType, + LayoutDevicePoint aPoint, + Modifiers aModifiers, + ScrollableLayerGuid aGuid, + uint64_t aInputBlockId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + dom::TabParent* tab = dom::TabParent::GetTabParentFromLayersId(aGuid.mLayersId); + if (tab) { + tab->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, aInputBlockId); + } +} + +void +RemoteContentController::HandleTap(TapType aTapType, + const LayoutDevicePoint& aPoint, + Modifiers aModifiers, + const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId) +{ + APZThreadUtils::AssertOnControllerThread(); + + if (XRE_GetProcessType() == GeckoProcessType_GPU) { + MOZ_ASSERT(MessageLoop::current() == mCompositorThread); + + // 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); + } + + return; + } + + MOZ_ASSERT(XRE_IsParentProcess()); + + if (NS_IsMainThread()) { + HandleTapOnMainThread(aTapType, aPoint, aModifiers, aGuid, aInputBlockId); + } else { + // We don't want to get the TabParent or call TabParent::SendHandleTap() from a non-main thread (this might happen + // on Android, where this is called from the Java UI thread) + NS_DispatchToMainThread(NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers, ScrollableLayerGuid, uint64_t> + (this, &RemoteContentController::HandleTapOnMainThread, aTapType, aPoint, aModifiers, aGuid, aInputBlockId)); + } +} + +void +RemoteContentController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType, + const ScrollableLayerGuid& aGuid, + 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()) { + MOZ_ASSERT(MessageLoop::current() == mCompositorThread); + + // 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, 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, aSpanChange, aModifiers); + } + } +} + +void +RemoteContentController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) +{ +#ifdef MOZ_WIDGET_ANDROID + AndroidBridge::Bridge()->PostTaskToUiThread(Move(aTask), aDelayMs); +#else + (MessageLoop::current() ? MessageLoop::current() : mCompositorThread)-> + PostDelayedTask(Move(aTask), aDelayMs); +#endif +} + +bool +RemoteContentController::IsRepaintThread() +{ + return MessageLoop::current() == mCompositorThread; +} + +void +RemoteContentController::DispatchToRepaintThread(already_AddRefed<Runnable> aTask) +{ + mCompositorThread->PostTask(Move(aTask)); +} + +void +RemoteContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, + APZStateChange aChange, + int aArg) +{ + if (MessageLoop::current() != mCompositorThread) { + // We have to send messages from the compositor thread + mCompositorThread->PostTask(NewRunnableMethod<ScrollableLayerGuid, + APZStateChange, + int>(this, + &RemoteContentController::NotifyAPZStateChange, + aGuid, aChange, aArg)); + return; + } + + if (mCanSend) { + Unused << SendNotifyAPZStateChange(aGuid, aChange, aArg); + } +} + +void +RemoteContentController::UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) +{ + if (MessageLoop::current() != mCompositorThread) { + mCompositorThread->PostTask(NewRunnableMethod<float, + float, bool>(this, + &RemoteContentController::UpdateOverscrollVelocity, + aX, aY, aIsRootContent)); + return; + } + if (mCanSend) { + Unused << SendUpdateOverscrollVelocity(aX, aY, aIsRootContent); + } +} + +void +RemoteContentController::UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) +{ + if (MessageLoop::current() != mCompositorThread) { + mCompositorThread->PostTask(NewRunnableMethod<float, + float, bool>(this, + &RemoteContentController::UpdateOverscrollOffset, + aX, aY, aIsRootContent)); + return; + } + if (mCanSend) { + Unused << SendUpdateOverscrollOffset(aX, aY, aIsRootContent); + } +} + +void +RemoteContentController::SetScrollingRootContent(bool aIsRootContent) +{ + if (MessageLoop::current() != mCompositorThread) { + mCompositorThread->PostTask(NewRunnableMethod<bool>(this, + &RemoteContentController::SetScrollingRootContent, + aIsRootContent)); + return; + } + if (mCanSend) { + Unused << SendSetScrollingRootContent(aIsRootContent); + } +} + +void +RemoteContentController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, + const nsString& aEvent) +{ + if (MessageLoop::current() != mCompositorThread) { + // We have to send messages from the compositor thread + mCompositorThread->PostTask(NewRunnableMethod<FrameMetrics::ViewID, + nsString>(this, + &RemoteContentController::NotifyMozMouseScrollEvent, + aScrollId, aEvent)); + return; + } + + if (mCanSend) { + Unused << SendNotifyMozMouseScrollEvent(aScrollId, aEvent); + } +} + +void +RemoteContentController::NotifyFlushComplete() +{ + MOZ_ASSERT(IsRepaintThread()); + + if (mCanSend) { + Unused << SendNotifyFlushComplete(); + } +} + +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(); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/RemoteContentController.h b/gfx/layers/ipc/RemoteContentController.h new file mode 100644 index 000000000..d015ada4f --- /dev/null +++ b/gfx/layers/ipc/RemoteContentController.h @@ -0,0 +1,96 @@ +/* -*- 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/. */ + +#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 TabParent; +} + +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::TapType; + using GeckoContentController::APZStateChange; + +public: + RemoteContentController(); + + virtual ~RemoteContentController(); + + virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override; + + virtual void HandleTap(TapType aTapType, + const LayoutDevicePoint& aPoint, + Modifiers aModifiers, + const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId) override; + + virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType, + const ScrollableLayerGuid& aGuid, + LayoutDeviceCoord aSpanChange, + Modifiers aModifiers) override; + + virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override; + + virtual bool IsRepaintThread() override; + + virtual void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override; + + virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, + APZStateChange aChange, + int aArg) override; + + virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) override; + + virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) override; + + virtual void SetScrollingRootContent(bool aIsRootContent) override; + + virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, + const nsString& aEvent) override; + + virtual void NotifyFlushComplete() override; + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + virtual void Destroy() override; + +private: + MessageLoop* mCompositorThread; + bool mCanSend; + + void HandleTapOnMainThread(TapType aType, + LayoutDevicePoint aPoint, + Modifiers aModifiers, + ScrollableLayerGuid aGuid, + uint64_t aInputBlockId); +}; + +} // namespace layers + +} // namespace mozilla + +#endif // mozilla_layers_RemoteContentController_h diff --git a/gfx/layers/ipc/ShadowLayerChild.cpp b/gfx/layers/ipc/ShadowLayerChild.cpp new file mode 100644 index 000000000..62d260838 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerChild.cpp @@ -0,0 +1,45 @@ +/* -*- 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 "ShadowLayerChild.h" +#include "Layers.h" // for Layer +#include "ShadowLayers.h" // for ShadowableLayer + +namespace mozilla { +namespace layers { + +ShadowLayerChild::ShadowLayerChild() + : mLayer(nullptr) +{ } + +ShadowLayerChild::~ShadowLayerChild() +{ } + +void +ShadowLayerChild::SetShadowableLayer(ShadowableLayer* aLayer) +{ + MOZ_ASSERT(!mLayer); + mLayer = aLayer; +} + +void +ShadowLayerChild::ActorDestroy(ActorDestroyReason why) +{ + MOZ_ASSERT(AncestorDeletion != why, + "shadowable layer should have been cleaned up by now"); + + if (AbnormalShutdown == why && mLayer) { + // This is last-ditch emergency shutdown. Just have the layer + // forget its IPDL resources; IPDL-generated code will clean up + // automatically in this case. + mLayer->AsLayer()->Disconnect(); + mLayer = nullptr; + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ShadowLayerChild.h b/gfx/layers/ipc/ShadowLayerChild.h new file mode 100644 index 000000000..38a7f9500 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerChild.h @@ -0,0 +1,39 @@ +/* -*- 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/. */ + +#ifndef mozilla_layers_ShadowLayerChild_h +#define mozilla_layers_ShadowLayerChild_h + +#include "mozilla/Attributes.h" // for override +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/layers/PLayerChild.h" // for PLayerChild + +namespace mozilla { +namespace layers { + +class ShadowableLayer; + +class ShadowLayerChild : public PLayerChild +{ +public: + ShadowLayerChild(); + virtual ~ShadowLayerChild(); + + void SetShadowableLayer(ShadowableLayer* aLayer); + ShadowableLayer* layer() const { return mLayer; } + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override; + +private: + ShadowableLayer* mLayer; +}; + +} // namespace layers +} // namespace mozilla + +#endif // ifndef mozilla_layers_ShadowLayerChild_h diff --git a/gfx/layers/ipc/ShadowLayerParent.cpp b/gfx/layers/ipc/ShadowLayerParent.cpp new file mode 100644 index 000000000..e0898715d --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerParent.cpp @@ -0,0 +1,138 @@ +/* -*- 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 "ShadowLayerParent.h" +#include "Layers.h" // for Layer, ContainerLayer +#include "nsDebug.h" // for NS_RUNTIMEABORT +#include "nsISupportsImpl.h" // for Layer::AddRef, etc + +#include "mozilla/layers/PaintedLayerComposite.h" +#include "mozilla/layers/CanvasLayerComposite.h" +#include "mozilla/layers/ColorLayerComposite.h" +#include "mozilla/layers/ImageLayerComposite.h" +#include "mozilla/layers/ContainerLayerComposite.h" + +namespace mozilla { +namespace layers { + +ShadowLayerParent::ShadowLayerParent() : mLayer(nullptr) +{ +} + +ShadowLayerParent::~ShadowLayerParent() +{ + Disconnect(); +} + +void +ShadowLayerParent::Disconnect() +{ + if (mLayer) { + mLayer->Disconnect(); + mLayer = nullptr; + } +} + +void +ShadowLayerParent::Bind(Layer* layer) +{ + if (mLayer != layer) { + Disconnect(); + mLayer = layer; + } +} + +void +ShadowLayerParent::Destroy() +{ + // It's possible for Destroy() to come in just after this has been + // created, but just before the transaction in which Bind() would + // have been called. In that case, we'll ignore shadow-layers + // transactions from there on and never get a layer here. + Disconnect(); +} + +ContainerLayerComposite* +ShadowLayerParent::AsContainerLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_CONTAINER + ? static_cast<ContainerLayerComposite*>(mLayer.get()) + : nullptr; +} + +CanvasLayerComposite* +ShadowLayerParent::AsCanvasLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_CANVAS + ? static_cast<CanvasLayerComposite*>(mLayer.get()) + : nullptr; +} + +ColorLayerComposite* +ShadowLayerParent::AsColorLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_COLOR + ? static_cast<ColorLayerComposite*>(mLayer.get()) + : nullptr; +} + +ImageLayerComposite* +ShadowLayerParent::AsImageLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_IMAGE + ? static_cast<ImageLayerComposite*>(mLayer.get()) + : nullptr; +} + +RefLayerComposite* +ShadowLayerParent::AsRefLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_REF + ? static_cast<RefLayerComposite*>(mLayer.get()) + : nullptr; +} + +PaintedLayerComposite* +ShadowLayerParent::AsPaintedLayerComposite() const +{ + return mLayer && mLayer->GetType() == Layer::TYPE_PAINTED + ? static_cast<PaintedLayerComposite*>(mLayer.get()) + : nullptr; +} + +void +ShadowLayerParent::ActorDestroy(ActorDestroyReason why) +{ + switch (why) { + case AncestorDeletion: + NS_RUNTIMEABORT("shadow layer deleted out of order!"); + return; // unreached + + case Deletion: + // See comment near Destroy() above. + Disconnect(); + break; + + case AbnormalShutdown: + Disconnect(); + break; + + case NormalShutdown: + // let IPDL-generated code automatically clean up Shmems and so + // forth; our channel is disconnected anyway + break; + + case FailedConstructor: + NS_RUNTIMEABORT("FailedConstructor isn't possible in PLayerTransaction"); + return; // unreached + } + + mLayer = nullptr; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ShadowLayerParent.h b/gfx/layers/ipc/ShadowLayerParent.h new file mode 100644 index 000000000..d40cf34ff --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerParent.h @@ -0,0 +1,58 @@ +/* -*- 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/. */ + +#ifndef mozilla_layers_ShadowLayerParent_h +#define mozilla_layers_ShadowLayerParent_h + +#include "mozilla/Attributes.h" // for override +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/layers/PLayerParent.h" // for PLayerParent + +namespace mozilla { +namespace layers { + +class ContainerLayer; +class Layer; + +class CanvasLayerComposite; +class ColorLayerComposite; +class ContainerLayerComposite; +class ImageLayerComposite; +class RefLayerComposite; +class PaintedLayerComposite; + +class ShadowLayerParent : public PLayerParent +{ +public: + ShadowLayerParent(); + + virtual ~ShadowLayerParent(); + + void Bind(Layer* layer); + void Destroy(); + + Layer* AsLayer() const { return mLayer; } + + ContainerLayerComposite* AsContainerLayerComposite() const; + CanvasLayerComposite* AsCanvasLayerComposite() const; + ColorLayerComposite* AsColorLayerComposite() const; + ImageLayerComposite* AsImageLayerComposite() const; + RefLayerComposite* AsRefLayerComposite() const; + PaintedLayerComposite* AsPaintedLayerComposite() const; + +private: + virtual void ActorDestroy(ActorDestroyReason why) override; + + void Disconnect(); + + RefPtr<Layer> mLayer; +}; + +} // namespace layers +} // namespace mozilla + +#endif // ifndef mozilla_layers_ShadowLayerParent_h diff --git a/gfx/layers/ipc/ShadowLayerUtils.h b/gfx/layers/ipc/ShadowLayerUtils.h new file mode 100644 index 000000000..257cee81d --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerUtils.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef IPC_ShadowLayerUtils_h +#define IPC_ShadowLayerUtils_h + +#include "ipc/IPCMessageUtils.h" +#include "GLContextTypes.h" +#include "SurfaceTypes.h" +#include "mozilla/WidgetUtils.h" + +#if defined(XP_MACOSX) +#define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS +#endif + +#if defined(MOZ_X11) +# include "mozilla/layers/ShadowLayerUtilsX11.h" +#else +namespace mozilla { namespace layers { +struct SurfaceDescriptorX11 { + bool operator==(const SurfaceDescriptorX11&) const { return false; } +}; +} // namespace layers +} // namespace mozilla +#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 000000000..cc5199ea8 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerUtilsMac.cpp @@ -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 "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 000000000..6b9660054 --- /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: 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 "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/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 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; + } + +#ifdef GL_PROVIDER_GLX + if (aForwardGLX) { + mGLXPixmap = aSurf->GetGLXPixmap(); + } +#endif +} + +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(); + 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); + } + +#ifdef GL_PROVIDER_GLX + if (mGLXPixmap) + surf->BindGLXPixmap(mGLXPixmap); +#endif + + 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 diff --git a/gfx/layers/ipc/ShadowLayerUtilsX11.h b/gfx/layers/ipc/ShadowLayerUtilsX11.h new file mode 100644 index 000000000..bf64b09ff --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerUtilsX11.h @@ -0,0 +1,85 @@ +/* -*- 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/. */ + +#ifndef mozilla_layers_ShadowLayerUtilsX11_h +#define mozilla_layers_ShadowLayerUtilsX11_h + +#include "ipc/IPCMessageUtils.h" +#include "mozilla/GfxMessageUtils.h" +#include "nsCOMPtr.h" // for already_AddRefed + +#define MOZ_HAVE_SURFACEDESCRIPTORX11 +#define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS + +typedef unsigned long XID; +typedef XID Drawable; + +class gfxXlibSurface; + +namespace IPC { +class Message; +} + +namespace mozilla { +namespace layers { + +struct SurfaceDescriptorX11 { + SurfaceDescriptorX11() + { } + + 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 + +namespace IPC { + +template <> +struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> { + typedef mozilla::layers::SurfaceDescriptorX11 paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mId); + WriteParam(aMsg, aParam.mSize); + WriteParam(aMsg, aParam.mFormat); + WriteParam(aMsg, aParam.mGLXPixmap); + } + + static bool 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 + +#endif // mozilla_layers_ShadowLayerUtilsX11_h diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp new file mode 100644 index 000000000..88b88bde0 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -0,0 +1,1044 @@ +/* -*- 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 "ClientLayerManager.h" // for ClientLayerManager +#include "ShadowLayers.h" +#include <set> // for _Rb_tree_const_iterator, etc +#include <vector> // for vector +#include "GeckoProfiler.h" // for PROFILER_LABEL +#include "ISurfaceAllocator.h" // for IsSurfaceDescriptorValid +#include "Layers.h" // for Layer +#include "RenderTrace.h" // for RenderTraceScope +#include "ShadowLayerChild.h" // for ShadowLayerChild +#include "gfx2DGlue.h" // for Moz2D transition helpers +#include "gfxPlatform.h" // for gfxImageFormat, gfxPlatform +//#include "gfxSharedImageSurface.h" // for gfxSharedImageSurface +#include "ipc/IPCMessageUtils.h" // for gfxContentType, null_t +#include "IPDLActor.h" +#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/ImageDataSerializer.h" +#include "mozilla/layers/ImageBridgeChild.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/LayerTransactionChild.h" +#include "mozilla/layers/PCompositableChild.h" +#include "mozilla/layers/PTextureChild.h" +#include "ShadowLayerUtils.h" +#include "mozilla/layers/TextureClient.h" // for TextureClient +#include "mozilla/mozalloc.h" // for operator new, etc +#include "nsTArray.h" // for AutoTArray, nsTArray, etc +#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc +#include "mozilla/ReentrantMonitor.h" + +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 std::vector<Edit> EditVector; +typedef nsTHashtable<nsPtrHashKey<ShadowableLayer>> ShadowableLayerSet; +typedef nsTArray<OpDestroy> OpDestroyVector; + +class Transaction +{ +public: + Transaction() + : mTargetRotation(ROTATION_0) + , mSwapRequired(false) + , mOpen(false) + , mRotationChanged(false) + {} + + void Begin(const gfx::IntRect& aTargetBounds, ScreenRotation aRotation, + dom::ScreenOrientationInternal 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 MarkSyncTransaction() + { + mSwapRequired = true; + } + void AddEdit(const Edit& aEdit) + { + MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); + mCset.push_back(aEdit); + } + void AddEdit(const CompositableOperation& aEdit) + { + AddEdit(Edit(aEdit)); + } + void AddPaint(const Edit& aPaint) + { + AddNoSwapPaint(aPaint); + mSwapRequired = true; + } + void AddPaint(const CompositableOperation& aPaint) + { + AddNoSwapPaint(Edit(aPaint)); + mSwapRequired = true; + } + + void AddNoSwapPaint(const Edit& aPaint) + { + MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); + mPaints.push_back(aPaint); + } + void AddNoSwapPaint(const CompositableOperation& aPaint) + { + MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); + mPaints.push_back(Edit(aPaint)); + } + void AddMutant(ShadowableLayer* aLayer) + { + MOZ_ASSERT(!Finished(), "forgot BeginTransaction?"); + mMutants.PutEntry(aLayer); + } + void End() + { + mCset.clear(); + mPaints.clear(); + mMutants.Clear(); + mDestroyedActors.Clear(); + mOpen = false; + mSwapRequired = false; + mRotationChanged = false; + } + + bool Empty() const { + return mCset.empty() && mPaints.empty() && mMutants.IsEmpty() + && mDestroyedActors.IsEmpty(); + } + bool RotationChanged() const { + return mRotationChanged; + } + bool Finished() const { return !mOpen && Empty(); } + + bool Opened() const { return mOpen; } + + EditVector mCset; + EditVector mPaints; + OpDestroyVector mDestroyedActors; + ShadowableLayerSet mMutants; + gfx::IntRect mTargetBounds; + ScreenRotation mTargetRotation; + dom::ScreenOrientationInternal mTargetOrientation; + bool mSwapRequired; + +private: + bool mOpen; + bool mRotationChanged; + + // disabled + Transaction(const Transaction&); + Transaction& operator=(const Transaction&); +}; +struct AutoTxnEnd { + explicit AutoTxnEnd(Transaction* aTxn) : mTxn(aTxn) {} + ~AutoTxnEnd() { mTxn->End(); } + Transaction* mTxn; +}; + +void +KnowsCompositor::IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier) +{ + mTextureFactoryIdentifier = aIdentifier; + + mSyncObject = SyncObject::CreateSyncObject(aIdentifier.mSyncHandle); +} + +KnowsCompositor::KnowsCompositor() + : mSerial(++sSerialCounter) +{} + +KnowsCompositor::~KnowsCompositor() +{} + +ShadowLayerForwarder::ShadowLayerForwarder(ClientLayerManager* aClientLayerManager) + : mClientLayerManager(aClientLayerManager) + , mMessageLoop(MessageLoop::current()) + , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC) + , mIsFirstPaint(false) + , mWindowOverlayChanged(false) + , mPaintSyncId(0) +{ + mTxn = new Transaction(); + mActiveResourceTracker = MakeUnique<ActiveResourceTracker>(1000, "CompositableForwarder"); +} + +template<typename T> +struct ReleaseOnMainThreadTask : public Runnable +{ + UniquePtr<T> mObj; + + explicit ReleaseOnMainThreadTask(UniquePtr<T>& aObj) + : mObj(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); + mShadowManager->Destroy(); + } + if (!NS_IsMainThread()) { + NS_DispatchToMainThread( + new ReleaseOnMainThreadTask<ActiveResourceTracker>(mActiveResourceTracker)); + } +} + +void +ShadowLayerForwarder::BeginTransaction(const gfx::IntRect& aTargetBounds, + ScreenRotation aRotation, + dom::ScreenOrientationInternal aOrientation) +{ + MOZ_ASSERT(IPCOpen(), "no manager to forward to"); + MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?"); + UpdateFwdTransactionId(); + mTxn->Begin(aTargetBounds, aRotation, aOrientation); +} + +static PLayerChild* +Shadow(ShadowableLayer* aLayer) +{ + return aLayer->GetShadow(); +} + +template<typename OpCreateT> +static void +CreatedLayer(Transaction* aTxn, ShadowableLayer* aLayer) +{ + aTxn->AddEdit(OpCreateT(nullptr, 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::SetRoot(ShadowableLayer* aRoot) +{ + mTxn->AddEdit(OpSetRoot(nullptr, 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(nullptr, Shadow(aContainer), + nullptr, Shadow(aChild), + nullptr, Shadow(aAfter))); + } else { + mTxn->AddEdit(OpPrependChild(nullptr, Shadow(aContainer), + nullptr, 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(nullptr, Shadow(aContainer), + nullptr, 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(nullptr, Shadow(aContainer), + nullptr, Shadow(aChild), + nullptr, Shadow(aAfter))); + } else { + MOZ_LAYERS_LOG(("[LayersForwarder] OpRaiseToTopChild container=%p child=%p", + aContainer->AsLayer(), aChild->AsLayer())); + mTxn->AddEdit(OpRaiseToTopChild(nullptr, Shadow(aContainer), + nullptr, 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(nullptr, aCompositable->GetIPDLActor(), + OpUseTiledLayerBuffer(aTileLayerDescriptor))); +} + +void +ShadowLayerForwarder::UpdateTextureRegion(CompositableClient* aCompositable, + const ThebesBufferData& aThebesBufferData, + const nsIntRegion& aUpdatedRegion) +{ + MOZ_ASSERT(aCompositable); + + if (!aCompositable->IsConnected()) { + return; + } + + mTxn->AddPaint( + CompositableOperation( + nullptr, aCompositable->GetIPDLActor(), + 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()); + ReadLockDescriptor readLock; + t.mTextureClient->SerializeReadLock(readLock); + textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), + readLock, + t.mTimeStamp, t.mPictureRect, + t.mFrameID, t.mProducerID)); + if ((t.mTextureClient->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD) + && t.mTextureClient->HasIntermediateBuffer()) { + + // We use IMMEDIATE_UPLOAD when we want to be sure that the upload cannot + // race with updates on the main thread. In this case we want the transaction + // to be synchronous. + mTxn->MarkSyncTransaction(); + } + mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient); + } + mTxn->AddEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(), + 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->GetIPDLActor()); + 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()); + + ReadLockDescriptor readLockW; + ReadLockDescriptor readLockB; + aTextureOnBlack->SerializeReadLock(readLockB); + aTextureOnWhite->SerializeReadLock(readLockW); + + mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack); + mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite); + + mTxn->AddEdit( + CompositableOperation( + nullptr, aCompositable->GetIPDLActor(), + OpUseComponentAlphaTextures( + nullptr, aTextureOnBlack->GetIPDLActor(), + nullptr, aTextureOnWhite->GetIPDLActor(), + readLockB, readLockW) + ) + ); +} + +static bool +AddOpDestroy(Transaction* aTxn, const OpDestroy& op, bool synchronously) +{ + if (!aTxn->Opened()) { + return false; + } + + aTxn->mDestroyedActors.AppendElement(op); + if (synchronously) { + aTxn->MarkSyncTransaction(); + } + + return true; +} + +bool +ShadowLayerForwarder::DestroyInTransaction(PTextureChild* aTexture, bool synchronously) +{ + return AddOpDestroy(mTxn, OpDestroy(aTexture), synchronously); +} + +bool +ShadowLayerForwarder::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) +{ + return AddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously); +} + +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( + nullptr, aCompositable->GetIPDLActor(), + OpRemoveTexture(nullptr, aTexture->GetIPDLActor()))); + if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) { + mTxn->MarkSyncTransaction(); + } +} + +bool +ShadowLayerForwarder::InWorkerThread() +{ + return MessageLoop::current() && (GetTextureForwarder()->GetMessageLoop()->id() == MessageLoop::current()->id()); +} + +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(uint64_t aId, TimeDuration aPaintTime) +{ + if (!IPCOpen() || + !mShadowManager->SendPaintTime(aId, aPaintTime)) { + NS_WARNING("Could not send paint times over IPC"); + } +} + +bool +ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies, + const nsIntRegion& aRegionToClear, + uint64_t aId, + bool aScheduleComposite, + uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + const mozilla::TimeStamp& aTransactionStart, + bool* aSent) +{ + *aSent = false; + + MOZ_ASSERT(IPCOpen(), "no manager to forward to"); + if (!IPCOpen()) { + return false; + } + + GetCompositorBridgeChild()->WillEndTransaction(); + + MOZ_ASSERT(aId); + + PROFILER_LABEL("ShadowLayerForwarder", "EndTransaction", + js::ProfileEntry::Category::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)); + } + if (mWindowOverlayChanged) { + mTxn->AddEdit(OpWindowOverlayChanged()); + } + + 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.empty()) { + // 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...")); + + // 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?"); + + LayerAttributes attrs; + CommonLayerAttributes& common = attrs.common(); + common.layerBounds() = mutant->GetLayerBounds(); + common.visibleRegion() = mutant->GetVisibleRegion(); + common.eventRegions() = mutant->GetEventRegions(); + common.postXScale() = mutant->GetPostXScale(); + common.postYScale() = mutant->GetPostYScale(); + common.transform() = mutant->GetBaseTransform(); + common.transformIsPerspective() = mutant->GetTransformIsPerspective(); + common.contentFlags() = mutant->GetContentFlags(); + common.opacity() = mutant->GetOpacity(); + common.useClipRect() = !!mutant->GetClipRect(); + common.clipRect() = (common.useClipRect() ? + *mutant->GetClipRect() : ParentLayerIntRect()); + common.scrolledClip() = mutant->GetScrolledClip(); + common.isFixedPosition() = mutant->GetIsFixedPosition(); + if (mutant->GetIsFixedPosition()) { + common.fixedPositionScrollContainerId() = mutant->GetFixedPositionScrollContainerId(); + common.fixedPositionAnchor() = mutant->GetFixedPositionAnchor(); + common.fixedPositionSides() = mutant->GetFixedPositionSides(); + } + common.isStickyPosition() = mutant->GetIsStickyPosition(); + if (mutant->GetIsStickyPosition()) { + common.stickyScrollContainerId() = mutant->GetStickyScrollContainerId(); + common.stickyScrollRangeOuter() = mutant->GetStickyScrollRangeOuter(); + common.stickyScrollRangeInner() = mutant->GetStickyScrollRangeInner(); + } else { +#ifdef MOZ_VALGRIND + // Initialize these so that Valgrind doesn't complain when we send them + // to another process. + common.stickyScrollContainerId() = 0; + common.stickyScrollRangeOuter() = LayerRect(); + common.stickyScrollRangeInner() = LayerRect(); +#endif + } + common.scrollbarTargetContainerId() = mutant->GetScrollbarTargetContainerId(); + common.scrollbarDirection() = mutant->GetScrollbarDirection(); + common.scrollbarThumbRatio() = mutant->GetScrollbarThumbRatio(); + common.isScrollbarContainer() = mutant->IsScrollbarContainer(); + common.mixBlendMode() = (int8_t)mutant->GetMixBlendMode(); + common.forceIsolatedGroup() = mutant->GetForceIsolatedGroup(); + if (Layer* maskLayer = mutant->GetMaskLayer()) { + common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer()); + } else { + common.maskLayerChild() = nullptr; + } + common.maskLayerParent() = nullptr; + common.animations() = mutant->GetAnimations(); + common.invalidRegion() = mutant->GetInvalidRegion().GetRegion(); + common.scrollMetadata() = mutant->GetAllScrollMetadata(); + for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) { + auto layer = Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer()); + common.ancestorMaskLayersChild().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)); + + mTxn->AddEdit(OpSetLayerAttributes(nullptr, Shadow(shadow), attrs)); + } + + AutoTArray<Edit, 10> cset; + size_t nCsets = mTxn->mCset.size() + mTxn->mPaints.size(); + if (nCsets == 0 && !mTxn->RotationChanged()) { + return true; + } + + cset.SetCapacity(nCsets); + if (!mTxn->mCset.empty()) { + cset.AppendElements(&mTxn->mCset.front(), mTxn->mCset.size()); + } + // Paints after non-paint ops, including attribute changes. See + // above. + if (!mTxn->mPaints.empty()) { + cset.AppendElements(&mTxn->mPaints.front(), mTxn->mPaints.size()); + } + + mWindowOverlayChanged = false; + + TargetConfig targetConfig(mTxn->mTargetBounds, + mTxn->mTargetRotation, + mTxn->mTargetOrientation, + aRegionToClear); + + if (!GetTextureForwarder()->IsSameProcess()) { + MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send...")); + PlatformSyncBeforeUpdate(); + } + + profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_END); + if (mTxn->mSwapRequired) { + MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction...")); + RenderTraceScope rendertrace3("Forward Transaction", "000093"); + if (!mShadowManager->SendUpdate(cset, mTxn->mDestroyedActors, + GetFwdTransactionId(), + aId, targetConfig, mPluginWindowData, + mIsFirstPaint, aScheduleComposite, + aPaintSequenceNumber, aIsRepeatTransaction, + aTransactionStart, mPaintSyncId, aReplies)) { + MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!")); + return false; + } + } else { + // If we don't require a swap we can call SendUpdateNoSwap which + // assumes that aReplies is empty (DEBUG assertion) + MOZ_LAYERS_LOG(("[LayersForwarder] sending no swap transaction...")); + RenderTraceScope rendertrace3("Forward NoSwap Transaction", "000093"); + if (!mShadowManager->SendUpdateNoSwap(cset, mTxn->mDestroyedActors, + GetFwdTransactionId(), + aId, targetConfig, mPluginWindowData, + mIsFirstPaint, aScheduleComposite, + aPaintSequenceNumber, aIsRepeatTransaction, + aTransactionStart, mPaintSyncId)) { + MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!")); + return false; + } + } + + *aSent = true; + mIsFirstPaint = false; + mPaintSyncId = 0; + MOZ_LAYERS_LOG(("[LayersForwarder] ... done")); + return true; +} + +void +ShadowLayerForwarder::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch) +{ + if (!IPCOpen()) { + return; + } + Unused << mShadowManager->SendSetLayerObserverEpoch(aLayerObserverEpoch); +} + +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. + */ +PLayerChild* +ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer) +{ + MOZ_ASSERT(IPCOpen(), "no manager to forward to"); + if (!IPCOpen()) { + return nullptr; + } + + ShadowLayerChild* child = new ShadowLayerChild(); + if (!mShadowManager->SendPLayerConstructor(child)) { + return nullptr; + } + + child->SetShadowableLayer(aLayer); + return child; +} + +#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; + } + PCompositableChild* actor = + mShadowManager->SendPCompositableConstructor(aCompositable->GetTextureInfo()); + if (!actor) { + return; + } + aCompositable->InitIPDLActor(actor); +} + +void ShadowLayerForwarder::Attach(CompositableClient* aCompositable, + ShadowableLayer* aLayer) +{ + MOZ_ASSERT(aLayer); + MOZ_ASSERT(aCompositable); + mTxn->AddEdit(OpAttachCompositable(nullptr, Shadow(aLayer), + nullptr, aCompositable->GetIPDLActor())); +} + +void ShadowLayerForwarder::AttachAsyncCompositable(uint64_t aCompositableID, + ShadowableLayer* aLayer) +{ + MOZ_ASSERT(aLayer); + MOZ_ASSERT(aCompositableID != 0); // zero is always an invalid compositable id. + mTxn->AddEdit(OpAttachAsyncCompositable(nullptr, Shadow(aLayer), + aCompositableID)); +} + +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::Composite() +{ + if (!IPCOpen()) { + return; + } + mShadowManager->SendForceComposite(); +} + +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: + NS_RUNTIMEABORT("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 = 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; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h new file mode 100644 index 000000000..8b207eb1a --- /dev/null +++ b/gfx/layers/ipc/ShadowLayers.h @@ -0,0 +1,465 @@ +/* -*- 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/. */ + +#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/dom/ScreenOrientation.h" // for ScreenOrientation +#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc +#include "mozilla/layers/CompositableForwarder.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 InfallibleTArray +#include "nsIWidget.h" +#include <vector> +#include "nsExpirationTracker.h" + +namespace mozilla { +namespace layers { + +class ClientLayerManager; +class CompositorBridgeChild; +class EditReply; +class FixedSizeSmallShmemSectionAllocator; +class ImageContainer; +class Layer; +class PLayerChild; +class PLayerTransactionChild; +class LayerTransactionChild; +class ShadowableLayer; +class SurfaceDescriptor; +class TextureClient; +class ThebesBuffer; +class ThebesBufferData; +class Transaction; + +/** + * 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) + : nsExpirationTracker(aExpirationCycle, aName) + {} + + virtual void NotifyExpired(ActiveResource* aResource) override + { + RemoveObject(aResource); + aResource->NotifyInactive(); + } +}; + +/** + * 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(uint64_t aCompositableID, + ShadowableLayer* aLayer); + + /** + * Begin recording a transaction to be forwarded atomically to a + * LayerManagerComposite. + */ + void BeginTransaction(const gfx::IntRect& aTargetBounds, + ScreenRotation aRotation, + mozilla::dom::ScreenOrientationInternal 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 is mutated. + */ + void Mutated(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; + + bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override; + bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override; + + virtual 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. + */ + virtual void UpdateTextureRegion(CompositableClient* aCompositable, + const ThebesBufferData& aThebesBufferData, + const nsIntRegion& aUpdatedRegion) override; + + /** + * See CompositableForwarder::UseTextures + */ + virtual void UseTextures(CompositableClient* aCompositable, + const nsTArray<TimedTextureClient>& aTextures) override; + virtual 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(uint64_t 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(InfallibleTArray<EditReply>* aReplies, + const nsIntRegion& aRegionToClear, + uint64_t aId, + bool aScheduleComposite, + uint32_t aPaintSequenceNumber, + bool aIsRepeatTransaction, + const mozilla::TimeStamp& aTransactionStart, + bool* aSent); + + /** + * 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 Composite(); + + /** + * True if this is forwarding to a LayerManagerComposite. + */ + bool HasShadowManager() const { return !!mShadowManager; } + LayerTransactionChild* GetShadowManager() const { return mShadowManager.get(); } + + virtual void WindowOverlayChanged() { mWindowOverlayChanged = true; } + + /** + * 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. + */ + + virtual bool IPCOpen() const override; + + /** + * Construct a shadow of |aLayer| on the "other side", at the + * LayerManagerComposite. + */ + PLayerChild* ConstructShadowFor(ShadowableLayer* aLayer); + + /** + * Flag the next paint as the first for a document. + */ + void SetIsFirstPaint() { mIsFirstPaint = true; } + + void SetPaintSyncId(int32_t aSyncId) { mPaintSyncId = aSyncId; } + + void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch); + + static void PlatformSyncBeforeUpdate(); + + virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize, + gfxContentType aContent, + SurfaceDescriptor* aBuffer) override; + + virtual bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize, + gfxContentType aContent, + uint32_t aCaps, + SurfaceDescriptor* aBuffer) override; + + virtual void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) override; + + virtual void UpdateFwdTransactionId() override; + virtual uint64_t GetFwdTransactionId() override; + + bool InForwarderThread() override { + return NS_IsMainThread(); + } + + // Returns true if aSurface wraps a Shmem. + static bool IsShmem(SurfaceDescriptor* aSurface); + + TextureForwarder* GetTextureForwarder() override { return GetCompositorBridgeChild(); } + LayersIPCActor* GetLayersIPCActor() override { return this; } + + ActiveResourceTracker& GetActiveResourceTracker() { return *mActiveResourceTracker.get(); } +protected: + virtual ~ShadowLayerForwarder(); + + explicit ShadowLayerForwarder(ClientLayerManager* aClientLayerManager); + +#ifdef DEBUG + void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const; +#else + void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const {} +#endif + + bool InWorkerThread(); + + CompositorBridgeChild* GetCompositorBridgeChild(); + + RefPtr<LayerTransactionChild> mShadowManager; + RefPtr<CompositorBridgeChild> mCompositorBridgeChild; + +private: + + ClientLayerManager* mClientLayerManager; + Transaction* mTxn; + MessageLoop* mMessageLoop; + DiagnosticTypes mDiagnosticTypes; + bool mIsFirstPaint; + bool mWindowOverlayChanged; + int32_t mPaintSyncId; + InfallibleTArray<PluginWindowData> mPluginWindowData; + UniquePtr<ActiveResourceTracker> mActiveResourceTracker; +}; + +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; } + + /** + * Return the IPC handle to a Shadow*Layer referring to this if one + * exists, nullptr if not. + */ + PLayerChild* GetShadow() { return mShadow; } + + virtual CompositableClient* GetCompositableClient() { return nullptr; } +protected: + ShadowableLayer() : mShadow(nullptr) {} + + PLayerChild* 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 000000000..6a1bbcac0 --- /dev/null +++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp @@ -0,0 +1,246 @@ +/* -*- 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 "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() { + MOZ_COUNT_DTOR(SharedPlanarYCbCrImage); + + if (mCompositable->GetAsyncID() != 0 && + !InImageBridgeChildThread()) { + if (mTextureClient) { + ADDREF_MANUALLY(mTextureClient); + ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient); + mTextureClient = nullptr; + } + } +} + +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* aForwarder) +{ + return mTextureClient.get(); +} + +uint8_t* +SharedPlanarYCbCrImage::GetBuffer() +{ + // This should never be used + MOZ_ASSERT(false); + return nullptr; +} + +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; +} + +// needs to be overriden because the parent class sets mBuffer which we +// do not want to happen. +uint8_t* +SharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize) +{ + MOZ_ASSERT(!mTextureClient, "This image already has allocated data"); + size_t size = ImageDataSerializer::ComputeYCbCrBufferSize(aSize); + if (!size) { + return nullptr; + } + + // XXX Add YUVColorSpace handling. Use YUVColorSpace::BT601 for now. + mTextureClient = TextureClient::CreateForYCbCrWithBufferSize(mCompositable->GetForwarder(), + size, + YUVColorSpace::BT601, + mCompositable->GetTextureFlags()); + + // get new buffer _without_ setting mBuffer. + if (!mTextureClient) { + return nullptr; + } + + // update buffer size + mBufferSize = size; + + MappedYCbCrTextureData mapped; + if (mTextureClient->BorrowMappedYCbCrData(mapped)) { + // The caller expects a pointer to the beginning of the writable part of the + // buffer which is where the y channel starts by default. + return mapped.y.data; + } else { + MOZ_CRASH("GFX: Cannot borrow mapped YCbCr data"); + } +} + +bool +SharedPlanarYCbCrImage::AdoptData(const Data &aData) +{ + // AdoptData is used to update YUV plane offsets without (re)allocating + // memory previously allocated with AllocateAndGetNewBuffer(). + + MOZ_ASSERT(mTextureClient, "This Image should have already allocated data"); + if (!mTextureClient) { + return false; + } + mData = aData; + mSize = aData.mPicSize; + mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY); + + uint8_t *base = GetBuffer(); + uint32_t yOffset = aData.mYChannel - base; + uint32_t cbOffset = aData.mCbChannel - base; + uint32_t crOffset = aData.mCrChannel - base; + + auto fwd = mCompositable->GetForwarder(); + bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV, + fwd->GetCompositorBackendType()); + + static_cast<BufferTextureData*>(mTextureClient->GetInternalData())->SetDesciptor( + YCbCrDescriptor(aData.mYSize, aData.mCbCrSize, yOffset, cbOffset, crOffset, + aData.mStereoMode, aData.mYUVColorSpace, hasIntermediateBuffer) + ); + + return true; +} + +bool +SharedPlanarYCbCrImage::IsValid() { + return mTextureClient && mTextureClient->IsValid(); +} + +bool +SharedPlanarYCbCrImage::Allocate(PlanarYCbCrData& aData) +{ + MOZ_ASSERT(!mTextureClient, + "This image already has allocated data"); + static const uint32_t MAX_POOLED_VIDEO_COUNT = 5; + + if (!mCompositable->HasTextureClientRecycler()) { + // Initialize TextureClientRecycler + mCompositable->GetTextureClientRecycler()->SetMaxPoolSize(MAX_POOLED_VIDEO_COUNT); + } + + { + YCbCrTextureClientAllocationHelper helper(aData, mCompositable->GetTextureFlags()); + mTextureClient = mCompositable->GetTextureClientRecycler()->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; + // 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 = mData.mYSize.width; + mData.mCbCrStride = mData.mCbCrSize.width; + + // 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.mCbCrSize); + 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 000000000..0c1b6e9c8 --- /dev/null +++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.h @@ -0,0 +1,60 @@ +/* -*- 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 <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 SharedPlanarYCbCrImage : public PlanarYCbCrImage +{ +public: + explicit SharedPlanarYCbCrImage(ImageClient* aCompositable); + +protected: + ~SharedPlanarYCbCrImage(); + +public: + virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override; + virtual uint8_t* GetBuffer() override; + + virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override; + virtual bool CopyData(const PlanarYCbCrData& aData) override; + virtual bool AdoptData(const Data &aData) override; + + virtual bool Allocate(PlanarYCbCrData& aData); + virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) override; + + virtual bool IsValid() override; + + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override + { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); + } + + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override; + +private: + RefPtr<TextureClient> mTextureClient; + RefPtr<ImageClient> mCompositable; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/SharedRGBImage.cpp b/gfx/layers/ipc/SharedRGBImage.cpp new file mode 100644 index 000000000..bb3bb968c --- /dev/null +++ b/gfx/layers/ipc/SharedRGBImage.cpp @@ -0,0 +1,113 @@ +/* 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/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 "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 { + +already_AddRefed<Image> +CreateSharedRGBImage(ImageContainer *aImageContainer, + gfx::IntSize aSize, + gfxImageFormat aImageFormat) +{ + NS_ASSERTION(aImageFormat == gfx::SurfaceFormat::A8R8G8B8_UINT32 || + aImageFormat == gfx::SurfaceFormat::X8R8G8B8_UINT32 || + aImageFormat == gfx::SurfaceFormat::R5G6B5_UINT16, + "RGB formats supported only"); + + if (!aImageContainer) { + NS_WARNING("No ImageContainer to allocate SharedRGBImage"); + return nullptr; + } + + RefPtr<SharedRGBImage> rgbImage = aImageContainer->CreateSharedRGBImage(); + if (!rgbImage) { + NS_WARNING("Failed to create SharedRGBImage"); + return nullptr; + } + if (!rgbImage->Allocate(aSize, gfx::ImageFormatToSurfaceFormat(aImageFormat))) { + NS_WARNING("Failed to allocate a shared image"); + return nullptr; + } + return rgbImage.forget(); +} + +SharedRGBImage::SharedRGBImage(ImageClient* aCompositable) +: Image(nullptr, ImageFormat::SHARED_RGB) +, mCompositable(aCompositable) +{ + MOZ_COUNT_CTOR(SharedRGBImage); +} + +SharedRGBImage::~SharedRGBImage() +{ + MOZ_COUNT_DTOR(SharedRGBImage); + + if (mCompositable->GetAsyncID() != 0 && + !InImageBridgeChildThread()) { + ADDREF_MANUALLY(mTextureClient); + ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient); + mTextureClient = nullptr; + } +} + +bool +SharedRGBImage::Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat) +{ + mSize = aSize; + mTextureClient = mCompositable->CreateBufferTextureClient(aFormat, aSize, + gfx::BackendType::NONE, + TextureFlags::DEFAULT); + return !!mTextureClient; +} + +uint8_t* +SharedRGBImage::GetBuffer() +{ + MappedTextureData mapped; + if (mTextureClient && mTextureClient->BorrowMappedData(mapped)) { + return mapped.data; + } + return 0; +} + +gfx::IntSize +SharedRGBImage::GetSize() +{ + return mSize; +} + +TextureClient* +SharedRGBImage::GetTextureClient(KnowsCompositor* aForwarder) +{ + return mTextureClient.get(); +} + +already_AddRefed<gfx::SourceSurface> +SharedRGBImage::GetAsSourceSurface() +{ + return nullptr; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/SharedRGBImage.h b/gfx/layers/ipc/SharedRGBImage.h new file mode 100644 index 000000000..2c6009c19 --- /dev/null +++ b/gfx/layers/ipc/SharedRGBImage.h @@ -0,0 +1,59 @@ +/* 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; + +already_AddRefed<Image> CreateSharedRGBImage(ImageContainer* aImageContainer, + gfx::IntSize aSize, + gfxImageFormat aImageFormat); + +/** + * 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); + +protected: + ~SharedRGBImage(); + +public: + virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override; + + virtual uint8_t* GetBuffer() override; + + gfx::IntSize GetSize() override; + + already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override; + + bool Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat); +private: + gfx::IntSize mSize; + RefPtr<ImageClient> mCompositable; + RefPtr<TextureClient> mTextureClient; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/SynchronousTask.h b/gfx/layers/ipc/SynchronousTask.h new file mode 100644 index 000000000..fc20e2843 --- /dev/null +++ b/gfx/layers/ipc/SynchronousTask.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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. +// This can go away when we switch ImageBridge to an XPCOM thread. +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 +{ +public: + explicit AutoCompleteTask(SynchronousTask* aTask) + : mTask(aTask), + mAutoEnter(aTask->mMonitor) + { + } + ~AutoCompleteTask() { + mTask->Complete(); + } + +private: + SynchronousTask* mTask; + ReentrantMonitorAutoEnter mAutoEnter; +}; + +} +} + +#endif diff --git a/gfx/layers/ipc/TextureForwarder.h b/gfx/layers/ipc/TextureForwarder.h new file mode 100644 index 000000000..0d06fa5f5 --- /dev/null +++ b/gfx/layers/ipc/TextureForwarder.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_LAYERS_TEXTUREFORWARDER +#define MOZILLA_LAYERS_TEXTUREFORWARDER + +#include <stdint.h> // for int32_t, uint64_t +#include "gfxTypes.h" +#include "mozilla/layers/LayersTypes.h" // for LayersBackend +#include "mozilla/layers/TextureClient.h" // for TextureClient +#include "mozilla/layers/KnowsCompositor.h" + +namespace mozilla { +namespace ipc { +class IShmemAllocator; +} +namespace layers { + +/** + * 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_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0; + NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0; + + virtual bool IsSameProcess() const = 0; + + virtual bool UsesImageBridge() const { return false; } + + virtual base::ProcessId GetParentPid() const = 0; + + virtual MessageLoop* GetMessageLoop() const = 0; + + virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() { return nullptr; } + + virtual void CancelWaitForRecycle(uint64_t aTextureId) = 0; + +protected: + virtual ~LayersIPCChannel() {} +}; + +/** + * 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, + LayersBackend aLayersBackend, + TextureFlags aFlags, + uint64_t aSerial) = 0; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h b/gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h new file mode 100644 index 000000000..e64705478 --- /dev/null +++ b/gfx/layers/ipc/ThreadSafeRefcountingWithMainThreadDestruction.h @@ -0,0 +1,91 @@ +/* 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 THREADSAFEREFCOUNTINGWITHMAINTHREADDESTRUCTION_H_ +#define THREADSAFEREFCOUNTINGWITHMAINTHREADDESTRUCTION_H_ + +#include "base/message_loop.h" +#include "MainThreadUtils.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace layers { + +inline MessageLoop* GetMainLoopAssertingMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + return MessageLoop::current(); +} + +inline MessageLoop* GetMainLoop() +{ + static MessageLoop* sMainLoop = GetMainLoopAssertingMainThread(); + return sMainLoop; +} + +struct HelperForMainThreadDestruction +{ + HelperForMainThreadDestruction() + { + MOZ_ASSERT(NS_IsMainThread()); + GetMainLoop(); + } + + ~HelperForMainThreadDestruction() + { + MOZ_ASSERT(NS_IsMainThread()); + } +}; + +template<typename T> +struct DeleteOnMainThreadTask : public Runnable +{ + T* mToDelete; + explicit DeleteOnMainThreadTask(T* aToDelete) : mToDelete(aToDelete) {} + NS_IMETHOD Run() override { + MOZ_ASSERT(NS_IsMainThread()); + mToDelete->DeleteToBeCalledOnMainThread(); + return NS_OK; + } +}; + +} // namespace layers +} // namespace mozilla + +#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(_class) \ +public: \ + NS_METHOD_(MozExternalRefCountType) AddRef(void) { \ + MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \ + MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \ + nsrefcnt count = ++mRefCnt; \ + NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \ + return (nsrefcnt) count; \ + } \ + void DeleteToBeCalledOnMainThread() { \ + MOZ_ASSERT(NS_IsMainThread()); \ + NS_LOG_RELEASE(this, 0, #_class); \ + delete this; \ + } \ + NS_METHOD_(MozExternalRefCountType) Release(void) { \ + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \ + nsrefcnt count = --mRefCnt; \ + if (count == 0) { \ + if (NS_IsMainThread()) { \ + DeleteToBeCalledOnMainThread(); \ + } else { \ + NS_DispatchToMainThread( \ + new mozilla::layers::DeleteOnMainThreadTask<_class>(this)); \ + } \ + } else { \ + NS_LOG_RELEASE(this, count, #_class); \ + } \ + return count; \ + } \ +protected: \ + ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \ +private: \ + ::mozilla::layers::HelperForMainThreadDestruction mHelperForMainThreadDestruction; \ +public: + +#endif diff --git a/gfx/layers/ipc/VideoBridgeChild.cpp b/gfx/layers/ipc/VideoBridgeChild.cpp new file mode 100644 index 000000000..9651c563e --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeChild.cpp @@ -0,0 +1,122 @@ +/* -*- 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 "VideoBridgeChild.h" +#include "VideoBridgeParent.h" +#include "CompositorThread.h" + +namespace mozilla { +namespace layers { + +StaticRefPtr<VideoBridgeChild> sVideoBridgeChildSingleton; + +/* static */ void +VideoBridgeChild::Startup() +{ + sVideoBridgeChildSingleton = new VideoBridgeChild(); + RefPtr<VideoBridgeParent> parent = new VideoBridgeParent(); + + MessageLoop* loop = CompositorThreadHolder::Loop(); + + sVideoBridgeChildSingleton->Open(parent->GetIPCChannel(), + loop, + ipc::ChildSide); + sVideoBridgeChildSingleton->mIPDLSelfRef = sVideoBridgeChildSingleton; + parent->SetOtherProcessId(base::GetCurrentProcId()); +} + +/* static */ void +VideoBridgeChild::Shutdown() +{ + if (sVideoBridgeChildSingleton) { + sVideoBridgeChildSingleton->Close(); + sVideoBridgeChildSingleton = nullptr; + } +} + +VideoBridgeChild::VideoBridgeChild() + : mMessageLoop(MessageLoop::current()) + , mCanSend(true) +{ +} + +VideoBridgeChild::~VideoBridgeChild() +{ +} + +VideoBridgeChild* +VideoBridgeChild::GetSingleton() +{ + return sVideoBridgeChildSingleton; +} + +bool +VideoBridgeChild::AllocUnsafeShmem(size_t aSize, + ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) +{ + 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); +} + +bool +VideoBridgeChild::DeallocShmem(ipc::Shmem& aShmem) +{ + return PVideoBridgeChild::DeallocShmem(aShmem); +} + +PTextureChild* +VideoBridgeChild::AllocPTextureChild(const SurfaceDescriptor&, + 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::DeallocPVideoBridgeChild() +{ + mIPDLSelfRef = nullptr; +} + +PTextureChild* +VideoBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData, + LayersBackend aLayersBackend, + TextureFlags aFlags, + uint64_t aSerial) +{ + MOZ_ASSERT(CanSend()); + return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial); +} + +bool VideoBridgeChild::IsSameProcess() const +{ + return OtherPid() == base::GetCurrentProcId(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/VideoBridgeChild.h b/gfx/layers/ipc/VideoBridgeChild.h new file mode 100644 index 000000000..f5677008e --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeChild.h @@ -0,0 +1,75 @@ +/* -*- 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/. */ + +#ifndef MOZILLA_GFX_VIDEOBRIDGECHILD_H +#define MOZILLA_GFX_VIDEOBRIDGECHILD_H + +#include "mozilla/layers/PVideoBridgeChild.h" +#include "ISurfaceAllocator.h" +#include "TextureForwarder.h" + +namespace mozilla { +namespace layers { + +class VideoBridgeChild final : public PVideoBridgeChild + , public TextureForwarder +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoBridgeChild, override); + + static void Startup(); + static void Shutdown(); + + static VideoBridgeChild* GetSingleton(); + + // PVideoBridgeChild + PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aSerial) override; + bool DeallocPTextureChild(PTextureChild* actor) override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + void DeallocPVideoBridgeChild() 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, + LayersBackend aLayersBackend, + TextureFlags aFlags, + uint64_t aSerial) override; + + // ClientIPCAllocator + base::ProcessId GetParentPid() const override { return OtherPid(); } + MessageLoop * GetMessageLoop() const override { return mMessageLoop; } + void CancelWaitForRecycle(uint64_t aTextureId) override { MOZ_ASSERT(false, "NO RECYCLING HERE"); } + + // ISurfaceAllocator + bool IsSameProcess() const override; + + bool CanSend() { return mCanSend; } + +private: + VideoBridgeChild(); + ~VideoBridgeChild(); + + RefPtr<VideoBridgeChild> mIPDLSelfRef; + MessageLoop* mMessageLoop; + 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 000000000..fce2184c8 --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeParent.cpp @@ -0,0 +1,125 @@ +/* vim: set ts=2 sw=2 et tw=80: */ +/* -*- 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 "VideoBridgeParent.h" +#include "mozilla/layers/TextureHost.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::ipc; +using namespace mozilla::gfx; +using namespace mozilla::media; + + +static VideoBridgeParent* sVideoBridgeSingleton; + +VideoBridgeParent::VideoBridgeParent() + : mClosed(false) +{ + mSelfRef = this; + sVideoBridgeSingleton = this; +} + +VideoBridgeParent::~VideoBridgeParent() +{ + sVideoBridgeSingleton = nullptr; +} + +/* static */ VideoBridgeParent* +VideoBridgeParent::GetSingleton() +{ + return sVideoBridgeSingleton; +} + +TextureHost* +VideoBridgeParent::LookupTexture(uint64_t aSerial) +{ + return TextureHost::AsTextureHost(mTextureMap[aSerial]); +} + +void +VideoBridgeParent::ActorDestroy(ActorDestroyReason aWhy) +{ + // Can't alloc/dealloc shmems from now on. + mClosed = true; +} + +void +VideoBridgeParent::DeallocPVideoBridgeParent() +{ + mSelfRef = nullptr; +} + +PTextureParent* +VideoBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aSerial) +{ + PTextureParent* parent = + TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial); + mTextureMap[aSerial] = parent; + return parent; +} + +bool +VideoBridgeParent::DeallocPTextureParent(PTextureParent* actor) +{ + mTextureMap.erase(TextureHost::GetTextureSerial(actor)); + return TextureHost::DestroyIPDLActor(actor); +} + +void +VideoBridgeParent::SendAsyncMessage(const InfallibleTArray<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); +} + +void +VideoBridgeParent::DeallocShmem(ipc::Shmem& aShmem) +{ + if (mClosed) { + 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 000000000..e5560acab --- /dev/null +++ b/gfx/layers/ipc/VideoBridgeParent.h @@ -0,0 +1,72 @@ +/* -*- 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/. */ + +#ifndef gfx_layers_ipc_VideoBridgeParent_h_ +#define gfx_layers_ipc_VideoBridgeParent_h_ + +#include "mozilla/layers/PVideoBridgeParent.h" + +namespace mozilla { +namespace layers { + +class VideoBridgeParent final : public PVideoBridgeParent, + public HostIPCAllocator, + public ShmemAllocator +{ +public: + VideoBridgeParent(); + ~VideoBridgeParent(); + + static VideoBridgeParent* GetSingleton(); + TextureHost* LookupTexture(uint64_t aSerial); + + // PVideoBridgeParent + void ActorDestroy(ActorDestroyReason aWhy) override; + PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, + const LayersBackend& aLayersBackend, + const TextureFlags& aFlags, + const uint64_t& aSerial) override; + bool DeallocPTextureParent(PTextureParent* actor) override; + + // HostIPCAllocator + base::ProcessId GetChildProcessId() override + { + return OtherPid(); + } + void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override; + void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override; + + // ISurfaceAllocator + ShmemAllocator* AsShmemAllocator() override { return this; } + bool IsSameProcess() const override; + bool IPCOpen() const override { return !mClosed; } + + // ShmemAllocator + 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; + + void DeallocShmem(ipc::Shmem& aShmem) override; + +private: + void DeallocPVideoBridgeParent() override; + + // This keeps us alive until ActorDestroy(), at which point we do a + // deferred destruction of ourselves. + RefPtr<VideoBridgeParent> mSelfRef; + + std::map<uint64_t, PTextureParent*> mTextureMap; + + bool mClosed; +}; + +} // namespace layers +} // namespace mozilla + +#endif // gfx_layers_ipc_VideoBridgeParent_h_ |